SRC += xdnd.c
SRC += playlist.c
SRC += nfont.c
+SRC += properties.c
OBJ = $(SRC:%.c=$(BUILD_ROOT)/build/application/%.$(OBJ_EXT))
#include "xdnd.h"
#include "utils.h"
#include "playlist.h"
-#include "ucx/string.h"
+#include <cx/string.h>
static void sidebar_class_init(void);
XtVaSetValues(parent, XmNbackground, WhitePixelOfScreen(XtScreen(parent)), NULL);
XtMakeResizeRequest(widget, 200, 100, &w, &h);
-
-
+
}
static void sidebar_expose(Widget widget, XEvent *event, Region region) {
Sidebar s = (Sidebar)widget;
XftFont *xftFont = s->sidebar.font->fonts->font;
- UcxList *tracks = s->sidebar.window->playlist.tracks;
- size_t numTracks = ucx_list_size(tracks);
+ CxList *tracks = s->sidebar.window->playlist.tracks;
+ size_t numTracks = tracks->size;
int fontheight = xftFont->ascent;
int height = s->sidebar.elmHeight;
//printf("current track: %d\n", s->sidebar.window->playlist.current_track);
- int i = 0;
- UCX_FOREACH(elm, s->sidebar.window->playlist.tracks) {
- char *name = util_resource_name(elm->data);
+ CxIterator i = cxListIterator(s->sidebar.window->playlist.tracks);
+ cx_foreach(const char *, elm, i) {
+ const char *name = util_resource_name(elm);
XftColor *cg = &s->sidebar.fg;
- if(i == s->sidebar.window->playlist.current_track) {
- XftDrawRect(s->sidebar.d, &s->sidebar.fg, 0, i*height, s->core.width, height);
+ if(i.index == s->sidebar.window->playlist.current_track) {
+ XftDrawRect(s->sidebar.d, &s->sidebar.fg, 0, i.index*height, s->core.width, height);
cg = &s->sidebar.bg;
}
- XftDrawString8(s->sidebar.d, cg, s->sidebar.font->fonts->font, 10, i*height + xftFont->ascent, (const FcChar8*)name, strlen(name));
-
- i++;
+ XftDrawString8(s->sidebar.d, cg, s->sidebar.font->fonts->font, 10, i.index*height + xftFont->ascent, (const FcChar8*)name, strlen(name));
}
}
}
-static void open_uri(Sidebar s, sstr_t uri) {
- char *urilist = uri.ptr;
+static void open_uri(Sidebar s, cxstring uri) {
+ const char *urilist = uri.ptr;
size_t len = uri.length;
size_t start = 0;
Sidebar s = (Sidebar)cdata;
- scstr_t urlist = scstr(udata);
+ cxstring urllist = cx_str(udata);
- ssize_t nuris;
- sstr_t *uris = scstrsplit(urlist, scstr("\r\n"), &nuris);
-
- for(int i=0;i<nuris;i++) {
- if(uris[i].length > 0) {
- open_uri(s, uris[i]);
- }
+ CxStrtokCtx tk = cx_strtok(urllist, cx_str("\r\n"), INT_MAX);
+ cxstring uri;
+ while(cx_strtok_next(&tk, &uri)) {
+ open_uri(s, uri);
}
SidebarRepaint((Widget)s);
NFont *font;
XftColor fg;
XftColor bg;
-
+
int elmHeight;
MainWindow *window;
#include "main.h"
#include "settings.h"
-#include <ucx/buffer.h>
-#include <ucx/utils.h>
+#include <cx/buffer.h>
+#include <cx/utils.h>
static XtAppContext app;
static Display *display;
#include "player.h"
#include "utils.h"
+#include <cx/array_list.h>
+
void PlayListInit(MainWindow *win) {
+ win->playlist.tracks = cxArrayListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS, 64);
win->playlist.current_track = -1;
}
void PlayListAddFile(MainWindow *win, const char *file) {
char *f = strdup(file);
- win->playlist.tracks = ucx_list_append(win->playlist.tracks, f);
+ cxListAdd(win->playlist.tracks, f);
}
void PlayListPlayNext(MainWindow *win, bool force) {
- UcxList *tracks = win->playlist.tracks;
+ CxList *tracks = win->playlist.tracks;
if(!tracks) return;
- size_t len = ucx_list_size(tracks);
+ size_t len = tracks->size;
int current = win->playlist.current_track;
if(win->playlist.repeatTrack) {
current++;
} else if(win->playlist.autoplayFolder) {
char *next_file = util_find_next_file(win->file);
- win->playlist.tracks = ucx_list_append(win->playlist.tracks, next_file);
+ cxListAdd(win->playlist.tracks, next_file);
current = len;
} else {
current = 0;
}
void PlayListPlayTrack(MainWindow *win, int i) {
- UcxList *tracks = win->playlist.tracks;
- if(i < ucx_list_size(tracks)) {
- win->playlist.current_track = i;
- UcxList *fileElm = ucx_list_get(tracks, i);
- if(fileElm) {
- win->file = fileElm->data;
+ CxList *tracks = win->playlist.tracks;
+ if(i < tracks->size) {
+ char *file = cxListAt(tracks, i);
+ if(file) {
+ win->playlist.current_track = i;
+ win->file = file;
PlayerOpenFile(win);
win->playlist.current_track = i;
}
* POSSIBILITY OF SUCH DAMAGE.
*/
-#include "ucx/properties.h"
+#include "properties.h"
#include <stdio.h>
#include <stdlib.h>
parser->tmplen += len;
}
-int ucx_properties_next(UcxProperties *parser, sstr_t *name, sstr_t *value) {
+int ucx_properties_next(UcxProperties *parser, cxstring *name, cxstring *value) {
if(parser->tmplen > 0) {
char *buf = parser->buffer + parser->pos;
size_t len = parser->buflen - parser->pos;
- sstr_t str = sstrn(buf, len);
- sstr_t nl = sstrchr(str, '\n');
+ cxstring str = cx_strn(buf, len);
+ cxstring nl = cx_strchr(str, '\n');
if(nl.ptr) {
size_t newlen = (size_t)(nl.ptr - buf) + 1;
parser_tmp_append(parser, buf, newlen);
return 0;
}
- sstr_t line = has_comment ? sstrn(buf, comment_index) : sstrn(buf, i);
+ cxstring line = has_comment ? cx_strn(buf, comment_index) : cx_strn(buf, i);
// check line
if(delimiter_index == 0) {
- line = sstrtrim(line);
+ line = cx_strtrim(line);
if(line.length != 0) {
parser->error = 1;
}
} else {
- sstr_t n = sstrn(buf, delimiter_index);
- sstr_t v = sstrn(
+ cxstring n = cx_strn(buf, delimiter_index);
+ cxstring v = cx_strn(
buf + delimiter_index + 1,
line.length - delimiter_index - 1);
- n = sstrtrim(n);
- v = sstrtrim(v);
+ n = cx_strtrim(n);
+ v = cx_strtrim(v);
if(n.length != 0 || v.length != 0) {
*name = n;
*value = v;
return 0;
}
-int ucx_properties2map(UcxProperties *parser, UcxMap *map) {
- sstr_t name;
- sstr_t value;
+int ucx_properties2map(UcxProperties *parser, CxMap *map) {
+ cxstring name;
+ cxstring value;
while(ucx_properties_next(parser, &name, &value)) {
- value = sstrdup_a(map->allocator, value);
- if(!value.ptr) {
+ cxmutstr mutvalue = cx_strdup_a(map->allocator, value);
+ if(!mutvalue.ptr) {
return 1;
}
- if(ucx_map_sstr_put(map, name, value.ptr)) {
- alfree(map->allocator, value.ptr);
+ if(cxMapPut(map, cx_hash_key_cxstr(name), mutvalue.ptr)) {
+ cxFree(map->allocator, mutvalue.ptr);
return 1;
}
}
// buffer size is documented - change doc, when you change bufsize!
#define UCX_PROPLOAD_BUFSIZE 1024
-int ucx_properties_load(UcxMap *map, FILE *file) {
+int ucx_properties_load(CxMap *map, FILE *file) {
UcxProperties *parser = ucx_properties_new();
if(!(parser && map && file)) {
return 1;
return error;
}
-int ucx_properties_store(UcxMap *map, FILE *file) {
- UcxMapIterator iter = ucx_map_iterator(map);
- void *v;
- sstr_t value;
+int ucx_properties_store(CxMap *map, FILE *file) {
+ CxIterator iter = cxMapIterator(map);
+ cxstring value;
size_t written;
- UCX_MAP_FOREACH(k, v, iter) {
- value = sstr((char*)v);
+ cx_foreach(CxMapEntry *, v, iter) {
+ value = cx_str(v->value);
written = 0;
- written += fwrite(k.data, 1, k.len, file);
+ written += fwrite(v->key->data.bytes, 1, v->key->len, file);
written += fwrite(" = ", 1, 3, file);
written += fwrite(value.ptr, 1, value.length, file);
written += fwrite("\n", 1, 1, file);
- if (written != k.len + value.length + 4) {
+ if (written != v->key->len + value.length + 4) {
return 1;
}
}
#ifndef UCX_PROPERTIES_H
#define UCX_PROPERTIES_H
-#include "ucx.h"
-#include "map.h"
+#include <cx/hash_map.h>
+#include <cx/string.h>
+
+#include <stdio.h>
#ifdef __cplusplus
extern "C" {
* found. If no more key/value-pairs are found, you may refill the input buffer
* with ucx_properties_fill().
*
- * <b>Attention:</b> the sstr_t.ptr pointers of the output parameters point to
+ * <b>Attention:</b> the cxstring.ptr pointers of the output parameters point to
* memory within the input buffer of the parser and will get invalid some time.
* If you want long term copies of the key/value-pairs, use sstrdup() after
* calling this function.
*
* @param prop the UcxProperties object
- * @param name a pointer to the sstr_t that shall contain the property name
- * @param value a pointer to the sstr_t that shall contain the property value
+ * @param name a pointer to the cxstring that shall contain the property name
+ * @param value a pointer to the cxstring that shall contain the property value
* @return Nonzero, if a key/value-pair was successfully retrieved
* @see ucx_properties_fill()
*/
-int ucx_properties_next(UcxProperties *prop, sstr_t *name, sstr_t *value);
+int ucx_properties_next(UcxProperties *prop, cxstring *name, cxstring *value);
/**
* Retrieves all available key/value-pairs and puts them into a UcxMap.
* @see ucx_properties_fill()
* @see UcxMap.allocator
*/
-int ucx_properties2map(UcxProperties *prop, UcxMap *map);
+int ucx_properties2map(UcxProperties *prop, CxMap *map);
/**
* Loads a properties file to a UcxMap.
* @see ucx_properties_fill()
* @see ucx_properties2map()
*/
-int ucx_properties_load(UcxMap *map, FILE *file);
+int ucx_properties_load(CxMap *map, FILE *file);
/**
* Stores a UcxMap to a file.
* @param file the <code>FILE*</code> stream to write to
* @return 0 on success, or a non-zero value on error
*/
-int ucx_properties_store(UcxMap *map, FILE *file);
+int ucx_properties_store(CxMap *map, FILE *file);
#ifdef __cplusplus
}
#include "utils.h"
#include "json.h"
-#include <ucx/map.h>
-#include <ucx/properties.h>
-#include <ucx/buffer.h>
-#include <ucx/utils.h>
+#include <cx/map.h>
+#include <cx/hash_map.h>
+//include <ucx/properties.h>
+#include <cx/buffer.h>
+#include <cx/utils.h>
#define CONFIG_BASE_DIR ".config"
#define UWP_CONFIG_DIR "uwplayer"
/*
* global settings from json config converted to key/value pairs
*/
-static UcxMap *uwp_settings;
+static CxMap *uwp_settings;
/*
* default settings
*/
-static UcxMap *uwp_default;
+static CxMap *uwp_default;
static int check_config_dir(void) {
char *home = getenv("HOME");
return 1;
}
- uwp_settings = ucx_map_new(16);
- uwp_default = ucx_map_new(32);
+ uwp_settings = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
+ uwp_default = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 32);
char *cfgfile_path = util_concat_path(uwp_config_dir, UWP_CONFIG_FILE);
FILE *cfgfile = fopen(cfgfile_path, "r");
// check if mpv or mplayer binaries are configured
- char *player_bin = ucx_map_cstr_get(uwp_settings, UWP_PLAYER_BIN);
- char *player_type = ucx_map_cstr_get(uwp_settings, UWP_PLAYER_TYPE);
+ char *player_bin = cxMapGet(uwp_settings, cx_hash_key_str(UWP_PLAYER_BIN));
+ char *player_type = cxMapGet(uwp_settings, cx_hash_key_str(UWP_PLAYER_TYPE));
if(!player_bin) {
// try to find the mpv or mplayer binary path
for(size_t i=0;i<s->size;i++) {
JSONObjValue *gs = &s->values[i];
if(gs->value->type == JSON_STRING) {
- ucx_map_cstr_put(uwp_settings, gs->name, strdup(gs->value->value.string.string));
+ cxMapPut(uwp_settings, cx_hash_key_str(gs->name), strdup(gs->value->value.string.string));
}
}
}
-static char* get_which_output(FILE *f, UcxBuffer *buf) {
+static char* get_which_output(FILE *f, CxBuffer *buf) {
buf->pos = 0;
buf->size = 0;
- ucx_stream_copy(f, buf, (read_func)fread, (write_func)ucx_buffer_write);
+ cx_stream_copy(f, buf, (cx_read_func)fread, (cx_write_func)cxBufferWrite);
if(!pclose(f)) {
- ucx_buffer_putc(buf, 0);
+ cxBufferPut(buf, 0);
size_t i;
for(i=0;i<buf->pos;i++) {
if(buf->space[i] == '\n') {
static Boolean finish_bin_search(XtPointer data) {
PlayerInfo *playerInfo = data;
- ucx_map_cstr_put(uwp_settings, UWP_PLAYER_BIN, playerInfo->bin);
- ucx_map_cstr_put(uwp_settings, UWP_PLAYER_TYPE, playerInfo->type);
+ cxMapPut(uwp_settings, cx_hash_key_str(UWP_PLAYER_BIN), playerInfo->bin);
+ cxMapPut(uwp_settings, cx_hash_key_str(UWP_PLAYER_TYPE), playerInfo->type);
free(playerInfo);
return 0;
}
static void* player_bin_search_thread(void *data) {
- UcxBuffer *buf = ucx_buffer_new(NULL, 256, UCX_BUFFER_AUTOEXTEND);
+ CxBuffer buf;
+ cxBufferInit(&buf, NULL, 256, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS);
FILE *f = popen("which mpv", "r");
if(f) {
- char *bin = get_which_output(f, buf);
+ char *bin = get_which_output(f, &buf);
if(bin) {
PlayerInfo *playerInfo = malloc(sizeof(PlayerInfo));
playerInfo->bin = strdup(bin);
playerInfo->type = strdup("mpv");
AppExecProc(finish_bin_search, playerInfo);
- ucx_buffer_free(buf);
+ cxBufferDestroy(&buf);
return NULL;
}
}
f = popen("which mplayer", "r");
if(f) {
- char *bin = get_which_output(f, buf);
+ char *bin = get_which_output(f, &buf);
if(bin) {
PlayerInfo *playerInfo = malloc(sizeof(PlayerInfo));
playerInfo->bin = strdup(bin);
}
}
- ucx_buffer_free(buf);
+ cxBufferDestroy(&buf);
return NULL;
}
char* SettingsGetPlayerBin(void) {
- return ucx_map_cstr_get(uwp_settings, UWP_PLAYER_BIN);
+ return cxMapGet(uwp_settings, cx_hash_key_str(UWP_PLAYER_BIN));
}
#include <dirent.h>
#include <errno.h>
-#include <ucx/string.h>
+#include <cx/string.h>
char* util_concat_path(const char *url_base, const char *p) {
- sstr_t base = sstr((char*)url_base);
- sstr_t path;
+ cxstring base = cx_str(url_base);
+ cxstring path;
if(p) {
- path = sstr((char*)p);
+ path = cx_str(p);
} else {
- path = sstrn("", 0);
+ path = cx_strn("", 0);
}
int add_separator = 0;
}
}
- sstr_t url;
+ cxmutstr url;
if(add_separator) {
- url = sstrcat(3, base, sstr("/"), path);
+ url = cx_strcat(3, base, cx_strn("/", 1), path);
} else {
- url = sstrcat(2, base, path);
+ url = cx_strcat(2, base, path);
}
return url.ptr;
}
-char* util_resource_name(char *url) {
- sstr_t urlstr = sstr(url);
+const char* util_resource_name(const char *url) {
+ cxstring urlstr = cx_str(url);
if(urlstr.ptr[urlstr.length-1] == '/') {
urlstr.length--;
}
- sstr_t resname = sstrrchr(urlstr, '/');
+ cxstring resname = cx_strrchr(urlstr, '/');
if(resname.length > 1) {
return resname.ptr+1;
} else {
}
char* util_parent_path(const char *path) {
- char *name = util_resource_name((char*)path);
+ const char *name = util_resource_name(path);
size_t namelen = strlen(name);
size_t pathlen = strlen(path);
size_t parentlen = pathlen - namelen;
char* util_find_next_file(char *current_file) {
char *current_folder = util_parent_path(current_file);
- char *current_file_name = util_resource_name(current_file);
+ const char *current_file_name = util_resource_name(current_file);
DIR *dir = opendir(current_folder);
if(!dir) {
fprintf(stderr, "Error: Cannot open directory '%s': %s\n", current_folder, strerror(errno));
char* util_concat_path(const char *url_base, const char *p);
-char* util_resource_name(char *url);
+const char* util_resource_name(const char *url);
char* util_parent_path(const char *path);
#include <Xm/XmAll.h>
#include <stdbool.h>
#include <unistd.h>
-#include <ucx/list.h>
+#include <cx/list.h>
+#include <cx/linked_list.h>
#ifdef __cplusplus
extern "C" {
} Player;
typedef struct {
- UcxList *tracks;
+ CxList *tracks;
int current_track;
Boolean repeatTrack;
include ../config.mk
# list of source files
-SRC = utils.c
+SRC = allocator.c
+SRC += array_list.c
+SRC += basic_mempool.c
+SRC += buffer.c
+SRC += compare.c
+SRC += hash_key.c
+SRC += hash_map.c
+SRC += linked_list.c
SRC += list.c
-SRC += map.c
-SRC += avl.c
-SRC += properties.c
-SRC += mempool.c
+SRC += printf.c
SRC += string.c
-SRC += test.c
-SRC += allocator.c
-SRC += logging.c
-SRC += buffer.c
-SRC += stack.c
-SRC += ucx.c
-SRC += array.c
+SRC += utils.c
OBJ = $(SRC:%.c=../build/ucx/%.$(OBJ_EXT))
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* POSSIBILITY OF SUCH DAMAGE.
*/
-#include "ucx/allocator.h"
+#include "cx/allocator.h"
-#include <stdlib.h>
+__attribute__((__malloc__, __alloc_size__(2)))
+static void *cx_malloc_stdlib(
+ __attribute__((__unused__)) void *d,
+ size_t n
+) {
+ return malloc(n);
+}
+
+__attribute__((__warn_unused_result__, __alloc_size__(3)))
+static void *cx_realloc_stdlib(
+ __attribute__((__unused__)) void *d,
+ void *mem,
+ size_t n
+) {
+ return realloc(mem, n);
+}
+
+__attribute__((__malloc__, __alloc_size__(2, 3)))
+static void *cx_calloc_stdlib(
+ __attribute__((__unused__)) void *d,
+ size_t nelem,
+ size_t n
+) {
+ return calloc(nelem, n);
+}
-static UcxAllocator default_allocator = {
- NULL,
- ucx_default_malloc,
- ucx_default_calloc,
- ucx_default_realloc,
- ucx_default_free
+__attribute__((__nonnull__))
+static void cx_free_stdlib(
+ __attribute__((__unused__)) void *d,
+ void *mem
+) {
+ free(mem);
+}
+
+static cx_allocator_class cx_default_allocator_class = {
+ cx_malloc_stdlib,
+ cx_realloc_stdlib,
+ cx_calloc_stdlib,
+ cx_free_stdlib
+};
+
+struct cx_allocator_s cx_default_allocator = {
+ &cx_default_allocator_class,
+ NULL
};
+CxAllocator *cxDefaultAllocator = &cx_default_allocator;
+
+// IMPLEMENTATION OF HIGH LEVEL API
-UcxAllocator *ucx_default_allocator() {
- UcxAllocator *allocator = &default_allocator;
- return allocator;
+void *cxMalloc(
+ CxAllocator const *allocator,
+ size_t n
+) {
+ return allocator->cl->malloc(allocator->data, n);
}
-void *ucx_default_malloc(void *ignore, size_t n) {
- return malloc(n);
+void *cxRealloc(
+ CxAllocator const *allocator,
+ void *mem,
+ size_t n
+) {
+ return allocator->cl->realloc(allocator->data, mem, n);
}
-void *ucx_default_calloc(void *ignore, size_t n, size_t size) {
- return calloc(n, size);
+int cxReallocate(
+ CxAllocator const *allocator,
+ void **mem,
+ size_t n
+) {
+ void *nmem = allocator->cl->realloc(allocator->data, *mem, n);
+ if (nmem == NULL) {
+ return 1;
+ } else {
+ *mem = nmem;
+ return 0;
+ }
}
-void *ucx_default_realloc(void *ignore, void *data, size_t n) {
- return realloc(data, n);
+void *cxCalloc(
+ CxAllocator const *allocator,
+ size_t nelem,
+ size_t n
+) {
+ return allocator->cl->calloc(allocator->data, nelem, n);
}
-void ucx_default_free(void *ignore, void *data) {
- free(data);
+void cxFree(
+ CxAllocator const *allocator,
+ void *mem
+) {
+ allocator->cl->free(allocator->data, mem);
}
+++ /dev/null
-/*
- * 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);
-}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "cx/array_list.h"
+#include <assert.h>
+#include <string.h>
+
+// LOW LEVEL ARRAY LIST FUNCTIONS
+
+enum cx_array_copy_result cx_array_copy(
+ void **target,
+ size_t *size,
+ size_t *capacity,
+ size_t index,
+ void const *src,
+ size_t elem_size,
+ size_t elem_count,
+ struct cx_array_reallocator_s *reallocator
+) {
+ // assert pointers
+ assert(target != NULL);
+ assert(size != NULL);
+ assert(src != NULL);
+
+ // determine capacity
+ size_t cap = capacity == NULL ? *size : *capacity;
+
+ // check if resize is required
+ size_t minsize = index + elem_count;
+ size_t newsize = *size < minsize ? minsize : *size;
+ bool needrealloc = newsize > cap;
+
+ // reallocate if possible
+ if (needrealloc) {
+ // a reallocator and a capacity variable must be available
+ if (reallocator == NULL || capacity == NULL) {
+ return CX_ARRAY_COPY_REALLOC_NOT_SUPPORTED;
+ }
+
+ // check, if we need to repair the src pointer
+ uintptr_t targetaddr = (uintptr_t) *target;
+ uintptr_t srcaddr = (uintptr_t) src;
+ bool repairsrc = targetaddr <= srcaddr
+ && srcaddr < targetaddr + cap * elem_size;
+
+ // calculate new capacity (next number divisible by 16)
+ cap = newsize - (newsize % 16) + 16;
+ assert(cap > newsize);
+
+ // perform reallocation
+ void *newmem = reallocator->realloc(
+ *target, cap, elem_size, reallocator
+ );
+ if (newmem == NULL) {
+ return CX_ARRAY_COPY_REALLOC_FAILED;
+ }
+
+ // repair src pointer, if necessary
+ if (repairsrc) {
+ src = ((char *) newmem) + (srcaddr - targetaddr);
+ }
+
+ // store new pointer and capacity
+ *target = newmem;
+ *capacity = cap;
+ }
+
+ // determine target pointer
+ char *start = *target;
+ start += index * elem_size;
+
+ // copy elements and set new size
+ memmove(start, src, elem_count * elem_size);
+ *size = newsize;
+
+ // return successfully
+ return CX_ARRAY_COPY_SUCCESS;
+}
+
+#ifndef CX_ARRAY_SWAP_SBO_SIZE
+#define CX_ARRAY_SWAP_SBO_SIZE 512
+#endif
+
+void cx_array_swap(
+ void *arr,
+ size_t elem_size,
+ size_t idx1,
+ size_t idx2
+) {
+ assert(arr != NULL);
+
+ // short circuit
+ if (idx1 == idx2) return;
+
+ char sbo_mem[CX_ARRAY_SWAP_SBO_SIZE];
+ void *tmp;
+
+ // decide if we can use the local buffer
+ if (elem_size > CX_ARRAY_SWAP_SBO_SIZE) {
+ tmp = malloc(elem_size);
+ // we don't want to enforce error handling
+ if (tmp == NULL) abort();
+ } else {
+ tmp = sbo_mem;
+ }
+
+ // calculate memory locations
+ char *left = arr, *right = arr;
+ left += idx1 * elem_size;
+ right += idx2 * elem_size;
+
+ // three-way swap
+ memcpy(tmp, left, elem_size);
+ memcpy(left, right, elem_size);
+ memcpy(right, tmp, elem_size);
+
+ // free dynamic memory, if it was needed
+ if (tmp != sbo_mem) {
+ free(tmp);
+ }
+}
+
+// HIGH LEVEL ARRAY LIST FUNCTIONS
+
+typedef struct {
+ struct cx_list_s base;
+ void *data;
+ struct cx_array_reallocator_s reallocator;
+} cx_array_list;
+
+static void *cx_arl_realloc(
+ void *array,
+ size_t capacity,
+ size_t elem_size,
+ struct cx_array_reallocator_s *alloc
+) {
+ // retrieve the pointer to the list allocator
+ CxAllocator const *al = alloc->ptr1;
+
+ // use the list allocator to reallocate the memory
+ return cxRealloc(al, array, capacity * elem_size);
+}
+
+static void cx_arl_destructor(struct cx_list_s *list) {
+ cx_array_list *arl = (cx_array_list *) list;
+ cxFree(list->allocator, arl->data);
+}
+
+static size_t cx_arl_insert_array(
+ struct cx_list_s *list,
+ size_t index,
+ void const *array,
+ size_t n
+) {
+ // out of bounds and special case check
+ if (index > list->size || n == 0) return 0;
+
+ // get a correctly typed pointer to the list
+ cx_array_list *arl = (cx_array_list *) list;
+
+ // do we need to move some elements?
+ if (index < list->size) {
+ char const *first_to_move = (char const *) arl->data;
+ first_to_move += index * list->itemsize;
+ size_t elems_to_move = list->size - index;
+ size_t start_of_moved = index + n;
+
+ if (CX_ARRAY_COPY_SUCCESS != cx_array_copy(
+ &arl->data,
+ &list->size,
+ &list->capacity,
+ start_of_moved,
+ first_to_move,
+ list->itemsize,
+ elems_to_move,
+ &arl->reallocator
+ )) {
+ // if moving existing elems is unsuccessful, abort
+ return 0;
+ }
+ }
+
+ // note that if we had to move the elements, the following operation
+ // is guaranteed to succeed, because we have the memory already allocated
+ // therefore, it is impossible to leave this function with an invalid array
+
+ // place the new elements
+ if (CX_ARRAY_COPY_SUCCESS == cx_array_copy(
+ &arl->data,
+ &list->size,
+ &list->capacity,
+ index,
+ array,
+ list->itemsize,
+ n,
+ &arl->reallocator
+ )) {
+ return n;
+ } else {
+ // array list implementation is "all or nothing"
+ return 0;
+ }
+}
+
+static int cx_arl_insert_element(
+ struct cx_list_s *list,
+ size_t index,
+ void const *element
+) {
+ return 1 != cx_arl_insert_array(list, index, element, 1);
+}
+
+static int cx_arl_insert_iter(
+ struct cx_mut_iterator_s *iter,
+ void const *elem,
+ int prepend
+) {
+ struct cx_list_s *list = iter->src_handle;
+ if (iter->index < list->size) {
+ int result = cx_arl_insert_element(
+ list,
+ iter->index + 1 - prepend,
+ elem
+ );
+ if (result == 0 && prepend != 0) {
+ iter->index++;
+ iter->elem_handle = ((char *) iter->elem_handle) + list->itemsize;
+ }
+ return result;
+ } else {
+ int result = cx_arl_insert_element(list, list->size, elem);
+ iter->index = list->size;
+ return result;
+ }
+}
+
+static int cx_arl_remove(
+ struct cx_list_s *list,
+ size_t index
+) {
+ cx_array_list *arl = (cx_array_list *) list;
+
+ // out-of-bounds check
+ if (index >= list->size) {
+ return 1;
+ }
+
+ // content destruction
+ if (list->content_destructor_type != CX_DESTRUCTOR_NONE) {
+ char *ptr = arl->data;
+ ptr += index * list->itemsize;
+ cx_list_invoke_destructor(list, ptr);
+ }
+
+ // short-circuit removal of last element
+ if (index == list->size - 1) {
+ list->size--;
+ return 0;
+ }
+
+ // just move the elements starting at index to the left
+ int result = cx_array_copy(
+ &arl->data,
+ &list->size,
+ &list->capacity,
+ index,
+ ((char *) arl->data) + (index + 1) * list->itemsize,
+ list->itemsize,
+ list->size - index - 1,
+ &arl->reallocator
+ );
+ if (result == 0) {
+ // decrease the size
+ list->size--;
+ }
+ return result;
+}
+
+static void cx_arl_clear(struct cx_list_s *list) {
+ if (list->size == 0) return;
+
+ cx_array_list *arl = (cx_array_list *) list;
+ char *ptr = arl->data;
+
+ switch (list->content_destructor_type) {
+ case CX_DESTRUCTOR_SIMPLE: {
+ for (size_t i = 0; i < list->size; i++) {
+ cx_list_invoke_simple_destructor(list, ptr);
+ ptr += list->itemsize;
+ }
+ break;
+ }
+ case CX_DESTRUCTOR_ADVANCED: {
+ for (size_t i = 0; i < list->size; i++) {
+ cx_list_invoke_advanced_destructor(list, ptr);
+ ptr += list->itemsize;
+ }
+ break;
+ }
+ case CX_DESTRUCTOR_NONE:
+ break; // nothing
+ }
+
+ memset(arl->data, 0, list->size * list->itemsize);
+ list->size = 0;
+}
+
+static int cx_arl_swap(
+ struct cx_list_s *list,
+ size_t i,
+ size_t j
+) {
+ if (i >= list->size || j >= list->size) return 1;
+ cx_array_list *arl = (cx_array_list *) list;
+ cx_array_swap(arl->data, list->itemsize, i, j);
+ return 0;
+}
+
+static void *cx_arl_at(
+ struct cx_list_s const *list,
+ size_t index
+) {
+ if (index < list->size) {
+ cx_array_list const *arl = (cx_array_list const *) list;
+ char *space = arl->data;
+ return space + index * list->itemsize;
+ } else {
+ return NULL;
+ }
+}
+
+static size_t cx_arl_find(
+ struct cx_list_s const *list,
+ void const *elem
+) {
+ assert(list->cmpfunc != NULL);
+ char *cur = ((cx_array_list const *) list)->data;
+
+ for (size_t i = 0; i < list->size; i++) {
+ if (0 == list->cmpfunc(elem, cur)) {
+ return i;
+ }
+ cur += list->itemsize;
+ }
+
+ return list->size;
+}
+
+static void cx_arl_sort(struct cx_list_s *list) {
+ assert(list->cmpfunc != NULL);
+ qsort(((cx_array_list *) list)->data,
+ list->size,
+ list->itemsize,
+ list->cmpfunc
+ );
+}
+
+static int cx_arl_compare(
+ struct cx_list_s const *list,
+ struct cx_list_s const *other
+) {
+ assert(list->cmpfunc != NULL);
+ if (list->size == other->size) {
+ char const *left = ((cx_array_list const *) list)->data;
+ char const *right = ((cx_array_list const *) other)->data;
+ for (size_t i = 0; i < list->size; i++) {
+ int d = list->cmpfunc(left, right);
+ if (d != 0) {
+ return d;
+ }
+ left += list->itemsize;
+ right += other->itemsize;
+ }
+ return 0;
+ } else {
+ return list->size < other->size ? -1 : 1;
+ }
+}
+
+static void cx_arl_reverse(struct cx_list_s *list) {
+ if (list->size < 2) return;
+ void *data = ((cx_array_list const *) list)->data;
+ size_t half = list->size / 2;
+ for (size_t i = 0; i < half; i++) {
+ cx_array_swap(data, list->itemsize, i, list->size - 1 - i);
+ }
+}
+
+static bool cx_arl_iter_valid(void const *it) {
+ struct cx_iterator_s const *iter = it;
+ struct cx_list_s const *list = iter->src_handle;
+ return iter->index < list->size;
+}
+
+static void *cx_arl_iter_current(void const *it) {
+ struct cx_iterator_s const *iter = it;
+ return iter->elem_handle;
+}
+
+static void cx_arl_iter_next(void *it) {
+ struct cx_iterator_base_s *itbase = it;
+ if (itbase->remove) {
+ struct cx_mut_iterator_s *iter = it;
+ itbase->remove = false;
+ cx_arl_remove(iter->src_handle, iter->index);
+ } else {
+ struct cx_iterator_s *iter = it;
+ iter->index++;
+ iter->elem_handle =
+ ((char *) iter->elem_handle)
+ + ((struct cx_list_s const *) iter->src_handle)->itemsize;
+ }
+}
+
+static void cx_arl_iter_prev(void *it) {
+ struct cx_iterator_base_s *itbase = it;
+ struct cx_mut_iterator_s *iter = it;
+ cx_array_list *const list = iter->src_handle;
+ if (itbase->remove) {
+ itbase->remove = false;
+ cx_arl_remove(iter->src_handle, iter->index);
+ }
+ iter->index--;
+ if (iter->index < list->base.size) {
+ iter->elem_handle = ((char *) list->data)
+ + iter->index * list->base.itemsize;
+ }
+}
+
+static bool cx_arl_iter_flag_rm(void *it) {
+ struct cx_iterator_base_s *iter = it;
+ if (iter->mutating) {
+ iter->remove = true;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static struct cx_iterator_s cx_arl_iterator(
+ struct cx_list_s const *list,
+ size_t index,
+ bool backwards
+) {
+ struct cx_iterator_s iter;
+
+ iter.index = index;
+ iter.src_handle = list;
+ iter.elem_handle = cx_arl_at(list, index);
+ iter.base.valid = cx_arl_iter_valid;
+ iter.base.current = cx_arl_iter_current;
+ iter.base.next = backwards ? cx_arl_iter_prev : cx_arl_iter_next;
+ iter.base.flag_removal = cx_arl_iter_flag_rm;
+ iter.base.remove = false;
+ iter.base.mutating = false;
+
+ return iter;
+}
+
+static cx_list_class cx_array_list_class = {
+ cx_arl_destructor,
+ cx_arl_insert_element,
+ cx_arl_insert_array,
+ cx_arl_insert_iter,
+ cx_arl_remove,
+ cx_arl_clear,
+ cx_arl_swap,
+ cx_arl_at,
+ cx_arl_find,
+ cx_arl_sort,
+ cx_arl_compare,
+ cx_arl_reverse,
+ cx_arl_iterator,
+};
+
+CxList *cxArrayListCreate(
+ CxAllocator const *allocator,
+ CxListComparator comparator,
+ size_t item_size,
+ size_t initial_capacity
+) {
+ if (allocator == NULL) {
+ allocator = cxDefaultAllocator;
+ }
+
+ cx_array_list *list = cxCalloc(allocator, 1, sizeof(cx_array_list));
+ if (list == NULL) return NULL;
+
+ list->data = cxCalloc(allocator, initial_capacity, item_size);
+ if (list->data == NULL) {
+ cxFree(allocator, list);
+ return NULL;
+ }
+
+ list->base.cl = &cx_array_list_class;
+ list->base.allocator = allocator;
+ list->base.cmpfunc = comparator;
+ list->base.capacity = initial_capacity;
+
+ if (item_size > 0) {
+ list->base.itemsize = item_size;
+ } else {
+ cxListStorePointers((CxList *) list);
+ }
+
+ // configure the reallocator
+ list->reallocator.realloc = cx_arl_realloc;
+ list->reallocator.ptr1 = (void *) allocator;
+
+ return (CxList *) list;
+}
+++ /dev/null
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "ucx/avl.h"
-
-#include <limits.h>
-
-#define ptrcast(ptr) ((void*)(ptr))
-#define alloc_tree(al) (UcxAVLTree*) almalloc((al), sizeof(UcxAVLTree))
-#define alloc_node(al) (UcxAVLNode*) almalloc((al), sizeof(UcxAVLNode))
-
-static void ucx_avl_connect(UcxAVLTree *tree,
- UcxAVLNode *node, UcxAVLNode *child, intptr_t nullkey) {
- if (child) {
- child->parent = node;
- }
- // if child is NULL, nullkey decides if left or right pointer is cleared
- if (tree->cmpfunc(
- ptrcast(child ? child->key : nullkey),
- ptrcast(node->key), tree->userdata) > 0) {
- node->right = child;
- } else {
- node->left = child;
- }
- size_t lh = node->left ? node->left->height : 0;
- size_t rh = node->right ? node->right->height : 0;
- node->height = 1 + (lh > rh ? lh : rh);
-}
-
-#define avlheight(node) ((node) ? (node)->height : 0)
-
-static UcxAVLNode* avl_rotright(UcxAVLTree *tree, UcxAVLNode *l0) {
- UcxAVLNode *p = l0->parent;
- UcxAVLNode *l1 = l0->left;
- if (p) {
- ucx_avl_connect(tree, p, l1, 0);
- } else {
- l1->parent = NULL;
- }
- ucx_avl_connect(tree, l0, l1->right, l1->key);
- ucx_avl_connect(tree, l1, l0, 0);
- return l1;
-}
-
-static UcxAVLNode* avl_rotleft(UcxAVLTree *tree, UcxAVLNode *l0) {
- UcxAVLNode *p = l0->parent;
- UcxAVLNode *l1 = l0->right;
- if (p) {
- ucx_avl_connect(tree, p, l1, 0);
- } else {
- l1->parent = NULL;
- }
- ucx_avl_connect(tree, l0, l1->left, l1->key);
- ucx_avl_connect(tree, l1, l0, 0);
- return l1;
-}
-
-static void ucx_avl_balance(UcxAVLTree *tree, UcxAVLNode *n) {
- int lh = avlheight(n->left);
- int rh = avlheight(n->right);
- n->height = 1 + (lh > rh ? lh : rh);
-
- if (lh - rh == 2) {
- UcxAVLNode *c = n->left;
- if (avlheight(c->right) - avlheight(c->left) == 1) {
- avl_rotleft(tree, c);
- }
- n = avl_rotright(tree, n);
- } else if (rh - lh == 2) {
- UcxAVLNode *c = n->right;
- if (avlheight(c->left) - avlheight(c->right) == 1) {
- avl_rotright(tree, c);
- }
- n = avl_rotleft(tree, n);
- }
-
- if (n->parent) {
- ucx_avl_balance(tree, n->parent);
- } else {
- tree->root = n;
- }
-}
-
-UcxAVLTree *ucx_avl_new(cmp_func cmpfunc) {
- return ucx_avl_new_a(cmpfunc, ucx_default_allocator());
-}
-
-UcxAVLTree *ucx_avl_new_a(cmp_func cmpfunc, UcxAllocator *allocator) {
- UcxAVLTree* tree = alloc_tree(allocator);
- if (tree) {
- tree->allocator = allocator;
- tree->cmpfunc = cmpfunc;
- tree->root = NULL;
- tree->userdata = NULL;
- }
-
- return tree;
-}
-
-static void ucx_avl_free_node(UcxAllocator *al, UcxAVLNode *node) {
- if (node) {
- ucx_avl_free_node(al, node->left);
- ucx_avl_free_node(al, node->right);
- alfree(al, node);
- }
-}
-
-void ucx_avl_free(UcxAVLTree *tree) {
- UcxAllocator *al = tree->allocator;
- ucx_avl_free_node(al, tree->root);
- alfree(al, tree);
-}
-
-static void ucx_avl_free_content_node(UcxAllocator *al, UcxAVLNode *node,
- ucx_destructor destr) {
- if (node) {
- ucx_avl_free_content_node(al, node->left, destr);
- ucx_avl_free_content_node(al, node->right, destr);
- if (destr) {
- destr(node->value);
- } else {
- alfree(al, node->value);
- }
- }
-}
-
-void ucx_avl_free_content(UcxAVLTree *tree, ucx_destructor destr) {
- ucx_avl_free_content_node(tree->allocator, tree->root, destr);
-}
-
-UcxAVLNode *ucx_avl_get_node(UcxAVLTree *tree, intptr_t key) {
- UcxAVLNode *n = tree->root;
- int cmpresult;
- while (n && (cmpresult = tree->cmpfunc(
- ptrcast(key), ptrcast(n->key), tree->userdata))) {
- n = cmpresult > 0 ? n->right : n->left;
- }
- return n;
-}
-
-void *ucx_avl_get(UcxAVLTree *tree, intptr_t key) {
- UcxAVLNode *n = ucx_avl_get_node(tree, key);
- return n ? n->value : NULL;
-}
-
-UcxAVLNode *ucx_avl_find_node(UcxAVLTree *tree, intptr_t key,
- distance_func dfnc, int mode) {
- UcxAVLNode *n = tree->root;
- UcxAVLNode *closest = NULL;
-
- intmax_t cmpresult;
- intmax_t closest_dist;
- closest_dist = mode == UCX_AVL_FIND_LOWER_BOUNDED ? INTMAX_MIN : INTMAX_MAX;
-
- while (n && (cmpresult = dfnc(
- ptrcast(key), ptrcast(n->key), tree->userdata))) {
- if (mode == UCX_AVL_FIND_CLOSEST) {
- intmax_t dist = cmpresult;
- if (dist < 0) dist *= -1;
- if (dist < closest_dist) {
- closest_dist = dist;
- closest = n;
- }
- } else if (mode == UCX_AVL_FIND_LOWER_BOUNDED && cmpresult <= 0) {
- if (cmpresult > closest_dist) {
- closest_dist = cmpresult;
- closest = n;
- }
- } else if (mode == UCX_AVL_FIND_UPPER_BOUNDED && cmpresult >= 0) {
- if (cmpresult < closest_dist) {
- closest_dist = cmpresult;
- closest = n;
- }
- }
- n = cmpresult > 0 ? n->right : n->left;
- }
- return n ? n : closest;
-}
-
-void *ucx_avl_find(UcxAVLTree *tree, intptr_t key,
- distance_func dfnc, int mode) {
- UcxAVLNode *n = ucx_avl_find_node(tree, key, dfnc, mode);
- return n ? n->value : NULL;
-}
-
-int ucx_avl_put(UcxAVLTree *tree, intptr_t key, void *value) {
- return ucx_avl_put_s(tree, key, value, NULL);
-}
-
-int ucx_avl_put_s(UcxAVLTree *tree, intptr_t key, void *value,
- void **oldvalue) {
- if (tree->root) {
- UcxAVLNode *n = tree->root;
- int cmpresult;
- while ((cmpresult = tree->cmpfunc(
- ptrcast(key), ptrcast(n->key), tree->userdata))) {
- UcxAVLNode *m = cmpresult > 0 ? n->right : n->left;
- if (m) {
- n = m;
- } else {
- break;
- }
- }
-
- if (cmpresult) {
- UcxAVLNode* e = alloc_node(tree->allocator);
- if (e) {
- e->key = key; e->value = value; e->height = 1;
- e->parent = e->left = e->right = NULL;
- ucx_avl_connect(tree, n, e, 0);
- ucx_avl_balance(tree, n);
- return 0;
- } else {
- return 1;
- }
- } else {
- if (oldvalue) {
- *oldvalue = n->value;
- }
- n->value = value;
- return 0;
- }
- } else {
- tree->root = alloc_node(tree->allocator);
- if (tree->root) {
- tree->root->key = key; tree->root->value = value;
- tree->root->height = 1;
- tree->root->parent = tree->root->left = tree->root->right = NULL;
-
- if (oldvalue) {
- *oldvalue = NULL;
- }
-
- return 0;
- } else {
- return 1;
- }
- }
-}
-
-int ucx_avl_remove(UcxAVLTree *tree, intptr_t key) {
- return ucx_avl_remove_s(tree, key, NULL, NULL);
-}
-
-int ucx_avl_remove_node(UcxAVLTree *tree, UcxAVLNode *node) {
- return ucx_avl_remove_s(tree, node->key, NULL, NULL);
-}
-
-int ucx_avl_remove_s(UcxAVLTree *tree, intptr_t key,
- intptr_t *oldkey, void **oldvalue) {
-
- UcxAVLNode *n = tree->root;
- int cmpresult;
- while (n && (cmpresult = tree->cmpfunc(
- ptrcast(key), ptrcast(n->key), tree->userdata))) {
- n = cmpresult > 0 ? n->right : n->left;
- }
- if (n) {
- if (oldkey) {
- *oldkey = n->key;
- }
- if (oldvalue) {
- *oldvalue = n->value;
- }
-
- UcxAVLNode *p = n->parent;
- if (n->left && n->right) {
- UcxAVLNode *s = n->right;
- while (s->left) {
- s = s->left;
- }
- ucx_avl_connect(tree, s->parent, s->right, s->key);
- n->key = s->key; n->value = s->value;
- p = s->parent;
- alfree(tree->allocator, s);
- } else {
- if (p) {
- ucx_avl_connect(tree, p, n->right ? n->right:n->left, n->key);
- } else {
- tree->root = n->right ? n->right : n->left;
- if (tree->root) {
- tree->root->parent = NULL;
- }
- }
- alfree(tree->allocator, n);
- }
-
- if (p) {
- ucx_avl_balance(tree, p);
- }
-
- return 0;
- } else {
- return 1;
- }
-}
-
-static size_t ucx_avl_countn(UcxAVLNode *node) {
- if (node) {
- return 1 + ucx_avl_countn(node->left) + ucx_avl_countn(node->right);
- } else {
- return 0;
- }
-}
-
-size_t ucx_avl_count(UcxAVLTree *tree) {
- return ucx_avl_countn(tree->root);
-}
-
-UcxAVLNode* ucx_avl_pred(UcxAVLNode* node) {
- if (node->left) {
- UcxAVLNode* n = node->left;
- while (n->right) {
- n = n->right;
- }
- return n;
- } else {
- UcxAVLNode* n = node;
- while (n->parent) {
- if (n->parent->right == n) {
- return n->parent;
- } else {
- n = n->parent;
- }
- }
- return NULL;
- }
-}
-
-UcxAVLNode* ucx_avl_succ(UcxAVLNode* node) {
- if (node->right) {
- UcxAVLNode* n = node->right;
- while (n->left) {
- n = n->left;
- }
- return n;
- } else {
- UcxAVLNode* n = node;
- while (n->parent) {
- if (n->parent->left == n) {
- return n->parent;
- } else {
- n = n->parent;
- }
- }
- return NULL;
- }
-}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "cx/basic_mempool.h"
+#include "cx/utils.h"
+#include <string.h>
+
+#define of_chk_(n) if (SIZE_MAX - sizeof(cx_destructor_func) < (n)) return NULL
+
+/** Internal structure for denoting pooled memory. */
+typedef struct {
+ /** The destructor. */
+ cx_destructor_func destructor;
+ /**
+ * Access to the first byte of the polled memory.
+ */
+ char c;
+} cx_basic_mempool_memory;
+
+static int cx_basic_mempool_chcap(
+ struct cx_basic_mempool_s *pool,
+ size_t newcap
+) {
+ if (newcap < pool->ndata) {
+ return 1;
+ }
+
+ size_t newcapsz;
+ if (cx_szmul(newcap, sizeof(void *), &newcapsz)) {
+ return 1;
+ }
+
+ void **data = realloc(pool->data, newcapsz);
+ if (data) {
+ pool->data = data;
+ pool->size = newcap;
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+void *cx_malloc_basic_mempool(
+ void *data,
+ size_t n
+) {
+ of_chk_(n);
+ struct cx_basic_mempool_s *pool = data;
+
+ if (pool->ndata >= pool->size) {
+ size_t newcap = pool->size * 2;
+ if (newcap < pool->size || cx_basic_mempool_chcap(pool, newcap)) {
+ return NULL;
+ }
+ }
+
+ cx_basic_mempool_memory *mem = malloc(sizeof(cx_destructor_func) + n);
+ if (mem == NULL) {
+ return NULL;
+ }
+
+ mem->destructor = NULL;
+ pool->data[pool->ndata] = mem;
+ pool->ndata++;
+
+ return &(mem->c);
+}
+
+void *cx_calloc_basic_mempool(
+ void *data,
+ size_t nelem,
+ size_t elsize
+) {
+ size_t msz;
+ if (cx_szmul(nelem, elsize, &msz)) {
+ return NULL;
+ }
+ void *ptr = cx_malloc_basic_mempool(data, msz);
+ if (ptr == NULL) {
+ return NULL;
+ }
+ memset(ptr, 0, nelem * elsize);
+ return ptr;
+}
+
+void *cx_realloc_basic_mempool(
+ void *data,
+ void *ptr,
+ size_t n
+) {
+ of_chk_(n);
+ struct cx_basic_mempool_s *pool = data;
+
+ char *mem = ((char *) ptr) - sizeof(cx_destructor_func);
+ char *newm = (char *) realloc(mem, n + sizeof(cx_destructor_func));
+ if (newm == NULL) {
+ return NULL;
+ }
+ if (mem != newm) {
+ cx_for_n(i, pool->ndata) {
+ if (pool->data[i] == mem) {
+ pool->data[i] = newm;
+ return newm + sizeof(cx_destructor_func);
+ }
+ }
+ abort();
+ } else {
+ return newm + sizeof(cx_destructor_func);
+ }
+}
+
+void cx_free_basic_mempool(
+ void *data,
+ void *ptr
+) {
+ struct cx_basic_mempool_s *pool = data;
+
+ cx_basic_mempool_memory *mem = (cx_basic_mempool_memory *)
+ ((char *) ptr - sizeof(cx_destructor_func));
+ cx_for_n(i, pool->ndata) {
+ if (mem == pool->data[i]) {
+ if (mem->destructor != NULL) {
+ mem->destructor(&(mem->c));
+ }
+ free(mem);
+ size_t last_index = pool->ndata - 1;
+ if (i != last_index) {
+ pool->data[i] = pool->data[last_index];
+ pool->data[last_index] = NULL;
+ }
+ pool->ndata--;
+ return;
+ }
+ }
+ abort();
+}
+
+void cx_basic_mempool_destroy(CxMempool *p) {
+ struct cx_basic_mempool_s *pool = (struct cx_basic_mempool_s *) p;
+ cx_basic_mempool_memory *mem;
+ cx_for_n(i, pool->ndata) {
+ mem = (cx_basic_mempool_memory *) pool->data[i];
+ if (mem) {
+ if (mem->destructor) {
+ mem->destructor(&(mem->c));
+ }
+ free(mem);
+ }
+ }
+ free(pool->data);
+ free((void *) p->allocator);
+ free(pool);
+}
+
+void cx_basic_mempool_set_destr(
+ __attribute__((__unused__)) CxMempool *pool,
+ void *ptr,
+ cx_destructor_func func
+) {
+ *(cx_destructor_func *) ((char *) ptr - sizeof(cx_destructor_func)) = func;
+}
+
+static cx_allocator_class cx_basic_mempool_allocator_class = {
+ cx_malloc_basic_mempool,
+ cx_realloc_basic_mempool,
+ cx_calloc_basic_mempool,
+ cx_free_basic_mempool
+};
+
+static cx_mempool_class cx_basic_mempool_class = {
+ cx_basic_mempool_destroy,
+ cx_basic_mempool_set_destr,
+};
+
+CxMempool *cxBasicMempoolCreate(size_t capacity) {
+ size_t poolsize;
+ if (cx_szmul(capacity, sizeof(void *), &poolsize)) {
+ return NULL;
+ }
+
+ struct cx_basic_mempool_s *pool =
+ malloc(sizeof(struct cx_basic_mempool_s));
+ if (pool == NULL) {
+ return NULL;
+ }
+
+
+ CxAllocator *provided_allocator = malloc(sizeof(CxAllocator));
+ if (!provided_allocator) {
+ free(pool);
+ return NULL;
+ }
+ provided_allocator->cl = &cx_basic_mempool_allocator_class;
+ provided_allocator->data = pool;
+
+ pool->base.cl = &cx_basic_mempool_class;
+ pool->base.allocator = provided_allocator;
+
+ pool->data = malloc(poolsize);
+ if (pool->data == NULL) {
+ free(provided_allocator);
+ free(pool);
+ return NULL;
+ }
+
+ pool->ndata = 0;
+ pool->size = capacity;
+
+ return (CxMempool *) pool;
+}
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* POSSIBILITY OF SUCH DAMAGE.
*/
-#include "ucx/buffer.h"
+#include "cx/buffer.h"
+#include "cx/utils.h"
-#include <stdarg.h>
-#include <stdlib.h>
+#include <stdio.h>
#include <string.h>
-UcxBuffer *ucx_buffer_new(void *space, size_t capacity, int flags) {
- UcxBuffer *buffer = (UcxBuffer*) malloc(sizeof(UcxBuffer));
- if (buffer) {
- buffer->flags = flags;
- if (!space) {
- buffer->space = (char*)malloc(capacity);
- if (!buffer->space) {
- free(buffer);
- return NULL;
- }
- memset(buffer->space, 0, capacity);
- buffer->flags |= UCX_BUFFER_AUTOFREE;
- } else {
- buffer->space = (char*)space;
+int cxBufferInit(
+ CxBuffer *buffer,
+ void *space,
+ size_t capacity,
+ CxAllocator const *allocator,
+ int flags
+) {
+ if (allocator == NULL) allocator = cxDefaultAllocator;
+ buffer->allocator = allocator;
+ buffer->flags = flags;
+ if (!space) {
+ buffer->bytes = cxMalloc(allocator, capacity);
+ if (buffer->bytes == NULL) {
+ return 1;
}
- buffer->capacity = capacity;
- buffer->size = 0;
-
- buffer->pos = 0;
+ buffer->flags |= CX_BUFFER_FREE_CONTENTS;
+ } else {
+ buffer->bytes = space;
}
+ buffer->capacity = capacity;
+ buffer->size = 0;
+ buffer->pos = 0;
- return buffer;
-}
+ buffer->flush_func = NULL;
+ buffer->flush_target = NULL;
+ buffer->flush_blkmax = 0;
+ buffer->flush_blksize = 4096;
+ buffer->flush_threshold = SIZE_MAX;
-void ucx_buffer_free(UcxBuffer *buffer) {
- if ((buffer->flags & UCX_BUFFER_AUTOFREE) == UCX_BUFFER_AUTOFREE) {
- free(buffer->space);
- }
- free(buffer);
+ return 0;
}
-UcxBuffer* ucx_buffer_extract(
- UcxBuffer *src, size_t start, size_t length, int flags) {
- if (src->size == 0 || length == 0 ||
- ((size_t)-1) - start < length || start+length > src->capacity)
- {
- return NULL;
- }
-
- UcxBuffer *dst = (UcxBuffer*) malloc(sizeof(UcxBuffer));
- if (dst) {
- dst->space = (char*)malloc(length);
- if (!dst->space) {
- free(dst);
- return NULL;
- }
- dst->capacity = length;
- dst->size = length;
- dst->flags = flags | UCX_BUFFER_AUTOFREE;
- dst->pos = 0;
- memcpy(dst->space, src->space+start, length);
+void cxBufferDestroy(CxBuffer *buffer) {
+ if ((buffer->flags & CX_BUFFER_FREE_CONTENTS) == CX_BUFFER_FREE_CONTENTS) {
+ cxFree(buffer->allocator, buffer->bytes);
}
- return dst;
}
-int ucx_buffer_seek(UcxBuffer *buffer, off_t offset, int whence) {
+int cxBufferSeek(
+ CxBuffer *buffer,
+ off_t offset,
+ int whence
+) {
size_t npos;
switch (whence) {
- case SEEK_CUR:
- npos = buffer->pos;
- break;
- case SEEK_END:
- npos = buffer->size;
- break;
- case SEEK_SET:
- npos = 0;
- break;
- default:
- return -1;
+ case SEEK_CUR:
+ npos = buffer->pos;
+ break;
+ case SEEK_END:
+ npos = buffer->size;
+ break;
+ case SEEK_SET:
+ npos = 0;
+ break;
+ default:
+ return -1;
}
size_t opos = npos;
npos += offset;
-
+
if ((offset > 0 && npos < opos) || (offset < 0 && npos > opos)) {
return -1;
}
-
+
if (npos >= buffer->size) {
return -1;
} else {
}
-int ucx_buffer_eof(UcxBuffer *buffer) {
+void cxBufferClear(CxBuffer *buffer) {
+ memset(buffer->bytes, 0, buffer->size);
+ buffer->size = 0;
+ buffer->pos = 0;
+}
+
+int cxBufferEof(CxBuffer const *buffer) {
return buffer->pos >= buffer->size;
}
-int ucx_buffer_extend(UcxBuffer *buffer, size_t len) {
- size_t newcap = buffer->capacity;
-
- if (buffer->capacity + len < buffer->capacity) {
- return -1;
- }
-
- while (buffer->capacity + len > newcap) {
- newcap <<= 1;
- if (newcap < buffer->capacity) {
- return -1;
- }
+int cxBufferMinimumCapacity(
+ CxBuffer *buffer,
+ size_t newcap
+) {
+ if (newcap <= buffer->capacity) {
+ return 0;
}
-
- char *newspace = (char*)realloc(buffer->space, newcap);
- if (newspace) {
- memset(newspace+buffer->size, 0, newcap-buffer->size);
- buffer->space = newspace;
+
+ if (cxReallocate(buffer->allocator,
+ (void **) &buffer->bytes, newcap) == 0) {
buffer->capacity = newcap;
+ return 0;
} else {
return -1;
}
-
- return 0;
}
-size_t ucx_buffer_write(const void *ptr, size_t size, size_t nitems,
- UcxBuffer *buffer) {
+/**
+ * Helps flushing data to the flush target of a buffer.
+ *
+ * @param buffer the buffer containing the config
+ * @param space the data to flush
+ * @param size the element size
+ * @param nitems the number of items
+ * @return the number of items flushed
+ */
+static size_t cx_buffer_write_flush_helper(
+ CxBuffer *buffer,
+ unsigned char const *space,
+ size_t size,
+ size_t nitems
+) {
+ size_t pos = 0;
+ size_t remaining = nitems;
+ size_t max_items = buffer->flush_blksize / size;
+ while (remaining > 0) {
+ size_t items = remaining > max_items ? max_items : remaining;
+ size_t flushed = buffer->flush_func(
+ space + pos,
+ size, items,
+ buffer->flush_target);
+ if (flushed > 0) {
+ pos += (flushed * size);
+ remaining -= flushed;
+ } else {
+ // if no bytes can be flushed out anymore, we give up
+ break;
+ }
+ }
+ return nitems - remaining;
+}
+
+size_t cxBufferWrite(
+ void const *ptr,
+ size_t size,
+ size_t nitems,
+ CxBuffer *buffer
+) {
+ // optimize for easy case
+ if (size == 1 && (buffer->capacity - buffer->pos) >= nitems) {
+ memcpy(buffer->bytes + buffer->pos, ptr, nitems);
+ buffer->pos += nitems;
+ if (buffer->pos > buffer->size) {
+ buffer->size = buffer->pos;
+ }
+ return nitems;
+ }
+
size_t len;
- if(ucx_szmul(size, nitems, &len)) {
+ size_t nitems_out = nitems;
+ if (cx_szmul(size, nitems, &len)) {
return 0;
}
size_t required = buffer->pos + len;
if (buffer->pos > required) {
return 0;
}
-
+
+ bool perform_flush = false;
if (required > buffer->capacity) {
- if ((buffer->flags & UCX_BUFFER_AUTOEXTEND) == UCX_BUFFER_AUTOEXTEND) {
- if (ucx_buffer_extend(buffer, required - buffer->capacity)) {
- return 0;
+ if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == CX_BUFFER_AUTO_EXTEND && required) {
+ if (buffer->flush_blkmax > 0 && required > buffer->flush_threshold) {
+ perform_flush = true;
+ } else {
+ if (cxBufferMinimumCapacity(buffer, required)) {
+ return 0;
+ }
}
} else {
- len = buffer->capacity - buffer->pos;
- if (size > 1) {
- len -= len%size;
+ if (buffer->flush_blkmax > 0) {
+ perform_flush = true;
+ } else {
+ // truncate data to be written, if we can neither extend nor flush
+ len = buffer->capacity - buffer->pos;
+ if (size > 1) {
+ len -= len % size;
+ }
+ nitems_out = len / size;
}
}
}
-
+
if (len == 0) {
return len;
}
-
- memcpy(buffer->space + buffer->pos, ptr, len);
- buffer->pos += len;
- if(buffer->pos > buffer->size) {
- buffer->size = buffer->pos;
+
+ if (perform_flush) {
+ size_t flush_max;
+ if (cx_szmul(buffer->flush_blkmax, buffer->flush_blksize, &flush_max)) {
+ return 0;
+ }
+ size_t flush_pos = buffer->flush_func == NULL || buffer->flush_target == NULL
+ ? buffer->pos
+ : cx_buffer_write_flush_helper(buffer, buffer->bytes, 1, buffer->pos);
+ if (flush_pos == buffer->pos) {
+ // entire buffer has been flushed, we can reset
+ buffer->size = buffer->pos = 0;
+
+ size_t items_flush; // how many items can also be directly flushed
+ size_t items_keep; // how many items have to be written to the buffer
+
+ items_flush = flush_max >= required ? nitems : (flush_max - flush_pos) / size;
+ if (items_flush > 0) {
+ items_flush = cx_buffer_write_flush_helper(buffer, ptr, size, items_flush / size);
+ // in case we could not flush everything, keep the rest
+ }
+ items_keep = nitems - items_flush;
+ if (items_keep > 0) {
+ // try again with the remaining stuff
+ unsigned char const *new_ptr = ptr;
+ new_ptr += items_flush * size;
+ // report the directly flushed items as written plus the remaining stuff
+ return items_flush + cxBufferWrite(new_ptr, size, items_keep, buffer);
+ } else {
+ // all items have been flushed - report them as written
+ return nitems;
+ }
+ } else if (flush_pos == 0) {
+ // nothing could be flushed at all, we immediately give up without writing any data
+ return 0;
+ } else {
+ // we were partially successful, we shift left and try again
+ cxBufferShiftLeft(buffer, flush_pos);
+ return cxBufferWrite(ptr, size, nitems, buffer);
+ }
+ } else {
+ memcpy(buffer->bytes + buffer->pos, ptr, len);
+ buffer->pos += len;
+ if (buffer->pos > buffer->size) {
+ buffer->size = buffer->pos;
+ }
+ return nitems_out;
}
-
- return len / size;
+
+}
+
+int cxBufferPut(
+ CxBuffer *buffer,
+ int c
+) {
+ c &= 0xFF;
+ unsigned char const ch = c;
+ if (cxBufferWrite(&ch, 1, 1, buffer) == 1) {
+ return c;
+ } else {
+ return EOF;
+ }
+}
+
+size_t cxBufferPutString(
+ CxBuffer *buffer,
+ const char *str
+) {
+ return cxBufferWrite(str, 1, strlen(str), buffer);
}
-size_t ucx_buffer_read(void *ptr, size_t size, size_t nitems,
- UcxBuffer *buffer) {
+size_t cxBufferRead(
+ void *ptr,
+ size_t size,
+ size_t nitems,
+ CxBuffer *buffer
+) {
size_t len;
- if(ucx_szmul(size, nitems, &len)) {
+ if (cx_szmul(size, nitems, &len)) {
return 0;
}
if (buffer->pos + len > buffer->size) {
len = buffer->size - buffer->pos;
- if (size > 1) len -= len%size;
+ if (size > 1) len -= len % size;
}
-
+
if (len <= 0) {
return len;
}
-
- memcpy(ptr, buffer->space + buffer->pos, len);
+
+ memcpy(ptr, buffer->bytes + buffer->pos, len);
buffer->pos += len;
-
- return len / size;
-}
-int ucx_buffer_putc(UcxBuffer *buffer, int c) {
- if(buffer->pos >= buffer->capacity) {
- if ((buffer->flags & UCX_BUFFER_AUTOEXTEND) == UCX_BUFFER_AUTOEXTEND) {
- if(ucx_buffer_extend(buffer, 1)) {
- return EOF;
- }
- } else {
- return EOF;
- }
- }
-
- c &= 0xFF;
- buffer->space[buffer->pos] = (char) c;
- buffer->pos++;
- if(buffer->pos > buffer->size) {
- buffer->size = buffer->pos;
- }
- return c;
+ return len / size;
}
-int ucx_buffer_getc(UcxBuffer *buffer) {
- if (ucx_buffer_eof(buffer)) {
+int cxBufferGet(CxBuffer *buffer) {
+ if (cxBufferEof(buffer)) {
return EOF;
} else {
- int c = ((unsigned char*)buffer->space)[buffer->pos];
+ int c = buffer->bytes[buffer->pos];
buffer->pos++;
return c;
}
}
-size_t ucx_buffer_puts(UcxBuffer *buffer, const char *str) {
- return ucx_buffer_write((const void*)str, 1, strlen(str), buffer);
-}
-
-int ucx_buffer_shift_left(UcxBuffer* buffer, size_t shift) {
+int cxBufferShiftLeft(
+ CxBuffer *buffer,
+ size_t shift
+) {
if (shift >= buffer->size) {
buffer->pos = buffer->size = 0;
} else {
- memmove(buffer->space, buffer->space + shift, buffer->size - shift);
+ memmove(buffer->bytes, buffer->bytes + shift, buffer->size - shift);
buffer->size -= shift;
-
+
if (buffer->pos >= shift) {
buffer->pos -= shift;
} else {
return 0;
}
-int ucx_buffer_shift_right(UcxBuffer* buffer, size_t shift) {
+int cxBufferShiftRight(
+ CxBuffer *buffer,
+ size_t shift
+) {
size_t req_capacity = buffer->size + shift;
size_t movebytes;
-
+
// auto extend buffer, if required and enabled
if (buffer->capacity < req_capacity) {
- if ((buffer->flags & UCX_BUFFER_AUTOEXTEND) == UCX_BUFFER_AUTOEXTEND) {
- if (ucx_buffer_extend(buffer, req_capacity - buffer->capacity)) {
+ if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == CX_BUFFER_AUTO_EXTEND) {
+ if (cxBufferMinimumCapacity(buffer, req_capacity)) {
return 1;
}
movebytes = buffer->size;
} else {
movebytes = buffer->size;
}
-
- memmove(buffer->space + shift, buffer->space, movebytes);
- buffer->size = shift+movebytes;
-
+
+ memmove(buffer->bytes + shift, buffer->bytes, movebytes);
+ buffer->size = shift + movebytes;
+
buffer->pos += shift;
if (buffer->pos > buffer->size) {
buffer->pos = buffer->size;
}
-
+
return 0;
}
-int ucx_buffer_shift(UcxBuffer* buffer, off_t shift) {
+int cxBufferShift(
+ CxBuffer *buffer,
+ off_t shift
+) {
if (shift < 0) {
- return ucx_buffer_shift_left(buffer, (size_t) (-shift));
+ return cxBufferShiftLeft(buffer, (size_t) (-shift));
} else if (shift > 0) {
- return ucx_buffer_shift_right(buffer, (size_t) shift);
+ return cxBufferShiftRight(buffer, (size_t) shift);
} else {
return 0;
}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "cx/compare.h"
+
+#include <math.h>
+
+int cx_cmp_int(void const *i1, void const *i2) {
+ int a = *((const int*) i1);
+ int b = *((const int*) i2);
+ if (a == b) {
+ return 0;
+ } else {
+ return a < b ? -1 : 1;
+ }
+}
+
+int cx_cmp_longint(void const *i1, void const *i2) {
+ long int a = *((const long int*) i1);
+ long int b = *((const long int*) i2);
+ if (a == b) {
+ return 0;
+ } else {
+ return a < b ? -1 : 1;
+ }
+}
+
+int cx_cmp_longlong(void const *i1, void const *i2) {
+ long long a = *((const long long*) i1);
+ long long b = *((const long long*) i2);
+ if (a == b) {
+ return 0;
+ } else {
+ return a < b ? -1 : 1;
+ }
+}
+
+int cx_cmp_int16(void const *i1, void const *i2) {
+ int16_t a = *((const int16_t*) i1);
+ int16_t b = *((const int16_t*) i2);
+ if (a == b) {
+ return 0;
+ } else {
+ return a < b ? -1 : 1;
+ }
+}
+
+int cx_cmp_int32(void const *i1, void const *i2) {
+ int32_t a = *((const int32_t*) i1);
+ int32_t b = *((const int32_t*) i2);
+ if (a == b) {
+ return 0;
+ } else {
+ return a < b ? -1 : 1;
+ }
+}
+
+int cx_cmp_int64(void const *i1, void const *i2) {
+ int64_t a = *((const int64_t*) i1);
+ int64_t b = *((const int64_t*) i2);
+ if (a == b) {
+ return 0;
+ } else {
+ return a < b ? -1 : 1;
+ }
+}
+
+int cx_cmp_uint(void const *i1, void const *i2) {
+ unsigned int a = *((const unsigned int*) i1);
+ unsigned int b = *((const unsigned int*) i2);
+ if (a == b) {
+ return 0;
+ } else {
+ return a < b ? -1 : 1;
+ }
+}
+
+int cx_cmp_ulongint(void const *i1, void const *i2) {
+ unsigned long int a = *((const unsigned long int*) i1);
+ unsigned long int b = *((const unsigned long int*) i2);
+ if (a == b) {
+ return 0;
+ } else {
+ return a < b ? -1 : 1;
+ }
+}
+
+int cx_cmp_ulonglong(void const *i1, void const *i2) {
+ unsigned long long a = *((const unsigned long long*) i1);
+ unsigned long long b = *((const unsigned long long*) i2);
+ if (a == b) {
+ return 0;
+ } else {
+ return a < b ? -1 : 1;
+ }
+}
+
+int cx_cmp_uint16(void const *i1, void const *i2) {
+ uint16_t a = *((const uint16_t*) i1);
+ uint16_t b = *((const uint16_t*) i2);
+ if (a == b) {
+ return 0;
+ } else {
+ return a < b ? -1 : 1;
+ }
+}
+
+int cx_cmp_uint32(void const *i1, void const *i2) {
+ uint32_t a = *((const uint32_t*) i1);
+ uint32_t b = *((const uint32_t*) i2);
+ if (a == b) {
+ return 0;
+ } else {
+ return a < b ? -1 : 1;
+ }
+}
+
+int cx_cmp_uint64(void const *i1, void const *i2) {
+ uint64_t a = *((const uint64_t*) i1);
+ uint64_t b = *((const uint64_t*) i2);
+ if (a == b) {
+ return 0;
+ } else {
+ return a < b ? -1 : 1;
+ }
+}
+
+int cx_cmp_float(void const *f1, void const *f2) {
+ float a = *((const float*) f1);
+ float b = *((const float*) f2);
+ if (fabsf(a - b) < 1e-6f) {
+ return 0;
+ } else {
+ return a < b ? -1 : 1;
+ }
+}
+
+int cx_cmp_double(
+ void const *d1,
+ void const *d2
+) {
+ double a = *((const double *) d1);
+ double b = *((const double *) d2);
+ if (fabs(a - b) < 1e-14) {
+ return 0;
+ } else {
+ return a < b ? -1 : 1;
+ }
+}
+
+int cx_cmp_intptr(
+ void const *ptr1,
+ void const *ptr2
+) {
+ intptr_t p1 = *(const intptr_t *) ptr1;
+ intptr_t p2 = *(const intptr_t *) ptr2;
+ if (p1 == p2) {
+ return 0;
+ } else {
+ return p1 < p2 ? -1 : 1;
+ }
+}
+
+int cx_cmp_uintptr(
+ void const *ptr1,
+ void const *ptr2
+) {
+ uintptr_t p1 = *(const uintptr_t *) ptr1;
+ uintptr_t p2 = *(const uintptr_t *) ptr2;
+ if (p1 == p2) {
+ return 0;
+ } else {
+ return p1 < p2 ? -1 : 1;
+ }
+}
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * \file allocator.h
+ * Interface for custom allocators.
+ */
+
+#ifndef UCX_ALLOCATOR_H
+#define UCX_ALLOCATOR_H
+
+#include "common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * The class definition for an allocator.
+ */
+typedef struct {
+ /**
+ * The allocator's malloc() implementation.
+ */
+ void *(*malloc)(
+ void *data,
+ size_t n
+ );
+
+ /**
+ * The allocator's realloc() implementation.
+ */
+ void *(*realloc)(
+ void *data,
+ void *mem,
+ size_t n
+ )
+ __attribute__((__warn_unused_result__));
+
+ /**
+ * The allocator's calloc() implementation.
+ */
+ void *(*calloc)(
+ void *data,
+ size_t nelem,
+ size_t n
+ );
+
+ /**
+ * The allocator's free() implementation.
+ */
+ void (*free)(
+ void *data,
+ void *mem
+ )
+ __attribute__((__nonnull__));
+} cx_allocator_class;
+
+/**
+ * Structure holding the data for an allocator.
+ */
+struct cx_allocator_s {
+ /**
+ * A pointer to the instance of the allocator class.
+ */
+ cx_allocator_class *cl;
+ /**
+ * A pointer to the data this allocator uses.
+ */
+ void *data;
+};
+
+/**
+ * High-Level type alias for the allocator type.
+ */
+typedef struct cx_allocator_s CxAllocator;
+
+/**
+ * A default allocator using standard library malloc() etc.
+ */
+extern CxAllocator *cxDefaultAllocator;
+
+/**
+ * Function pointer type for destructor functions.
+ *
+ * A destructor function deallocates possible contents and MAY free the memory
+ * pointed to by \p memory. Read the documentation of the respective function
+ * pointer to learn if a destructor SHALL, MAY, or MUST NOT free the memory in that
+ * particular implementation.
+ *
+ * @param memory a pointer to the object to destruct
+ */
+typedef void (*cx_destructor_func)(void *memory) __attribute__((__nonnull__));
+
+/**
+ * Function pointer type for destructor functions.
+ *
+ * A destructor function deallocates possible contents and MAY free the memory
+ * pointed to by \p memory. Read the documentation of the respective function
+ * pointer to learn if a destructor SHALL, MAY, or MUST NOT free the memory in that
+ * particular implementation.
+ *
+ * @param data an optional pointer to custom data
+ * @param memory a pointer to the object to destruct
+ */
+typedef void (*cx_destructor_func2)(
+ void *data,
+ void *memory
+) __attribute__((__nonnull__(2)));
+
+/**
+ * Structure holding an advanced destructor function and the desired payload.
+ * Invocations of func should use data as first argument.
+ */
+typedef struct {
+ /**
+ * A pointer to the data that SHALL be used to invoke func.
+ */
+ void *data;
+ /**
+ * A pointer to the function to invoke.
+ */
+ cx_destructor_func2 func;
+} cx_advanced_destructor;
+
+/**
+ * Specifies the type of destructor to use.
+ */
+enum cx_destructor_type {
+ /**
+ * Do not use a destructor function.
+ */
+ CX_DESTRUCTOR_NONE,
+ /**
+ * Use a simple destructor.
+ * @see cx_destructor_func
+ */
+ CX_DESTRUCTOR_SIMPLE,
+ /**
+ * Use an advanced destructor.
+ * @see cx_advanced_destructor
+ */
+ CX_DESTRUCTOR_ADVANCED
+};
+
+/**
+ * Allocate \p n bytes of memory.
+ *
+ * @param allocator the allocator
+ * @param n the number of bytes
+ * @return a pointer to the allocated memory
+ */
+void *cxMalloc(
+ CxAllocator const *allocator,
+ size_t n
+)
+__attribute__((__malloc__))
+__attribute__((__alloc_size__(2)));
+
+/**
+ * Re-allocate the previously allocated block in \p mem, making the new block \p n bytes long.
+ * This function may return the same pointer that was passed to it, if moving the memory
+ * was not necessary.
+ *
+ * \note Re-allocating a block allocated by a different allocator is undefined.
+ *
+ * @param allocator the allocator
+ * @param mem pointer to the previously allocated block
+ * @param n the new size in bytes
+ * @return a pointer to the re-allocated memory
+ */
+void *cxRealloc(
+ CxAllocator const *allocator,
+ void *mem,
+ size_t n
+)
+__attribute__((__warn_unused_result__))
+__attribute__((__alloc_size__(3)));
+
+/**
+ * Re-allocate a previously allocated block and changes the pointer in-place, if necessary.
+ * This function acts like cxRealloc() using the pointer pointed to by \p mem.
+ * On success, the pointer is changed to the new location (in case the
+ *
+ * \note Re-allocating a block allocated by a different allocator is undefined.
+ *
+ * \par Error handling
+ * \c errno will be set, if the underlying realloc function does so.
+ *
+ * @param allocator the allocator
+ * @param mem pointer to the pointer to allocated block
+ * @param n the new size in bytes
+ * @return zero on success, non-zero on failure
+ */
+int cxReallocate(
+ CxAllocator const *allocator,
+ void **mem,
+ size_t n
+)
+__attribute__((__nonnull__));
+
+/**
+ * Allocate \p nelem elements of \p n bytes each, all initialized to zero.
+ *
+ * @param allocator the allocator
+ * @param nelem the number of elements
+ * @param n the size of each element in bytes
+ * @return a pointer to the allocated memory
+ */
+void *cxCalloc(
+ CxAllocator const *allocator,
+ size_t nelem,
+ size_t n
+)
+__attribute__((__malloc__))
+__attribute__((__alloc_size__(2, 3)));
+
+/**
+ * Free a block allocated by this allocator.
+ *
+ * \note Freeing a block of a different allocator is undefined.
+ *
+ * @param allocator the allocator
+ * @param mem a pointer to the block to free
+ */
+void cxFree(
+ CxAllocator const *allocator,
+ void *mem
+)
+__attribute__((__nonnull__));
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // UCX_ALLOCATOR_H
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * \file array_list.h
+ * \brief Array list implementation.
+ * \details Also provides several low-level functions for custom array list implementations.
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+
+#ifndef UCX_ARRAY_LIST_H
+#define UCX_ARRAY_LIST_H
+
+#include "list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Defines a reallocation mechanism for arrays.
+ */
+struct cx_array_reallocator_s {
+ /**
+ * Re-allocates space for the given array.
+ *
+ * Implementations are not required to free the original array.
+ * This allows re-allocation of static memory by allocating heap memory
+ * and copying the array contents. The information in \p data can keep
+ * track of the state of the memory or other additional allocator info.
+ *
+ * @param array the array to reallocate
+ * @param capacity the new capacity (number of elements)
+ * @param elem_size the size of each element
+ * @param alloc a reference to this allocator
+ * @return a pointer to the reallocated memory or \c NULL on failure
+ */
+ void *(*realloc)(
+ void *array,
+ size_t capacity,
+ size_t elem_size,
+ struct cx_array_reallocator_s *alloc
+ );
+
+ /**
+ * Custom data pointer.
+ */
+ void *ptr1;
+ /**
+ * Custom data pointer.
+ */
+ void *ptr2;
+ /**
+ * Custom data integer.
+ */
+ size_t int1;
+ /**
+ * Custom data integer.
+ */
+ size_t int2;
+};
+
+/**
+ * Return codes for cx_array_copy().
+ */
+enum cx_array_copy_result {
+ CX_ARRAY_COPY_SUCCESS,
+ CX_ARRAY_COPY_REALLOC_NOT_SUPPORTED,
+ CX_ARRAY_COPY_REALLOC_FAILED,
+};
+
+/**
+ * Copies elements from one array to another.
+ *
+ * The elements are copied to the \p target array at the specified \p index,
+ * overwriting possible elements. The \p index does not need to be in range of
+ * the current array \p size. If the new index plus the number of elements added
+ * would extend the array's size, and \p capacity is not \c NULL, the remaining
+ * capacity is used.
+ *
+ * If the capacity is insufficient to hold the new data, a reallocation
+ * attempt is made, unless the allocator is set to \c NULL, in which case
+ * this function ultimately returns a failure.
+ *
+ * @param target the target array
+ * @param size a pointer to the size of the target array
+ * @param capacity a pointer to the target array's capacity -
+ * \c NULL if only the size shall be used to bound the array
+ * @param index the index where the copied elements shall be placed
+ * @param src the source array
+ * @param elem_size the size of one element
+ * @param elem_count the number of elements to copy
+ * @param reallocator the array re-allocator to use, or \c NULL
+ * if re-allocation shall not happen
+ * @return zero on success, non-zero error code on failure
+ */
+enum cx_array_copy_result cx_array_copy(
+ void **target,
+ size_t *size,
+ size_t *capacity,
+ size_t index,
+ void const *src,
+ size_t elem_size,
+ size_t elem_count,
+ struct cx_array_reallocator_s *reallocator
+) __attribute__((__nonnull__(1, 2, 5)));
+
+
+/**
+ * Swaps two array elements.
+ *
+ * @param arr the array
+ * @param elem_size the element size
+ * @param idx1 index of first element
+ * @param idx2 index of second element
+ */
+void cx_array_swap(
+ void *arr,
+ size_t elem_size,
+ size_t idx1,
+ size_t idx2
+) __attribute__((__nonnull__));
+
+/**
+ * Allocates an array list for storing elements with \p item_size bytes each.
+ *
+ * If \p item_size is CX_STORE_POINTERS, the created list will be created as if
+ * cxListStorePointers() was called immediately after creation.
+ *
+ * @param allocator the allocator for allocating the list memory
+ * (if \c NULL the cxDefaultAllocator will be used)
+ * @param comparator the comparator for the elements
+ * (if \c NULL sort and find functions will not work)
+ * @param item_size the size of each element in bytes
+ * @param initial_capacity the initial number of elements the array can store
+ * @return the created list
+ */
+CxList *cxArrayListCreate(
+ CxAllocator const *allocator,
+ CxListComparator comparator,
+ size_t item_size,
+ size_t initial_capacity
+);
+
+/**
+ * Allocates an array list for storing elements with \p item_size bytes each.
+ *
+ * The list will use the cxDefaultAllocator and \em NO compare function.
+ * If you want to call functions that need a compare function, you have to
+ * set it immediately after creation or use cxArrayListCreate().
+ *
+ * If \p item_size is CX_STORE_POINTERS, the created list will be created as if
+ * cxListStorePointers() was called immediately after creation.
+ *
+ * @param item_size the size of each element in bytes
+ * @param initial_capacity the initial number of elements the array can store
+ * @return the created list
+ */
+#define cxArrayListCreateSimple(item_size, initial_capacity) \
+ cxArrayListCreate(NULL, NULL, item_size, initial_capacity)
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // UCX_ARRAY_LIST_H
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * \file basic_mempool.h
+ * \brief Implementation of a basic memory pool.
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+#ifndef UCX_BASIC_MEMPOOL_H
+#define UCX_BASIC_MEMPOOL_H
+
+#include "mempool.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Basic array-based memory pool.
+ */
+struct cx_basic_mempool_s {
+ /** Inherit base structure members. */
+ CxMempool base;
+
+ /** List of pointers to pooled memory. */
+ void **data;
+
+ /** Number of pooled memory items. */
+ size_t ndata;
+
+ /** Memory pool size. */
+ size_t size;
+};
+
+/**
+ * Creates a basic array-based memory pool.
+ *
+ * @param capacity the initial capacity of the pool
+ * @return the created memory pool or \c NULL if allocation failed
+ */
+__attribute__((__warn_unused_result__))
+CxMempool *cxBasicMempoolCreate(size_t capacity);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // UCX_BASIC_MEMPOOL_H
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file buffer.h
+ *
+ * \brief Advanced buffer implementation.
+ *
+ * Instances of CxBuffer can be used to read from or to write to like one
+ * would do with a stream.
+ *
+ * Some features for convenient use of the buffer
+ * can be enabled. See the documentation of the macro constants for more
+ * information.
+ *
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+#ifndef UCX_BUFFER_H
+#define UCX_BUFFER_H
+
+#include "common.h"
+#include "allocator.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * No buffer features enabled (all flags cleared).
+ */
+#define CX_BUFFER_DEFAULT 0x00
+
+/**
+ * If this flag is enabled, the buffer will automatically free its contents when destroyed.
+ */
+#define CX_BUFFER_FREE_CONTENTS 0x01
+
+/**
+ * If this flag is enabled, the buffer will automatically extends its capacity.
+ */
+#define CX_BUFFER_AUTO_EXTEND 0x02
+
+/** Structure for the UCX buffer data. */
+typedef struct {
+ /** A pointer to the buffer contents. */
+ union {
+ /**
+ * Data is interpreted as text.
+ */
+ char *space;
+ /**
+ * Data is interpreted as binary.
+ */
+ unsigned char *bytes;
+ };
+ /** The allocator to use for automatic memory management. */
+ CxAllocator const *allocator;
+ /** Current position of the buffer. */
+ size_t pos;
+ /** Current capacity (i.e. maximum size) of the buffer. */
+ size_t capacity;
+ /** Current size of the buffer content. */
+ size_t size;
+ /**
+ * The buffer may not extend beyond this threshold before starting to flush.
+ * Default is \c SIZE_MAX (flushing disabled when auto extension is enabled).
+ */
+ size_t flush_threshold;
+ /**
+ * The block size for the elements to flush.
+ * Default is 4096 bytes.
+ */
+ size_t flush_blksize;
+ /**
+ * The maximum number of blocks to flush in one cycle.
+ * Zero disables flushing entirely (this is the default).
+ * Set this to \c SIZE_MAX to flush the entire buffer.
+ *
+ * @attention if the maximum number of blocks multiplied with the block size
+ * is smaller than the expected contents written to this buffer within one write
+ * operation, multiple flush cycles are performed after that write.
+ * That means the total number of blocks flushed after one write to this buffer may
+ * be larger than \c flush_blkmax.
+ */
+ size_t flush_blkmax;
+
+ /**
+ * The write function used for flushing.
+ * If NULL, the flushed content gets discarded.
+ */
+ cx_write_func flush_func;
+
+ /**
+ * The target for \c flush_func.
+ */
+ void *flush_target;
+
+ /**
+ * Flag register for buffer features.
+ * @see #CX_BUFFER_DEFAULT
+ * @see #CX_BUFFER_FREE_CONTENTS
+ * @see #CX_BUFFER_AUTO_EXTEND
+ */
+ int flags;
+} cx_buffer_s;
+
+/**
+ * UCX buffer.
+ */
+typedef cx_buffer_s CxBuffer;
+
+/**
+ * Initializes a fresh buffer.
+ *
+ * \note You may provide \c NULL as argument for \p space.
+ * Then this function will allocate the space and enforce
+ * the #CX_BUFFER_FREE_CONTENTS flag.
+ *
+ * @param buffer the buffer to initialize
+ * @param space pointer to the memory area, or \c NULL to allocate
+ * new memory
+ * @param capacity the capacity of the buffer
+ * @param allocator the allocator this buffer shall use for automatic
+ * memory management. If \c NULL, the default heap allocator will be used.
+ * @param flags buffer features (see cx_buffer_s.flags)
+ * @return zero on success, non-zero if a required allocation failed
+ */
+__attribute__((__nonnull__(1)))
+int cxBufferInit(
+ CxBuffer *buffer,
+ void *space,
+ size_t capacity,
+ CxAllocator const *allocator,
+ int flags
+);
+
+/**
+ * Destroys the buffer contents.
+ *
+ * Has no effect if the #CX_BUFFER_FREE_CONTENTS feature is not enabled.
+ *
+ * @param buffer the buffer which contents shall be destroyed
+ */
+__attribute__((__nonnull__))
+void cxBufferDestroy(CxBuffer *buffer);
+
+/**
+ * Shifts the contents of the buffer by the given offset.
+ *
+ * If the offset is positive, the contents are shifted to the right.
+ * If auto extension is enabled, the buffer grows, if necessary.
+ * In case the auto extension fails, this function returns a non-zero value and
+ * no contents are changed.
+ * If auto extension is disabled, the contents that do not fit into the buffer
+ * are discarded.
+ *
+ * If the offset is negative, the contents are shifted to the left where the
+ * first \p shift bytes are discarded.
+ * The new size of the buffer is the old size minus the absolute shift value.
+ * If this value is larger than the buffer size, the buffer is emptied (but
+ * not cleared, see the security note below).
+ *
+ * The buffer position gets shifted alongside with the content but is kept
+ * within the boundaries of the buffer.
+ *
+ * \note For situations where \c off_t is not large enough, there are specialized cxBufferShiftLeft() and
+ * cxBufferShiftRight() functions using a \c size_t as parameter type.
+ *
+ * \attention
+ * Security Note: The shifting operation does \em not erase the previously occupied memory cells.
+ * But you can easily do that manually, e.g. by calling
+ * <code>memset(buffer->bytes, 0, shift)</code> for a right shift or
+ * <code>memset(buffer->bytes + buffer->size, 0, buffer->capacity - buffer->size)</code>
+ * for a left shift.
+ *
+ * @param buffer the buffer
+ * @param shift the shift offset (negative means left shift)
+ * @return 0 on success, non-zero if a required auto-extension fails
+ */
+__attribute__((__nonnull__))
+int cxBufferShift(
+ CxBuffer *buffer,
+ off_t shift
+);
+
+/**
+ * Shifts the buffer to the right.
+ * See cxBufferShift() for details.
+ *
+ * @param buffer the buffer
+ * @param shift the shift offset
+ * @return 0 on success, non-zero if a required auto-extension fails
+ * @see cxBufferShift()
+ */
+__attribute__((__nonnull__))
+int cxBufferShiftRight(
+ CxBuffer *buffer,
+ size_t shift
+);
+
+/**
+ * Shifts the buffer to the left.
+ * See cxBufferShift() for details.
+ *
+ * \note Since a left shift cannot fail due to memory allocation problems, this
+ * function always returns zero.
+ *
+ * @param buffer the buffer
+ * @param shift the positive shift offset
+ * @return always zero
+ * @see cxBufferShift()
+ */
+__attribute__((__nonnull__))
+int cxBufferShiftLeft(
+ CxBuffer *buffer,
+ size_t shift
+);
+
+
+/**
+ * Moves the position of the buffer.
+ *
+ * The new position is relative to the \p whence argument.
+ *
+ * \li \c SEEK_SET marks the start of the buffer.
+ * \li \c SEEK_CUR marks the current position.
+ * \li \c SEEK_END marks the end of the buffer.
+ *
+ * With an offset of zero, this function sets the buffer position to zero
+ * (\c SEEK_SET), the buffer size (\c SEEK_END) or leaves the buffer position
+ * unchanged (\c SEEK_CUR).
+ *
+ * @param buffer the buffer
+ * @param offset position offset relative to \p whence
+ * @param whence one of \c SEEK_SET, \c SEEK_CUR or \c SEEK_END
+ * @return 0 on success, non-zero if the position is invalid
+ *
+ */
+__attribute__((__nonnull__))
+int cxBufferSeek(
+ CxBuffer *buffer,
+ off_t offset,
+ int whence
+);
+
+/**
+ * Clears the buffer by resetting the position and deleting the data.
+ *
+ * The data is deleted by zeroing it with a call to memset().
+ *
+ * @param buffer the buffer to be cleared
+ */
+__attribute__((__nonnull__))
+void cxBufferClear(CxBuffer *buffer);
+
+/**
+ * Tests, if the buffer position has exceeded the buffer capacity.
+ *
+ * @param buffer the buffer to test
+ * @return non-zero, if the current buffer position has exceeded the last
+ * available byte of the buffer.
+ */
+__attribute__((__nonnull__))
+int cxBufferEof(CxBuffer const *buffer);
+
+
+/**
+ * Ensures that the buffer has a minimum capacity.
+ *
+ * If the current capacity is not sufficient, the buffer will be extended.
+ *
+ * @param buffer the buffer
+ * @param capacity the minimum required capacity for this buffer
+ * @return 0 on success or a non-zero value on failure
+ */
+__attribute__((__nonnull__))
+int cxBufferMinimumCapacity(
+ CxBuffer *buffer,
+ size_t capacity
+);
+
+/**
+ * Writes data to a CxBuffer.
+ *
+ * If flushing is enabled and the buffer needs to flush, the data is flushed to
+ * the target until the target signals that it cannot take more data by
+ * returning zero via the respective write function. In that case, the remaining
+ * data in this buffer is shifted to the beginning of this buffer so that the
+ * newly available space can be used to append as much data as possible. This
+ * function only stops writing more elements, when the flush target and this
+ * buffer are both incapable of taking more data or all data has been written.
+ * The number returned by this function is the total number of elements that
+ * could be written during the process. It does not necessarily mean that those
+ * elements are still in this buffer, because some of them could have also be
+ * flushed already.
+ *
+ * If automatic flushing is not enabled, the position of the buffer is increased
+ * by the number of bytes written.
+ *
+ * \note The signature is compatible with the fwrite() family of functions.
+ *
+ * @param ptr a pointer to the memory area containing the bytes to be written
+ * @param size the length of one element
+ * @param nitems the element count
+ * @param buffer the CxBuffer to write to
+ * @return the total count of elements written
+ */
+__attribute__((__nonnull__))
+size_t cxBufferWrite(
+ void const *ptr,
+ size_t size,
+ size_t nitems,
+ CxBuffer *buffer
+);
+
+/**
+ * Reads data from a CxBuffer.
+ *
+ * The position of the buffer is increased by the number of bytes read.
+ *
+ * \note The signature is compatible with the fread() family of functions.
+ *
+ * @param ptr a pointer to the memory area where to store the read data
+ * @param size the length of one element
+ * @param nitems the element count
+ * @param buffer the CxBuffer to read from
+ * @return the total number of elements read
+ */
+__attribute__((__nonnull__))
+size_t cxBufferRead(
+ void *ptr,
+ size_t size,
+ size_t nitems,
+ CxBuffer *buffer
+);
+
+/**
+ * Writes a character to a buffer.
+ *
+ * The least significant byte of the argument is written to the buffer. If the
+ * end of the buffer is reached and #CX_BUFFER_AUTO_EXTEND feature is enabled,
+ * the buffer capacity is extended by cxBufferMinimumCapacity(). If the feature is
+ * disabled or buffer extension fails, \c EOF is returned.
+ *
+ * On successful write, the position of the buffer is increased.
+ *
+ * @param buffer the buffer to write to
+ * @param c the character to write
+ * @return the byte that has bean written or \c EOF when the end of the stream is
+ * reached and automatic extension is not enabled or not possible
+ */
+__attribute__((__nonnull__))
+int cxBufferPut(
+ CxBuffer *buffer,
+ int c
+);
+
+/**
+ * Writes a string to a buffer.
+ *
+ * @param buffer the buffer
+ * @param str the zero-terminated string
+ * @return the number of bytes written
+ */
+__attribute__((__nonnull__))
+size_t cxBufferPutString(
+ CxBuffer *buffer,
+ const char *str
+);
+
+/**
+ * Gets a character from a buffer.
+ *
+ * The current position of the buffer is increased after a successful read.
+ *
+ * @param buffer the buffer to read from
+ * @return the character or \c EOF, if the end of the buffer is reached
+ */
+__attribute__((__nonnull__))
+int cxBufferGet(CxBuffer *buffer);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // UCX_BUFFER_H
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file common.h
+ *
+ * \brief Common definitions and feature checks.
+ *
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ *
+ * \mainpage UAP Common Extensions
+ * Library with common and useful functions, macros and data structures.
+ * <p>
+ * Latest available source:<br>
+ * <a href="https://sourceforge.net/projects/ucx/files/">https://sourceforge.net/projects/ucx/files/</a>
+ * </p>
+ *
+ * <p>
+ * Repositories:<br>
+ * <a href="https://sourceforge.net/p/ucx/code">https://sourceforge.net/p/ucx/code</a>
+ * - or -
+ * <a href="https://develop.uap-core.de/hg/ucx">https://develop.uap-core.de/hg/ucx</a>
+ * </p>
+ *
+ * <h2>LICENCE</h2>
+ *
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UCX_COMMON_H
+#define UCX_COMMON_H
+
+/** Major UCX version as integer constant. */
+#define UCX_VERSION_MAJOR 3
+
+/** Minor UCX version as integer constant. */
+#define UCX_VERSION_MINOR 0
+
+/** Version constant which ensures to increase monotonically. */
+#define UCX_VERSION (((UCX_VERSION_MAJOR)<<16)|UCX_VERSION_MINOR)
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+/**
+ * Function pointer compatible with fwrite-like functions.
+ */
+typedef size_t (*cx_write_func)(
+ void const *,
+ size_t,
+ size_t,
+ void *
+);
+
+/**
+ * Function pointer compatible with fread-like functions.
+ */
+typedef size_t (*cx_read_func)(
+ void *,
+ size_t,
+ size_t,
+ void *
+);
+
+#ifdef _WIN32
+
+#ifdef __MINGW32__
+#include <sys/types.h>
+#endif // __MINGW32__
+
+#else // !_WIN32
+
+#include <sys/types.h>
+
+#endif // _WIN32
+
+#ifndef __GNUC__
+/**
+ * Removes GNU C attributes where they are not supported.
+ */
+#define __attribute__(x)
+#endif
+
+#endif // UCX_COMMON_H
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * \file compare.h
+ * \brief A collection of simple compare functions.
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+#ifndef UCX_COMPARE_H
+#define UCX_COMPARE_H
+
+#include "common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Compares two integers of type int.
+ *
+ * @param i1 pointer to integer one
+ * @param i2 pointer to integer two
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int cx_cmp_int(void const *i1, void const *i2);
+
+/**
+ * Compares two integers of type long int.
+ *
+ * @param i1 pointer to long integer one
+ * @param i2 pointer to long integer two
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int cx_cmp_longint(void const *i1, void const *i2);
+
+/**
+ * Compares two integers of type long long.
+ *
+ * @param i1 pointer to long long one
+ * @param i2 pointer to long long two
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int cx_cmp_longlong(void const *i1, void const *i2);
+
+/**
+ * Compares two integers of type int16_t.
+ *
+ * @param i1 pointer to int16_t one
+ * @param i2 pointer to int16_t two
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int cx_cmp_int16(void const *i1, void const *i2);
+
+/**
+ * Compares two integers of type int32_t.
+ *
+ * @param i1 pointer to int32_t one
+ * @param i2 pointer to int32_t two
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int cx_cmp_int32(void const *i1, void const *i2);
+
+/**
+ * Compares two integers of type int64_t.
+ *
+ * @param i1 pointer to int64_t one
+ * @param i2 pointer to int64_t two
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int cx_cmp_int64(void const *i1, void const *i2);
+
+/**
+ * Compares two integers of type unsigned int.
+ *
+ * @param i1 pointer to unsigned integer one
+ * @param i2 pointer to unsigned integer two
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int cx_cmp_uint(void const *i1, void const *i2);
+
+/**
+ * Compares two integers of type unsigned long int.
+ *
+ * @param i1 pointer to unsigned long integer one
+ * @param i2 pointer to unsigned long integer two
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int cx_cmp_ulongint(void const *i1, void const *i2);
+
+/**
+ * Compares two integers of type unsigned long long.
+ *
+ * @param i1 pointer to unsigned long long one
+ * @param i2 pointer to unsigned long long two
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int cx_cmp_ulonglong(void const *i1, void const *i2);
+
+/**
+ * Compares two integers of type uint16_t.
+ *
+ * @param i1 pointer to uint16_t one
+ * @param i2 pointer to uint16_t two
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int cx_cmp_uint16(void const *i1, void const *i2);
+
+/**
+ * Compares two integers of type uint32_t.
+ *
+ * @param i1 pointer to uint32_t one
+ * @param i2 pointer to uint32_t two
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int cx_cmp_uint32(void const *i1, void const *i2);
+
+/**
+ * Compares two integers of type uint64_t.
+ *
+ * @param i1 pointer to uint64_t one
+ * @param i2 pointer to uint64_t two
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int cx_cmp_uint64(void const *i1, void const *i2);
+
+/**
+ * Compares two real numbers of type float with precision 1e-6f.
+ *
+ * @param f1 pointer to float one
+ * @param f2 pointer to float two
+ * @return -1, if *f1 is less than *f2, 0 if both are equal,
+ * 1 if *f1 is greater than *f2
+ */
+
+int cx_cmp_float(void const *f1, void const *f2);
+
+/**
+ * Compares two real numbers of type double with precision 1e-14.
+ *
+ * @param d1 pointer to double one
+ * @param d2 pointer to double two
+ * @return -1, if *d1 is less than *d2, 0 if both are equal,
+ * 1 if *d1 is greater than *d2
+ */
+int cx_cmp_double(
+ void const *d1,
+ void const *d2
+);
+
+/**
+ * Compares the integer representation of two pointers.
+ *
+ * @param ptr1 pointer to pointer one (intptr_t const*)
+ * @param ptr2 pointer to pointer two (intptr_t const*)
+ * @return -1 if *ptr1 is less than *ptr2, 0 if both are equal,
+ * 1 if *ptr1 is greater than *ptr2
+ */
+int cx_cmp_intptr(
+ void const *ptr1,
+ void const *ptr2
+);
+
+/**
+ * Compares the unsigned integer representation of two pointers.
+ *
+ * @param ptr1 pointer to pointer one (uintptr_t const*)
+ * @param ptr2 pointer to pointer two (uintptr_t const*)
+ * @return -1 if *ptr1 is less than *ptr2, 0 if both are equal,
+ * 1 if *ptr1 is greater than *ptr2
+ */
+int cx_cmp_uintptr(
+ void const *ptr1,
+ void const *ptr2
+);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif //UCX_COMPARE_H
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * \file hash_key.h
+ * \brief Interface for map implementations.
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+
+#ifndef UCX_HASH_KEY_H
+#define UCX_HASH_KEY_H
+
+#include "stddef.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Internal structure for a key within a hash map. */
+struct cx_hash_key_s {
+ /** The key data. */
+ union {
+ unsigned char *bytes;
+ unsigned char const *cbytes;
+ char *str;
+ char const *cstr;
+ void *obj;
+ void const *cobj;
+ } data;
+ /**
+ * The key data length.
+ */
+ size_t len;
+ /** The hash value of the key data. */
+ unsigned hash;
+};
+
+/**
+ * Type for a hash key.
+ */
+typedef struct cx_hash_key_s CxHashKey;
+
+/**
+ * Computes a murmur2 32 bit hash.
+ *
+ * You need to initialize \c data and \c len in the key struct.
+ * The hash is then directly written to that struct.
+ *
+ * \note If \c data is \c NULL, the hash is defined as 1574210520.
+ *
+ * @param key the key, the hash shall be computed for
+ */
+void cx_hash_murmur(CxHashKey *key);
+
+/**
+ * Computes a hash key from a string.
+ *
+ * The string needs to be zero-terminated.
+ *
+ * @param str the string
+ * @return the hash key
+ */
+__attribute__((__warn_unused_result__))
+CxHashKey cx_hash_key_str(char const *str);
+
+/**
+ * Computes a hash key from a byte array.
+ *
+ * @param bytes the array
+ * @param len the length
+ * @return the hash key
+ */
+__attribute__((__warn_unused_result__))
+CxHashKey cx_hash_key_bytes(
+ unsigned char const *bytes,
+ size_t len
+);
+
+/**
+ * Computes a hash key for an arbitrary object.
+ *
+ * The computation uses the in-memory representation that might not be
+ * the same on different platforms. Therefore, this hash should not be
+ * used for data exchange with different machines.
+ *
+ * @param obj a pointer to an arbitrary object
+ * @param len the length of object in memory
+ * @return the hash key
+ */
+__attribute__((__warn_unused_result__))
+CxHashKey cx_hash_key(
+ void const *obj,
+ size_t len
+);
+
+/**
+ * Computes a hash key from a UCX string.
+ *
+ * @param str the string
+ * @return the hash key
+ */
+#define cx_hash_key_cxstr(str) cx_hash_key((void*)(str).ptr, (str).length)
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // UCX_HASH_KEY_H
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * \file hash_map.h
+ * \brief Hash map implementation.
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+#ifndef UCX_HASH_MAP_H
+#define UCX_HASH_MAP_H
+
+#include "map.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Internal structure for an element of a hash map. */
+struct cx_hash_map_element_s;
+
+/**
+ * Internal structure for a hash map.
+ */
+struct cx_hash_map_s {
+ /**
+ * Base structure for maps.
+ */
+ struct cx_map_s base;
+ /**
+ * The buckets of this map, each containing a linked list of elements.
+ */
+ struct cx_hash_map_element_s **buckets;
+ /**
+ * The number of buckets.
+ */
+ size_t bucket_count;
+};
+
+
+/**
+ * Creates a new hash map with the specified number of buckets.
+ *
+ * If \p buckets is zero, an implementation defined default will be used.
+ *
+ * If \p itemsize is CX_STORE_POINTERS, the created map will be created as if
+ * cxMapStorePointers() was called immediately after creation.
+ *
+ * @note Iterators provided by this hash map implementation provide the remove operation.
+ * The index value of an iterator is the incremented when the iterator advanced without removal.
+ * In other words, when the iterator is finished, \c index==size .
+ *
+ * @param allocator the allocator to use
+ * @param itemsize the size of one element
+ * @param buckets the initial number of buckets in this hash map
+ * @return a pointer to the new hash map
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+CxMap *cxHashMapCreate(
+ CxAllocator *allocator,
+ size_t itemsize,
+ size_t buckets
+);
+
+/**
+ * Increases the number of buckets, if necessary.
+ *
+ * The load threshold is \c 0.75*buckets. If the element count exceeds the load
+ * threshold, the map will be rehashed. Otherwise, no action is performed and
+ * this function simply returns 0.
+ *
+ * The rehashing process ensures, that the number of buckets is at least
+ * 2.5 times the element count. So there is enough room for additional
+ * elements without the need of another soon rehashing.
+ *
+ * You can use this function after filling a map to increase access performance.
+ *
+ * @note If the specified map is not a hash map, the behavior is undefined.
+ *
+ * @param map the map to rehash
+ * @return zero on success, non-zero if a memory allocation error occurred
+ */
+__attribute__((__nonnull__))
+int cxMapRehash(CxMap *map);
+
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // UCX_HASH_MAP_H
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * \file iterator.h
+ * \brief Interface for iterator implementations.
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+#ifndef UCX_ITERATOR_H
+#define UCX_ITERATOR_H
+
+#include "common.h"
+
+/**
+ * The base of mutating and non-mutating iterators.
+ */
+struct cx_iterator_base_s {
+ /**
+ * True iff the iterator points to valid data.
+ */
+ __attribute__ ((__nonnull__))
+ bool (*valid)(void const *);
+
+ /**
+ * Returns a pointer to the current element.
+ */
+ __attribute__ ((__nonnull__))
+ void *(*current)(void const *);
+
+ /**
+ * Original implementation in case the function needs to be wrapped.
+ */
+ __attribute__ ((__nonnull__))
+ void *(*current_impl)(void const *);
+
+ /**
+ * Advances the iterator.
+ */
+ __attribute__ ((__nonnull__))
+ void (*next)(void *);
+
+ /**
+ * Flag current element for removal, if possible.
+ */
+ __attribute__ ((__nonnull__))
+ bool (*flag_removal)(void *);
+
+ /**
+ * Indicates whether this iterator is muting.
+ */
+ bool mutating;
+
+ /**
+ * Internal flag for removing the current element when advancing.
+ */
+ bool remove;
+};
+
+/**
+ * Internal iterator struct - use CxMutIterator.
+ */
+struct cx_mut_iterator_s {
+
+ /**
+ * The base properties of this iterator.
+ */
+ struct cx_iterator_base_s base;
+
+ /**
+ * Handle for the current element, if required.
+ */
+ void *elem_handle;
+
+ /**
+ * Handle for the source collection, if any.
+ */
+ void *src_handle;
+
+ /**
+ * Field for storing a key-value pair.
+ * May be used by iterators that iterate over k/v-collections.
+ */
+ struct {
+ /**
+ * A pointer to the key.
+ */
+ void const *key;
+ /**
+ * A pointer to the value.
+ */
+ void *value;
+ } kv_data;
+
+ /**
+ * Field for storing a slot number.
+ * May be used by iterators that iterate over multi-bucket collections.
+ */
+ size_t slot;
+
+ /**
+ * If the iterator is position-aware, contains the index of the element in the underlying collection.
+ * Otherwise, this field is usually uninitialized.
+ */
+ size_t index;
+};
+
+/**
+ * Mutating iterator value type.
+ *
+ * An iterator points to a certain element in an (possibly unbounded) chain of elements.
+ * Iterators that are based on collections (which have a defined "first" element), are supposed
+ * to be "position-aware", which means that they keep track of the current index within the collection.
+ *
+ * @note Objects that are pointed to by an iterator are mutable through that iterator. However, if the
+ * iterator is based on a collection and the underlying collection is mutated by other means than this iterator
+ * (e.g. elements added or removed), the iterator becomes invalid (regardless of what cxIteratorValid() returns)
+ * and MUST be re-obtained from the collection.
+ *
+ * @see CxIterator
+ */
+typedef struct cx_mut_iterator_s CxMutIterator;
+
+/**
+ * Internal iterator struct - use CxIterator.
+ */
+struct cx_iterator_s {
+
+ /**
+ * The base properties of this iterator.
+ */
+ struct cx_iterator_base_s base;
+
+ /**
+ * Handle for the current element, if required.
+ */
+ void *elem_handle;
+
+ /**
+ * Handle for the source collection, if any.
+ */
+ void const *src_handle;
+
+ /**
+ * Field for storing a key-value pair.
+ * May be used by iterators that iterate over k/v-collections.
+ */
+ struct {
+ /**
+ * A pointer to the key.
+ */
+ void const *key;
+ /**
+ * A pointer to the value.
+ */
+ void *value;
+ } kv_data;
+
+ /**
+ * Field for storing a slot number.
+ * May be used by iterators that iterate over multi-bucket collections.
+ */
+ size_t slot;
+
+ /**
+ * If the iterator is position-aware, contains the index of the element in the underlying collection.
+ * Otherwise, this field is usually uninitialized.
+ */
+ size_t index;
+};
+
+/**
+ * Iterator value type.
+ * An iterator points to a certain element in an (possibly unbounded) chain of elements.
+ * Iterators that are based on collections (which have a defined "first" element), are supposed
+ * to be "position-aware", which means that they keep track of the current index within the collection.
+ *
+ * @note Objects that are pointed to by an iterator are always mutable through that iterator. However,
+ * this iterator cannot mutate the collection itself (add or remove elements) and any mutation of the
+ * collection by other means make this iterator invalid (regardless of what cxIteratorValid() returns).
+ *
+ * @see CxMutIterator
+ */
+typedef struct cx_iterator_s CxIterator;
+
+/**
+ * Checks if the iterator points to valid data.
+ *
+ * This is especially false for past-the-end iterators.
+ *
+ * @param iter the iterator
+ * @return true iff the iterator points to valid data
+ */
+#define cxIteratorValid(iter) (iter).base.valid(&(iter))
+
+/**
+ * Returns a pointer to the current element.
+ *
+ * The behavior is undefined if this iterator is invalid.
+ *
+ * @param iter the iterator
+ * @return a pointer to the current element
+ */
+#define cxIteratorCurrent(iter) (iter).base.current(&iter)
+
+/**
+ * Advances the iterator to the next element.
+ *
+ * @param iter the iterator
+ */
+#define cxIteratorNext(iter) (iter).base.next(&iter)
+
+/**
+ * Flags the current element for removal.
+ *
+ * @param iter the iterator
+ * @return false if this iterator cannot remove the element
+ */
+#define cxIteratorFlagRemoval(iter) (iter).base.flag_removal(&iter)
+
+/**
+ * Loops over an iterator.
+ * @param type the type of the elements
+ * @param elem the name of the iteration variable
+ * @param iter the iterator
+ */
+#define cx_foreach(type, elem, iter) \
+for (type elem; cxIteratorValid(iter) && (elem = (type)cxIteratorCurrent(iter)) != NULL ; cxIteratorNext(iter))
+
+#endif // UCX_ITERATOR_H
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * \file linked_list.h
+ * \brief Linked list implementation.
+ * \details Also provides several low-level functions for custom linked list implementations.
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+#ifndef UCX_LINKED_LIST_H
+#define UCX_LINKED_LIST_H
+
+#include "common.h"
+#include "list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Set this flag to true, if you want to disable the use of SBO for
+ * linked list swap operations.
+ */
+extern bool CX_DISABLE_LINKED_LIST_SWAP_SBO;
+
+/**
+ * Allocates a linked list for storing elements with \p item_size bytes each.
+ *
+ * If \p item_size is CX_STORE_POINTERS, the created list will be created as if
+ * cxListStorePointers() was called immediately after creation.
+ *
+ * @param allocator the allocator for allocating the list nodes
+ * (if \c NULL the cxDefaultAllocator will be used)
+ * @param comparator the comparator for the elements
+ * (if \c NULL sort and find functions will not work)
+ * @param item_size the size of each element in bytes
+ * @return the created list
+ */
+CxList *cxLinkedListCreate(
+ CxAllocator const *allocator,
+ CxListComparator comparator,
+ size_t item_size
+);
+
+/**
+ * Allocates a linked list for storing elements with \p item_size bytes each.
+ *
+ * The list will use cxDefaultAllocator and no comparator function. If you want
+ * to call functions that need a comparator, you must either set one immediately
+ * after list creation or use cxLinkedListCreate().
+ *
+ * If \p item_size is CX_STORE_POINTERS, the created list will be created as if
+ * cxListStorePointers() was called immediately after creation.
+ *
+ * @param item_size the size of each element in bytes
+ * @return the created list
+ */
+#define cxLinkedListCreateSimple(item_size) \
+ cxLinkedListCreate(NULL, NULL, item_size)
+
+/**
+ * Finds the node at a certain index.
+ *
+ * This function can be used to start at an arbitrary position within the list.
+ * If the search index is large than the start index, \p loc_advance must denote
+ * the location of some sort of \c next pointer (i.e. a pointer to the next node).
+ * But it is also possible that the search index is smaller than the start index
+ * (e.g. in cases where traversing a list backwards is faster) in which case
+ * \p loc_advance must denote the location of some sort of \c prev pointer
+ * (i.e. a pointer to the previous node).
+ *
+ * @param start a pointer to the start node
+ * @param start_index the start index
+ * @param loc_advance the location of the pointer to advance
+ * @param index the search index
+ * @return the node found at the specified index
+ */
+void *cx_linked_list_at(
+ void const *start,
+ size_t start_index,
+ ptrdiff_t loc_advance,
+ size_t index
+) __attribute__((__nonnull__));
+
+/**
+ * Finds the index of an element within a linked list.
+ *
+ * @param start a pointer to the start node
+ * @param loc_advance the location of the pointer to advance
+ * @param loc_data the location of the \c data pointer within your node struct
+ * @param cmp_func a compare function to compare \p elem against the node data
+ * @param elem a pointer to the element to find
+ * @return the index of the element or a past-one index if the element could not be found
+ */
+size_t cx_linked_list_find(
+ void const *start,
+ ptrdiff_t loc_advance,
+ ptrdiff_t loc_data,
+ CxListComparator cmp_func,
+ void const *elem
+) __attribute__((__nonnull__));
+
+/**
+ * Finds the first node in a linked list.
+ *
+ * The function starts with the pointer denoted by \p node and traverses the list
+ * along a prev pointer whose location within the node struct is
+ * denoted by \p loc_prev.
+ *
+ * @param node a pointer to a node in the list
+ * @param loc_prev the location of the \c prev pointer
+ * @return a pointer to the first node
+ */
+void *cx_linked_list_first(
+ void const *node,
+ ptrdiff_t loc_prev
+) __attribute__((__nonnull__));
+
+/**
+ * Finds the last node in a linked list.
+ *
+ * The function starts with the pointer denoted by \p node and traverses the list
+ * along a next pointer whose location within the node struct is
+ * denoted by \p loc_next.
+ *
+ * @param node a pointer to a node in the list
+ * @param loc_next the location of the \c next pointer
+ * @return a pointer to the last node
+ */
+void *cx_linked_list_last(
+ void const *node,
+ ptrdiff_t loc_next
+) __attribute__((__nonnull__));
+
+/**
+ * Finds the predecessor of a node in case it is not linked.
+ *
+ * \remark If \p node is not contained in the list starting with \p begin, the behavior is undefined.
+ *
+ * @param begin the node where to start the search
+ * @param loc_next the location of the \c next pointer
+ * @param node the successor of the node to find
+ * @return the node or \c NULL if \p node has no predecessor
+ */
+void *cx_linked_list_prev(
+ void const *begin,
+ ptrdiff_t loc_next,
+ void const *node
+) __attribute__((__nonnull__));
+
+/**
+ * Adds a new node to a linked list.
+ * The node must not be part of any list already.
+ *
+ * \remark One of the pointers \p begin or \p end may be \c NULL, but not both.
+ *
+ * @param begin a pointer to the begin node pointer (if your list has one)
+ * @param end a pointer to the end node pointer (if your list has one)
+ * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one)
+ * @param loc_next the location of a \c next pointer within your node struct (required)
+ * @param new_node a pointer to the node that shall be appended
+ */
+void cx_linked_list_add(
+ void **begin,
+ void **end,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next,
+ void *new_node
+) __attribute__((__nonnull__(5)));
+
+/**
+ * Prepends a new node to a linked list.
+ * The node must not be part of any list already.
+ *
+ * \remark One of the pointers \p begin or \p end may be \c NULL, but not both.
+ *
+ * @param begin a pointer to the begin node pointer (if your list has one)
+ * @param end a pointer to the end node pointer (if your list has one)
+ * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one)
+ * @param loc_next the location of a \c next pointer within your node struct (required)
+ * @param new_node a pointer to the node that shall be prepended
+ */
+void cx_linked_list_prepend(
+ void **begin,
+ void **end,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next,
+ void *new_node
+) __attribute__((__nonnull__(5)));
+
+/**
+ * Links two nodes.
+ *
+ * @param left the new predecessor of \p right
+ * @param right the new successor of \p left
+ * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one)
+ * @param loc_next the location of a \c next pointer within your node struct (required)
+ */
+void cx_linked_list_link(
+ void *left,
+ void *right,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next
+) __attribute__((__nonnull__));
+
+/**
+ * Unlinks two nodes.
+ *
+ * If right is not the successor of left, the behavior is undefined.
+ *
+ * @param left the predecessor of \p right
+ * @param right the successor of \p left
+ * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one)
+ * @param loc_next the location of a \c next pointer within your node struct (required)
+ */
+void cx_linked_list_unlink(
+ void *left,
+ void *right,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next
+) __attribute__((__nonnull__));
+
+/**
+ * Inserts a new node after a given node of a linked list.
+ * The new node must not be part of any list already.
+ *
+ * \note If you specify \c NULL as the \p node to insert after, this function needs either the \p begin or
+ * the \p end pointer to determine the start of the list. Then the new node will be prepended to the list.
+ *
+ * @param begin a pointer to the begin node pointer (if your list has one)
+ * @param end a pointer to the end node pointer (if your list has one)
+ * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one)
+ * @param loc_next the location of a \c next pointer within your node struct (required)
+ * @param node the node after which to insert (\c NULL if you want to prepend the node to the list)
+ * @param new_node a pointer to the node that shall be prepended
+ */
+void cx_linked_list_insert(
+ void **begin,
+ void **end,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next,
+ void *node,
+ void *new_node
+) __attribute__((__nonnull__(6)));
+
+/**
+ * Inserts a chain of nodes after a given node of a linked list.
+ * The chain must not be part of any list already.
+ *
+ * If you do not explicitly specify the end of the chain, it will be determined by traversing
+ * the \c next pointer.
+ *
+ * \note If you specify \c NULL as the \p node to insert after, this function needs either the \p begin or
+ * the \p end pointer to determine the start of the list. If only the \p end pointer is specified, you also need
+ * to provide a valid \p loc_prev location.
+ * Then the chain will be prepended to the list.
+ *
+ * @param begin a pointer to the begin node pointer (if your list has one)
+ * @param end a pointer to the end node pointer (if your list has one)
+ * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one)
+ * @param loc_next the location of a \c next pointer within your node struct (required)
+ * @param node the node after which to insert (\c NULL to prepend the chain to the list)
+ * @param insert_begin a pointer to the first node of the chain that shall be inserted
+ * @param insert_end a pointer to the last node of the chain (or NULL if the last node shall be determined)
+ */
+void cx_linked_list_insert_chain(
+ void **begin,
+ void **end,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next,
+ void *node,
+ void *insert_begin,
+ void *insert_end
+) __attribute__((__nonnull__(6)));
+
+/**
+ * Removes a node from the linked list.
+ *
+ * If the node to remove is the begin (resp. end) node of the list and if \p begin (resp. \p end)
+ * addresses are provided, the pointers are adjusted accordingly.
+ *
+ * The following combinations of arguments are valid (more arguments are optional):
+ * \li \p loc_next and \p loc_prev (ancestor node is determined by using the prev pointer, overall O(1) performance)
+ * \li \p loc_next and \p begin (ancestor node is determined by list traversal, overall O(n) performance)
+ *
+ * \remark The \c next and \c prev pointers of the removed node are not cleared by this function and may still be used
+ * to traverse to a former adjacent node in the list.
+ *
+ * @param begin a pointer to the begin node pointer (optional)
+ * @param end a pointer to the end node pointer (optional)
+ * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one)
+ * @param loc_next the location of a \c next pointer within your node struct (required)
+ * @param node the node to remove
+ */
+void cx_linked_list_remove(
+ void **begin,
+ void **end,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next,
+ void *node
+) __attribute__((__nonnull__(5)));
+
+
+/**
+ * Determines the size of a linked list starting with \p node.
+ * @param node the first node
+ * @param loc_next the location of the \c next pointer within the node struct
+ * @return the size of the list or zero if \p node is \c NULL
+ */
+size_t cx_linked_list_size(
+ void const *node,
+ ptrdiff_t loc_next
+);
+
+/**
+ * Sorts a linked list based on a comparison function.
+ *
+ * This function can work with linked lists of the following structure:
+ * \code
+ * typedef struct node node;
+ * struct node {
+ * node* prev;
+ * node* next;
+ * my_payload data;
+ * }
+ * \endcode
+ *
+ * @note This is a recursive function with at most logarithmic recursion depth.
+ *
+ * @param begin a pointer to the begin node pointer (required)
+ * @param end a pointer to the end node pointer (optional)
+ * @param loc_prev the location of a \c prev pointer within your node struct (negative if not present)
+ * @param loc_next the location of a \c next pointer within your node struct (required)
+ * @param loc_data the location of the \c data pointer within your node struct
+ * @param cmp_func the compare function defining the sort order
+ */
+void cx_linked_list_sort(
+ void **begin,
+ void **end,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next,
+ ptrdiff_t loc_data,
+ CxListComparator cmp_func
+) __attribute__((__nonnull__(1, 6)));
+
+
+/**
+ * Compares two lists element wise.
+ *
+ * \note Both list must have the same structure.
+ *
+ * @param begin_left the begin of the left list (\c NULL denotes an empty list)
+ * @param begin_right the begin of the right list (\c NULL denotes an empty list)
+ * @param loc_advance the location of the pointer to advance
+ * @param loc_data the location of the \c data pointer within your node struct
+ * @param cmp_func the function to compare the elements
+ * @return the first non-zero result of invoking \p cmp_func or: negative if the left list is smaller than the
+ * right list, positive if the left list is larger than the right list, zero if both lists are equal.
+ */
+int cx_linked_list_compare(
+ void const *begin_left,
+ void const *begin_right,
+ ptrdiff_t loc_advance,
+ ptrdiff_t loc_data,
+ CxListComparator cmp_func
+) __attribute__((__nonnull__(5)));
+
+/**
+ * Reverses the order of the nodes in a linked list.
+ *
+ * @param begin a pointer to the begin node pointer (required)
+ * @param end a pointer to the end node pointer (optional)
+ * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one)
+ * @param loc_next the location of a \c next pointer within your node struct (required)
+ */
+void cx_linked_list_reverse(
+ void **begin,
+ void **end,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next
+) __attribute__((__nonnull__(1)));
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // UCX_LINKED_LIST_H
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * \file list.h
+ * \brief Interface for list implementations.
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+#ifndef UCX_LIST_H
+#define UCX_LIST_H
+
+#include "common.h"
+#include "allocator.h"
+#include "iterator.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef CX_STORE_POINTERS
+/**
+ * Special constant used for creating collections that are storing pointers.
+ */
+#define CX_STORE_POINTERS 0
+#endif
+
+/**
+ * A comparator function comparing two list elements.
+ */
+typedef int(*CxListComparator)(
+ void const *left,
+ void const *right
+);
+
+/**
+ * List class type.
+ */
+typedef struct cx_list_class_s cx_list_class;
+
+/**
+ * Structure for holding the base data of a list.
+ */
+struct cx_list_s {
+ /**
+ * The list class definition.
+ */
+ cx_list_class const *cl;
+ /**
+ * The actual implementation in case the list class is delegating.
+ */
+ cx_list_class const *climpl;
+ /**
+ * The allocator to use.
+ */
+ CxAllocator const *allocator;
+ /**
+ * The comparator function for the elements.
+ */
+ CxListComparator cmpfunc;
+ /**
+ * The size of each element (payload only).
+ */
+ size_t itemsize;
+ /**
+ * The size of the list (number of currently stored elements).
+ */
+ size_t size;
+ /**
+ * The capacity of the list (maximum number of elements).
+ */
+ size_t capacity;
+ union {
+ /**
+ * An optional simple destructor for the list contents that admits the free() interface.
+ *
+ * @remark Set content_destructor_type to #CX_DESTRUCTOR_SIMPLE.
+ *
+ * @attention Read the documentation of the particular list implementation
+ * whether this destructor shall only destroy the contents or also free the memory.
+ */
+ cx_destructor_func simple_destructor;
+ /**
+ * An optional advanced destructor for the list contents providing additional data.
+ *
+ * @remark Set content_destructor_type to #CX_DESTRUCTOR_ADVANCED.
+ *
+ * @attention Read the documentation of the particular list implementation
+ * whether this destructor shall only destroy the contents or also free the memory.
+ */
+ cx_advanced_destructor advanced_destructor;
+ };
+ /**
+ * The type of destructor to use.
+ */
+ enum cx_destructor_type content_destructor_type;
+};
+
+/**
+ * The class definition for arbitrary lists.
+ */
+struct cx_list_class_s {
+ /**
+ * Destructor function.
+ */
+ void (*destructor)(struct cx_list_s *list);
+
+ /**
+ * Member function for inserting a single elements.
+ * Implementors SHOULD see to performant implementations for corner cases.
+ */
+ int (*insert_element)(
+ struct cx_list_s *list,
+ size_t index,
+ void const *data
+ );
+
+ /**
+ * Member function for inserting multiple elements.
+ * Implementors SHOULD see to performant implementations for corner cases.
+ */
+ size_t (*insert_array)(
+ struct cx_list_s *list,
+ size_t index,
+ void const *data,
+ size_t n
+ );
+
+ /**
+ * Member function for inserting an element relative to an iterator position.
+ */
+ int (*insert_iter)(
+ struct cx_mut_iterator_s *iter,
+ void const *elem,
+ int prepend
+ );
+
+ /**
+ * Member function for removing an element.
+ */
+ int (*remove)(
+ struct cx_list_s *list,
+ size_t index
+ );
+
+ /**
+ * Member function for removing all elements.
+ */
+ void (*clear)(struct cx_list_s *list);
+
+ /**
+ * Member function for swapping two elements.
+ */
+ int (*swap)(
+ struct cx_list_s *list,
+ size_t i,
+ size_t j
+ );
+
+ /**
+ * Member function for element lookup.
+ */
+ void *(*at)(
+ struct cx_list_s const *list,
+ size_t index
+ );
+
+ /**
+ * Member function for finding an element.
+ */
+ size_t (*find)(
+ struct cx_list_s const *list,
+ void const *elem
+ );
+
+ /**
+ * Member function for sorting the list in place.
+ */
+ void (*sort)(struct cx_list_s *list);
+
+ /**
+ * Member function for comparing this list to another list of the same type.
+ */
+ int (*compare)(
+ struct cx_list_s const *list,
+ struct cx_list_s const *other
+ );
+
+ /**
+ * Member function for reversing the order of the items.
+ */
+ void (*reverse)(struct cx_list_s *list);
+
+ /**
+ * Member function for returning an iterator pointing to the specified index.
+ */
+ struct cx_iterator_s (*iterator)(
+ struct cx_list_s const *list,
+ size_t index,
+ bool backward
+ );
+};
+
+/**
+ * Common type for all list implementations.
+ */
+typedef struct cx_list_s CxList;
+
+/**
+ * Invokes the configured destructor function for a specific element.
+ *
+ * Usually only used by list implementations. There should be no need
+ * to invoke this function manually.
+ *
+ * @param list the list
+ * @param elem the element
+ */
+__attribute__((__nonnull__))
+void cx_list_invoke_destructor(
+ struct cx_list_s const *list,
+ void *elem
+);
+
+/**
+ * Invokes the simple destructor function for a specific element.
+ *
+ * Usually only used by list implementations. There should be no need
+ * to invoke this function manually.
+ *
+ * @param list the list
+ * @param elem the element
+ */
+__attribute__((__nonnull__))
+void cx_list_invoke_simple_destructor(
+ struct cx_list_s const *list,
+ void *elem
+);
+
+/**
+ * Invokes the advanced destructor function for a specific element.
+ *
+ * Usually only used by list implementations. There should be no need
+ * to invoke this function manually.
+ *
+ * @param list the list
+ * @param elem the element
+ */
+__attribute__((__nonnull__))
+void cx_list_invoke_advanced_destructor(
+ struct cx_list_s const *list,
+ void *elem
+);
+
+/**
+ * Advises the list to store copies of the objects (default mode of operation).
+ *
+ * Retrieving objects from this list will yield pointers to the copies stored
+ * within this list.
+ *
+ * @param list the list
+ * @see cxListStorePointers()
+ */
+__attribute__((__nonnull__))
+void cxListStoreObjects(CxList *list);
+
+/**
+ * Advises the list to only store pointers to the objects.
+ *
+ * Retrieving objects from this list will yield the original pointers stored.
+ *
+ * @note This function forcibly sets the element size to the size of a pointer.
+ * Invoking this function on a non-empty list that already stores copies of
+ * objects is undefined.
+ *
+ * @param list the list
+ * @see cxListStoreObjects()
+ */
+__attribute__((__nonnull__))
+void cxListStorePointers(CxList *list);
+
+/**
+ * Returns true, if this list is storing pointers instead of the actual data.
+ *
+ * @param list
+ * @return
+ * @see cxListStorePointers()
+ */
+__attribute__((__nonnull__))
+bool cxListIsStoringPointers(CxList const *list);
+
+/**
+ * Adds an item to the end of the list.
+ *
+ * @param list the list
+ * @param elem a pointer to the element to add
+ * @return zero on success, non-zero on memory allocation failure
+ * @see cxListAddArray()
+ */
+__attribute__((__nonnull__))
+static inline int cxListAdd(
+ CxList *list,
+ void const *elem
+) {
+ return list->cl->insert_element(list, list->size, elem);
+}
+
+/**
+ * Adds multiple items to the end of the list.
+ *
+ * This method is more efficient than invoking cxListAdd() multiple times.
+ *
+ * If there is not enough memory to add all elements, the returned value is
+ * less than \p n.
+ *
+ * If this list is storing pointers instead of objects \p array is expected to
+ * be an array of pointers.
+ *
+ * @param list the list
+ * @param array a pointer to the elements to add
+ * @param n the number of elements to add
+ * @return the number of added elements
+ */
+__attribute__((__nonnull__))
+static inline size_t cxListAddArray(
+ CxList *list,
+ void const *array,
+ size_t n
+) {
+ return list->cl->insert_array(list, list->size, array, n);
+}
+
+/**
+ * Inserts an item at the specified index.
+ *
+ * If \p index equals the list \c size, this is effectively cxListAdd().
+ *
+ * @param list the list
+ * @param index the index the element shall have
+ * @param elem a pointer to the element to add
+ * @return zero on success, non-zero on memory allocation failure
+ * or when the index is out of bounds
+ * @see cxListInsertAfter()
+ * @see cxListInsertBefore()
+ */
+__attribute__((__nonnull__))
+static inline int cxListInsert(
+ CxList *list,
+ size_t index,
+ void const *elem
+) {
+ return list->cl->insert_element(list, index, elem);
+}
+
+/**
+ * Inserts multiple items to the list at the specified index.
+ * If \p index equals the list size, this is effectively cxListAddArray().
+ *
+ * This method is usually more efficient than invoking cxListInsert()
+ * multiple times.
+ *
+ * If there is not enough memory to add all elements, the returned value is
+ * less than \p n.
+ *
+ * If this list is storing pointers instead of objects \p array is expected to
+ * be an array of pointers.
+ *
+ * @param list the list
+ * @param index the index where to add the elements
+ * @param array a pointer to the elements to add
+ * @param n the number of elements to add
+ * @return the number of added elements
+ */
+__attribute__((__nonnull__))
+static inline size_t cxListInsertArray(
+ CxList *list,
+ size_t index,
+ void const *array,
+ size_t n
+) {
+ return list->cl->insert_array(list, index, array, n);
+}
+
+/**
+ * Inserts an element after the current location of the specified iterator.
+ *
+ * The used iterator remains operational, but all other active iterators should
+ * be considered invalidated.
+ *
+ * If \p iter is not a list iterator, the behavior is undefined.
+ * If \p iter is a past-the-end iterator, the new element gets appended to the list.
+ *
+ * @param iter an iterator
+ * @param elem the element to insert
+ * @return zero on success, non-zero on memory allocation failure
+ * @see cxListInsert()
+ * @see cxListInsertBefore()
+ */
+__attribute__((__nonnull__))
+static inline int cxListInsertAfter(
+ CxMutIterator *iter,
+ void const *elem
+) {
+ return ((struct cx_list_s *) iter->src_handle)->cl->insert_iter(iter, elem, 0);
+}
+
+/**
+ * Inserts an element before the current location of the specified iterator.
+ *
+ * The used iterator remains operational, but all other active iterators should
+ * be considered invalidated.
+ *
+ * If \p iter is not a list iterator, the behavior is undefined.
+ * If \p iter is a past-the-end iterator, the new element gets appended to the list.
+ *
+ * @param iter an iterator
+ * @param elem the element to insert
+ * @return zero on success, non-zero on memory allocation failure
+ * @see cxListInsert()
+ * @see cxListInsertAfter()
+ */
+__attribute__((__nonnull__))
+static inline int cxListInsertBefore(
+ CxMutIterator *iter,
+ void const *elem
+) {
+ return ((struct cx_list_s *) iter->src_handle)->cl->insert_iter(iter, elem, 1);
+}
+
+/**
+ * Removes the element at the specified index.
+ *
+ * If an element destructor function is specified, it is called before
+ * removing the element.
+ *
+ * @param list the list
+ * @param index the index of the element
+ * @return zero on success, non-zero if the index is out of bounds
+ */
+__attribute__((__nonnull__))
+static inline int cxListRemove(
+ CxList *list,
+ size_t index
+) {
+ return list->cl->remove(list, index);
+}
+
+/**
+ * Removes all elements from this list.
+ *
+ * If an element destructor function is specified, it is called for each
+ * element before removing them.
+ *
+ * @param list the list
+ */
+__attribute__((__nonnull__))
+static inline void cxListClear(CxList *list) {
+ list->cl->clear(list);
+}
+
+/**
+ * Swaps two items in the list.
+ *
+ * Implementations should only allocate temporary memory for the swap, if
+ * it is necessary.
+ *
+ * @param list the list
+ * @param i the index of the first element
+ * @param j the index of the second element
+ * @return zero on success, non-zero if one of the indices is out of bounds
+ */
+__attribute__((__nonnull__))
+static inline int cxListSwap(
+ CxList *list,
+ size_t i,
+ size_t j
+) {
+ return list->cl->swap(list, i, j);
+}
+
+/**
+ * Returns a pointer to the element at the specified index.
+ *
+ * @param list the list
+ * @param index the index of the element
+ * @return a pointer to the element or \c NULL if the index is out of bounds
+ */
+__attribute__((__nonnull__))
+static inline void *cxListAt(
+ CxList *list,
+ size_t index
+) {
+ return list->cl->at(list, index);
+}
+
+/**
+ * Returns an iterator pointing to the item at the specified index.
+ *
+ * The returned iterator is position-aware.
+ *
+ * If the index is out of range, a past-the-end iterator will be returned.
+ *
+ * @param list the list
+ * @param index the index where the iterator shall point at
+ * @return a new iterator
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline CxIterator cxListIteratorAt(
+ CxList const *list,
+ size_t index
+) {
+ return list->cl->iterator(list, index, false);
+}
+
+/**
+ * Returns a backwards iterator pointing to the item at the specified index.
+ *
+ * The returned iterator is position-aware.
+ *
+ * If the index is out of range, a past-the-end iterator will be returned.
+ *
+ * @param list the list
+ * @param index the index where the iterator shall point at
+ * @return a new iterator
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline CxIterator cxListBackwardsIteratorAt(
+ CxList const *list,
+ size_t index
+) {
+ return list->cl->iterator(list, index, true);
+}
+
+/**
+ * Returns a mutating iterator pointing to the item at the specified index.
+ *
+ * The returned iterator is position-aware.
+ *
+ * If the index is out of range, a past-the-end iterator will be returned.
+ *
+ * @param list the list
+ * @param index the index where the iterator shall point at
+ * @return a new iterator
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+CxMutIterator cxListMutIteratorAt(
+ CxList *list,
+ size_t index
+);
+
+/**
+ * Returns a mutating backwards iterator pointing to the item at the
+ * specified index.
+ *
+ * The returned iterator is position-aware.
+ *
+ * If the index is out of range, a past-the-end iterator will be returned.
+ *
+ * @param list the list
+ * @param index the index where the iterator shall point at
+ * @return a new iterator
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+CxMutIterator cxListMutBackwardsIteratorAt(
+ CxList *list,
+ size_t index
+);
+
+/**
+ * Returns an iterator pointing to the first item of the list.
+ *
+ * The returned iterator is position-aware.
+ *
+ * If the list is empty, a past-the-end iterator will be returned.
+ *
+ * @param list the list
+ * @return a new iterator
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline CxIterator cxListIterator(CxList const *list) {
+ return list->cl->iterator(list, 0, false);
+}
+
+/**
+ * Returns a mutating iterator pointing to the first item of the list.
+ *
+ * The returned iterator is position-aware.
+ *
+ * If the list is empty, a past-the-end iterator will be returned.
+ *
+ * @param list the list
+ * @return a new iterator
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline CxMutIterator cxListMutIterator(CxList *list) {
+ return cxListMutIteratorAt(list, 0);
+}
+
+
+/**
+ * Returns a backwards iterator pointing to the last item of the list.
+ *
+ * The returned iterator is position-aware.
+ *
+ * If the list is empty, a past-the-end iterator will be returned.
+ *
+ * @param list the list
+ * @return a new iterator
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline CxIterator cxListBackwardsIterator(CxList const *list) {
+ return list->cl->iterator(list, list->size - 1, true);
+}
+
+/**
+ * Returns a mutating backwards iterator pointing to the last item of the list.
+ *
+ * The returned iterator is position-aware.
+ *
+ * If the list is empty, a past-the-end iterator will be returned.
+ *
+ * @param list the list
+ * @return a new iterator
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline CxMutIterator cxListMutBackwardsIterator(CxList *list) {
+ return cxListMutBackwardsIteratorAt(list, list->size - 1);
+}
+
+/**
+ * Returns the index of the first element that equals \p elem.
+ *
+ * Determining equality is performed by the list's comparator function.
+ *
+ * @param list the list
+ * @param elem the element to find
+ * @return the index of the element or \c (size+1) if the element is not found
+ */
+__attribute__((__nonnull__))
+static inline size_t cxListFind(
+ CxList const *list,
+ void const *elem
+) {
+ return list->cl->find(list, elem);
+}
+
+/**
+ * Sorts the list in place.
+ *
+ * \remark The underlying sort algorithm is implementation defined.
+ *
+ * @param list the list
+ */
+__attribute__((__nonnull__))
+static inline void cxListSort(CxList *list) {
+ list->cl->sort(list);
+}
+
+/**
+ * Reverses the order of the items.
+ *
+ * @param list the list
+ */
+__attribute__((__nonnull__))
+static inline void cxListReverse(CxList *list) {
+ list->cl->reverse(list);
+}
+
+/**
+ * Compares a list to another list of the same type.
+ *
+ * First, the list sizes are compared.
+ * If they match, the lists are compared element-wise.
+ *
+ * @param list the list
+ * @param other the list to compare to
+ * @return zero, if both lists are equal element wise,
+ * negative if the first list is smaller, positive if the first list is larger
+ */
+__attribute__((__nonnull__))
+int cxListCompare(
+ CxList const *list,
+ CxList const *other
+);
+
+/**
+ * Deallocates the memory of the specified list structure.
+ *
+ * Also calls content a destructor function, depending on the configuration
+ * in CxList.content_destructor_type.
+ *
+ * This function itself is a destructor function for the CxList.
+ *
+ * @param list the list which shall be destroyed
+ */
+__attribute__((__nonnull__))
+void cxListDestroy(CxList *list);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // UCX_LIST_H
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * \file map.h
+ * \brief Interface for map implementations.
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+#ifndef UCX_MAP_H
+#define UCX_MAP_H
+
+#include "common.h"
+#include "allocator.h"
+#include "iterator.h"
+#include "hash_key.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef CX_STORE_POINTERS
+/**
+ * Special constant used for creating collections that are storing pointers.
+ */
+#define CX_STORE_POINTERS 0
+#endif
+
+/** Type for the UCX map. */
+typedef struct cx_map_s CxMap;
+
+/** Type for a map entry. */
+typedef struct cx_map_entry_s CxMapEntry;
+
+/** Type for map class definitions. */
+typedef struct cx_map_class_s cx_map_class;
+
+/** Structure for the UCX map. */
+struct cx_map_s {
+ /** The map class definition. */
+ cx_map_class *cl;
+ /** An allocator that is used for the map elements. */
+ CxAllocator *allocator;
+ /** The number of elements currently stored. */
+ size_t size;
+ /**
+ * The size of an element.
+ */
+ size_t itemsize;
+ /**
+ * True, if this map shall store pointers instead
+ * of copies of objects.
+ */
+ bool store_pointers;
+};
+
+/**
+ * The class definition for arbitrary maps.
+ */
+struct cx_map_class_s {
+ /**
+ * Deallocates the entire memory.
+ */
+ __attribute__((__nonnull__))
+ void (*destructor)(struct cx_map_s *map);
+
+ /**
+ * Removes all elements.
+ */
+ __attribute__((__nonnull__))
+ void (*clear)(struct cx_map_s *map);
+
+ /**
+ * Add or overwrite an element.
+ */
+ __attribute__((__nonnull__))
+ int (*put)(
+ CxMap *map,
+ CxHashKey key,
+ void *value
+ );
+
+ /**
+ * Returns an element.
+ */
+ __attribute__((__nonnull__, __warn_unused_result__))
+ void *(*get)(
+ CxMap const *map,
+ CxHashKey key
+ );
+
+ /**
+ * Removes an element.
+ */
+ __attribute__((__nonnull__))
+ void *(*remove)(
+ CxMap *map,
+ CxHashKey key
+ );
+
+ /**
+ * Iterator over the key/value pairs.
+ */
+ __attribute__((__nonnull__, __warn_unused_result__))
+ CxIterator (*iterator)(CxMap const *map);
+
+ /**
+ * Iterator over the keys.
+ */
+ __attribute__((__nonnull__, __warn_unused_result__))
+ CxIterator (*iterator_keys)(CxMap const *map);
+
+ /**
+ * Iterator over the values.
+ */
+ __attribute__((__nonnull__, __warn_unused_result__))
+ CxIterator (*iterator_values)(CxMap const *map);
+
+ /**
+ * Mutating iterator over the key/value pairs.
+ */
+ __attribute__((__nonnull__, __warn_unused_result__))
+ CxMutIterator (*mut_iterator)(CxMap *map);
+
+ /**
+ * Mutating iterator over the keys.
+ */
+ __attribute__((__nonnull__, __warn_unused_result__))
+ CxMutIterator (*mut_iterator_keys)(CxMap *map);
+
+ /**
+ * Mutating iterator over the values.
+ */
+ __attribute__((__nonnull__, __warn_unused_result__))
+ CxMutIterator (*mut_iterator_values)(CxMap *map);
+};
+
+/**
+ * A map entry.
+ */
+struct cx_map_entry_s {
+ /**
+ * A pointer to the key.
+ */
+ CxHashKey const *key;
+ /**
+ * A pointer to the value.
+ */
+ void *value;
+};
+
+/**
+ * Advises the map to store copies of the objects (default mode of operation).
+ *
+ * Retrieving objects from this map will yield pointers to the copies stored
+ * within this list.
+ *
+ * @param map the map
+ * @see cxMapStorePointers()
+ */
+__attribute__((__nonnull__))
+static inline void cxMapStoreObjects(CxMap *map) {
+ map->store_pointers = false;
+}
+
+/**
+ * Advises the map to only store pointers to the objects.
+ *
+ * Retrieving objects from this list will yield the original pointers stored.
+ *
+ * @note This function forcibly sets the element size to the size of a pointer.
+ * Invoking this function on a non-empty map that already stores copies of
+ * objects is undefined.
+ *
+ * @param map the map
+ * @see cxMapStoreObjects()
+ */
+__attribute__((__nonnull__))
+static inline void cxMapStorePointers(CxMap *map) {
+ map->store_pointers = true;
+ map->itemsize = sizeof(void *);
+}
+
+
+/**
+ * Deallocates the memory of the specified map.
+ *
+ * @param map the map to be destroyed
+ */
+__attribute__((__nonnull__))
+static inline void cxMapDestroy(CxMap *map) {
+ // TODO: likely to add auto-free feature for contents in the future
+ map->cl->destructor(map);
+}
+
+
+/**
+ * Clears a map by removing all elements.
+ *
+ * @param map the map to be cleared
+ */
+__attribute__((__nonnull__))
+static inline void cxMapClear(CxMap *map) {
+ map->cl->clear(map);
+}
+
+/**
+ * Puts a key/value-pair into the map.
+ *
+ * @param map the map
+ * @param key the key
+ * @param value the value
+ * @return 0 on success, non-zero value on failure
+ */
+__attribute__((__nonnull__))
+static inline int cxMapPut(
+ CxMap *map,
+ CxHashKey key,
+ void *value
+) {
+ return map->cl->put(map, key, value);
+}
+
+/**
+ * Retrieves a value by using a key.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the value
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cxMapGet(
+ CxMap const *map,
+ CxHashKey key
+) {
+ return map->cl->get(map, key);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * If this map is storing pointers, you should make sure that the map
+ * is not the last location where this pointer is stored.
+ * Otherwise, use cxMapRemoveAndGet() to retrieve the pointer while
+ * removing it from the map.
+ *
+ * @param map the map
+ * @param key the key
+ * @see cxMapRemoveAndGet()
+ */
+__attribute__((__nonnull__))
+static inline void cxMapRemove(
+ CxMap *map,
+ CxHashKey key
+) {
+ (void) map->cl->remove(map, key);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * This function should only be used when the map is storing pointers,
+ * in order to retrieve the pointer you are about to remove.
+ * In any other case, cxMapRemove() is sufficient.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the stored pointer or \c NULL if either the key is not present
+ * in the map or the map is not storing pointers
+ * @see cxMapStorePointers()
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cxMapRemoveAndGet(
+ CxMap *map,
+ CxHashKey key
+) {
+ return map->cl->remove(map, key);
+}
+
+// TODO: set-like map operations (union, intersect, difference)
+
+/**
+ * Creates a value iterator for a map.
+ *
+ * \note An iterator iterates over all elements successively. Therefore the order
+ * highly depends on the map implementation and may change arbitrarily when the contents change.
+ *
+ * @param map the map to create the iterator for
+ * @return an iterator for the currently stored values
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline CxIterator cxMapIteratorValues(CxMap *map) {
+ return map->cl->iterator_values(map);
+}
+
+/**
+ * Creates a key iterator for a map.
+ *
+ * The elements of the iterator are keys of type CxHashKey.
+ *
+ * \note An iterator iterates over all elements successively. Therefore the order
+ * highly depends on the map implementation and may change arbitrarily when the contents change.
+ *
+ * @param map the map to create the iterator for
+ * @return an iterator for the currently stored keys
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline CxIterator cxMapIteratorKeys(CxMap *map) {
+ return map->cl->iterator_keys(map);
+}
+
+/**
+ * Creates an iterator for a map.
+ *
+ * The elements of the iterator are key/value pairs of type CxMapEntry.
+ *
+ * \note An iterator iterates over all elements successively. Therefore the order
+ * highly depends on the map implementation and may change arbitrarily when the contents change.
+ *
+ * @param map the map to create the iterator for
+ * @return an iterator for the currently stored entries
+ * @see cxMapIteratorKeys()
+ * @see cxMapIteratorValues()
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline CxIterator cxMapIterator(CxMap *map) {
+ return map->cl->iterator(map);
+}
+
+
+/**
+ * Creates a mutating iterator over the values of a map.
+ *
+ * \note An iterator iterates over all elements successively. Therefore the order
+ * highly depends on the map implementation and may change arbitrarily when the contents change.
+ *
+ * @param map the map to create the iterator for
+ * @return an iterator for the currently stored values
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline CxMutIterator cxMapMutIteratorValues(CxMap *map) {
+ return map->cl->mut_iterator_values(map);
+}
+
+/**
+ * Creates a mutating iterator over the keys of a map.
+ *
+ * The elements of the iterator are keys of type CxHashKey.
+ *
+ * \note An iterator iterates over all elements successively. Therefore the order
+ * highly depends on the map implementation and may change arbitrarily when the contents change.
+ *
+ * @param map the map to create the iterator for
+ * @return an iterator for the currently stored keys
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline CxMutIterator cxMapMutIteratorKeys(CxMap *map) {
+ return map->cl->mut_iterator_keys(map);
+}
+
+/**
+ * Creates a mutating iterator for a map.
+ *
+ * The elements of the iterator are key/value pairs of type CxMapEntry.
+ *
+ * \note An iterator iterates over all elements successively. Therefore the order
+ * highly depends on the map implementation and may change arbitrarily when the contents change.
+ *
+ * @param map the map to create the iterator for
+ * @return an iterator for the currently stored entries
+ * @see cxMapMutIteratorKeys()
+ * @see cxMapMutIteratorValues()
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline CxMutIterator cxMapMutIterator(CxMap *map) {
+ return map->cl->mut_iterator(map);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // UCX_MAP_H
\ No newline at end of file
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * \file mempool.h
+ * \brief Interface for memory pool implementations.
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+#ifndef UCX_MEMPOOL_H
+#define UCX_MEMPOOL_H
+
+#include "allocator.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Memory pool class type.
+ */
+typedef struct cx_mempool_class_s cx_mempool_class;
+
+/**
+ * The basic structure of a memory pool.
+ * Should be the first member of an actual memory pool implementation.
+ */
+struct cx_mempool_s {
+ /**
+ * The pool class definition.
+ */
+ cx_mempool_class *cl;
+ /**
+ * The provided allocator.
+ */
+ CxAllocator const *allocator;
+};
+
+/**
+ * Common type for all memory pool implementations.
+ */
+typedef struct cx_mempool_s CxMempool;
+
+/**
+ * The class definition for a memory pool.
+ */
+struct cx_mempool_class_s {
+ /** Member function for destroying the pool. */
+ __attribute__((__nonnull__))
+ void (*destroy)(CxMempool *pool);
+
+ /** Member function for setting a destructor. */
+ __attribute__((__nonnull__))
+ void (*set_destructor)(
+ CxMempool *pool,
+ void *memory,
+ cx_destructor_func fnc
+ );
+};
+
+
+/**
+ * Destroys a memory pool including their contents.
+ *
+ * @param pool the memory pool to destroy
+ */
+__attribute__((__nonnull__))
+static inline void cxMempoolDestroy(CxMempool *pool) {
+ pool->cl->destroy(pool);
+}
+
+/**
+ * Sets a destructor function for an allocated memory object.
+ *
+ * If the memory is not managed by the pool, the behavior is undefined.
+ *
+ * @param pool the pool
+ * @param memory the objected allocated in the pool
+ * @param fnc the destructor function
+ */
+__attribute__((__nonnull__))
+static inline void cxMempoolSetDestructor(
+ CxMempool *pool,
+ void *memory,
+ cx_destructor_func fnc
+) {
+ pool->cl->set_destructor(pool, memory, fnc);
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // UCX_MEMPOOL_H
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * \file printf.h
+ * \brief Wrapper for write functions with a printf-like interface.
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+#ifndef UCX_PRINTF_H
+#define UCX_PRINTF_H
+
+#include "common.h"
+#include "string.h"
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * A \c fprintf like function which writes the output to a stream by
+ * using a write_func.
+ *
+ * @param stream the stream the data is written to
+ * @param wfc the write function
+ * @param fmt format string
+ * @param ... additional arguments
+ * @return the total number of bytes written
+ */
+__attribute__((__nonnull__(1, 2, 3), __format__(printf, 3, 4)))
+int cx_fprintf(
+ void *stream,
+ cx_write_func wfc,
+ char const *fmt,
+ ...
+);
+
+/**
+ * A \c vfprintf like function which writes the output to a stream by
+ * using a write_func.
+ *
+ * @param stream the stream the data is written to
+ * @param wfc the write function
+ * @param fmt format string
+ * @param ap argument list
+ * @return the total number of bytes written
+ * @see cx_fprintf()
+ */
+__attribute__((__nonnull__))
+int cx_vfprintf(
+ void *stream,
+ cx_write_func wfc,
+ char const *fmt,
+ va_list ap
+);
+
+/**
+ * A \c asprintf like function which allocates space for a string
+ * the result is written to.
+ *
+ * \note The resulting string is guaranteed to be zero-terminated.
+ *
+ * @param allocator the CxAllocator used for allocating the string
+ * @param fmt format string
+ * @param ... additional arguments
+ * @return the formatted string
+ * @see cx_strfree_a()
+ */
+__attribute__((__nonnull__(1, 2), __format__(printf, 2, 3)))
+cxmutstr cx_asprintf_a(
+ CxAllocator *allocator,
+ char const *fmt,
+ ...
+);
+
+/**
+ * A \c asprintf like function which allocates space for a string
+ * the result is written to.
+ *
+ * \note The resulting string is guaranteed to be zero-terminated.
+ *
+ * @param fmt format string
+ * @param ... additional arguments
+ * @return the formatted string
+ * @see cx_strfree()
+ */
+#define cx_asprintf(fmt, ...) \
+ cx_asprintf_a(cxDefaultAllocator, fmt, __VA_ARGS__)
+
+/**
+* A \c vasprintf like function which allocates space for a string
+ * the result is written to.
+ *
+ * \note The resulting string is guaranteed to be zero-terminated.
+ *
+ * @param allocator the CxAllocator used for allocating the string
+ * @param fmt format string
+ * @param ap argument list
+ * @return the formatted string
+ * @see cx_asprintf_a()
+ */
+__attribute__((__nonnull__))
+cxmutstr cx_vasprintf_a(
+ CxAllocator *allocator,
+ char const *fmt,
+ va_list ap
+);
+
+/**
+* A \c vasprintf like function which allocates space for a string
+ * the result is written to.
+ *
+ * \note The resulting string is guaranteed to be zero-terminated.
+ *
+ * @param fmt format string
+ * @param ap argument list
+ * @return the formatted string
+ * @see cx_asprintf()
+ */
+#define cx_vasprintf(fmt, ap) cx_vasprintf_a(cxDefaultAllocator, fmt, ap)
+
+/**
+ * A \c printf like function which writes the output to a CxBuffer.
+ *
+ * @param buffer a pointer to the buffer the data is written to
+ * @param fmt the format string
+ * @param ... additional arguments
+ * @return the total number of bytes written
+ * @see ucx_fprintf()
+ */
+#define cx_bprintf(buffer, fmt, ...) cx_fprintf((CxBuffer*)buffer, \
+ (cx_write_func) cxBufferWrite, fmt, __VA_ARGS__)
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif //UCX_PRINTF_H
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * \file string.h
+ * \brief Strings that know their length.
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+#ifndef UCX_STRING_H
+#define UCX_STRING_H
+
+#include "common.h"
+#include "allocator.h"
+
+/**
+ * The UCX string structure.
+ */
+struct cx_mutstr_s {
+ /**
+ * A pointer to the string.
+ * \note The string is not necessarily \c NULL terminated.
+ * Always use the length.
+ */
+ char *ptr;
+ /** The length of the string */
+ size_t length;
+};
+
+/**
+ * A mutable string.
+ */
+typedef struct cx_mutstr_s cxmutstr;
+
+/**
+ * The UCX string structure for immutable (constant) strings.
+ */
+struct cx_string_s {
+ /**
+ * A pointer to the immutable string.
+ * \note The string is not necessarily \c NULL terminated.
+ * Always use the length.
+ */
+ char const *ptr;
+ /** The length of the string */
+ size_t length;
+};
+
+/**
+ * An immutable string.
+ */
+typedef struct cx_string_s cxstring;
+
+/**
+ * Context for string tokenizing.
+ */
+struct cx_strtok_ctx_s {
+ /**
+ * The string to tokenize.
+ */
+ cxstring str;
+ /**
+ * The primary delimiter.
+ */
+ cxstring delim;
+ /**
+ * Optional array of more delimiters.
+ */
+ cxstring const *delim_more;
+ /**
+ * Length of the array containing more delimiters.
+ */
+ size_t delim_more_count;
+ /**
+ * Position of the currently active token in the source string.
+ */
+ size_t pos;
+ /**
+ * Position of next delimiter in the source string.
+ *
+ * If the tokenizer has not yet returned a token, the content of this field
+ * is undefined. If the tokenizer reached the end of the string, this field
+ * contains the length of the source string.
+ */
+ size_t delim_pos;
+ /**
+ * The position of the next token in the source string.
+ */
+ size_t next_pos;
+ /**
+ * The number of already found tokens.
+ */
+ size_t found;
+ /**
+ * The maximum number of tokens that shall be returned.
+ */
+ size_t limit;
+};
+
+/**
+ * A string tokenizing context.
+ */
+typedef struct cx_strtok_ctx_s CxStrtokCtx;
+
+/**
+ * A literal initializer for an UCX string structure.
+ *
+ * The argument MUST be a string (const char*) \em literal.
+ *
+ * @param literal the string literal
+ */
+#define CX_STR(literal) {literal, sizeof(literal) - 1}
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * Wraps a mutable string that must be zero-terminated.
+ *
+ * The length is implicitly inferred by using a call to \c strlen().
+ *
+ * \note the wrapped string will share the specified pointer to the string.
+ * If you do want a copy, use cx_strdup() on the return value of this function.
+ *
+ * If you need to wrap a constant string, use cx_str().
+ *
+ * @param cstring the string to wrap, must be zero-terminated
+ * @return the wrapped string
+ *
+ * @see cx_mutstrn()
+ */
+__attribute__((__warn_unused_result__, __nonnull__))
+cxmutstr cx_mutstr(char *cstring);
+
+/**
+ * Wraps a string that does not need to be zero-terminated.
+ *
+ * The argument may be \c NULL if the length is zero.
+ *
+ * \note the wrapped string will share the specified pointer to the string.
+ * If you do want a copy, use cx_strdup() on the return value of this function.
+ *
+ * If you need to wrap a constant string, use cx_strn().
+ *
+ * @param cstring the string to wrap (or \c NULL, only if the length is zero)
+ * @param length the length of the string
+ * @return the wrapped string
+ *
+ * @see cx_mutstr()
+ */
+__attribute__((__warn_unused_result__))
+cxmutstr cx_mutstrn(
+ char *cstring,
+ size_t length
+);
+
+/**
+ * Wraps a string that must be zero-terminated.
+ *
+ * The length is implicitly inferred by using a call to \c strlen().
+ *
+ * \note the wrapped string will share the specified pointer to the string.
+ * If you do want a copy, use cx_strdup() on the return value of this function.
+ *
+ * If you need to wrap a non-constant string, use cx_mutstr().
+ *
+ * @param cstring the string to wrap, must be zero-terminated
+ * @return the wrapped string
+ *
+ * @see cx_strn()
+ */
+__attribute__((__warn_unused_result__, __nonnull__))
+cxstring cx_str(char const *cstring);
+
+
+/**
+ * Wraps a string that does not need to be zero-terminated.
+ *
+ * The argument may be \c NULL if the length is zero.
+ *
+ * \note the wrapped string will share the specified pointer to the string.
+ * If you do want a copy, use cx_strdup() on the return value of this function.
+ *
+ * If you need to wrap a non-constant string, use cx_mutstrn().
+ *
+ * @param cstring the string to wrap (or \c NULL, only if the length is zero)
+ * @param length the length of the string
+ * @return the wrapped string
+ *
+ * @see cx_str()
+ */
+__attribute__((__warn_unused_result__))
+cxstring cx_strn(
+ char const *cstring,
+ size_t length
+);
+
+/**
+* Casts a mutable string to an immutable string.
+*
+* \note This is not seriously a cast. Instead you get a copy
+* of the struct with the desired pointer type. Both structs still
+* point to the same location, though!
+*
+* @param str the mutable string to cast
+* @return an immutable copy of the string pointer
+*/
+__attribute__((__warn_unused_result__))
+cxstring cx_strcast(cxmutstr str);
+
+/**
+ * Passes the pointer in this string to \c free().
+ *
+ * The pointer in the struct is set to \c NULL and the length is set to zero.
+ *
+ * \note There is no implementation for cxstring, because it is unlikely that
+ * you ever have a \c char \c const* you are really supposed to free. If you
+ * encounter such situation, you should double-check your code.
+ *
+ * @param str the string to free
+ */
+__attribute__((__nonnull__))
+void cx_strfree(cxmutstr *str);
+
+/**
+ * Passes the pointer in this string to the allocators free function.
+ *
+ * The pointer in the struct is set to \c NULL and the length is set to zero.
+ *
+ * \note There is no implementation for cxstring, because it is unlikely that
+ * you ever have a \c char \c const* you are really supposed to free. If you
+ * encounter such situation, you should double-check your code.
+ *
+ * @param alloc the allocator
+ * @param str the string to free
+ */
+__attribute__((__nonnull__))
+void cx_strfree_a(
+ CxAllocator *alloc,
+ cxmutstr *str
+);
+
+/**
+ * Returns the accumulated length of all specified strings.
+ *
+ * \attention if the count argument is larger than the number of the
+ * specified strings, the behavior is undefined.
+ *
+ * @param count the total number of specified strings
+ * @param ... all strings
+ * @return the accumulated length of all strings
+ */
+__attribute__((__warn_unused_result__))
+size_t cx_strlen(
+ size_t count,
+ ...
+);
+
+/**
+ * Concatenates two or more strings.
+ *
+ * The resulting string will be allocated by the specified allocator.
+ * So developers \em must pass the return value to cx_strfree() eventually.
+ *
+ * \note It is guaranteed that there is only one allocation.
+ * It is also guaranteed that the returned string is zero-terminated.
+ *
+ * @param alloc the allocator to use
+ * @param count the total number of strings to concatenate
+ * @param ... all strings
+ * @return the concatenated string
+ */
+__attribute__((__warn_unused_result__, __nonnull__))
+cxmutstr cx_strcat_a(
+ CxAllocator *alloc,
+ size_t count,
+ ...
+);
+
+/**
+ * Concatenates two or more strings.
+ *
+ * The resulting string will be allocated by standard \c malloc().
+ * So developers \em must pass the return value to cx_strfree() eventually.
+ *
+ * \note It is guaranteed that there is only one allocation.
+ * It is also guaranteed that the returned string is zero-terminated.
+ *
+ * @param count the total number of strings to concatenate
+ * @param ... all strings
+ * @return the concatenated string
+ */
+#define cx_strcat(count, ...) \
+cx_strcat_a(cxDefaultAllocator, count, __VA_ARGS__)
+
+/**
+ * Returns a substring starting at the specified location.
+ *
+ * \attention the new string references the same memory area as the
+ * input string and is usually \em not zero-terminated.
+ * Use cx_strdup() to get a copy.
+ *
+ * @param string input string
+ * @param start start location of the substring
+ * @return a substring of \p string starting at \p start
+ *
+ * @see cx_strsubsl()
+ * @see cx_strsubs_m()
+ * @see cx_strsubsl_m()
+ */
+__attribute__((__warn_unused_result__))
+cxstring cx_strsubs(
+ cxstring string,
+ size_t start
+);
+
+/**
+ * Returns a substring starting at the specified location.
+ *
+ * The returned string will be limited to \p length bytes or the number
+ * of bytes available in \p string, whichever is smaller.
+ *
+ * \attention the new string references the same memory area as the
+ * input string and is usually \em not zero-terminated.
+ * Use cx_strdup() to get a copy.
+ *
+ * @param string input string
+ * @param start start location of the substring
+ * @param length the maximum length of the returned string
+ * @return a substring of \p string starting at \p start
+ *
+ * @see cx_strsubs()
+ * @see cx_strsubs_m()
+ * @see cx_strsubsl_m()
+ */
+__attribute__((__warn_unused_result__))
+cxstring cx_strsubsl(
+ cxstring string,
+ size_t start,
+ size_t length
+);
+
+/**
+ * Returns a substring starting at the specified location.
+ *
+ * \attention the new string references the same memory area as the
+ * input string and is usually \em not zero-terminated.
+ * Use cx_strdup() to get a copy.
+ *
+ * @param string input string
+ * @param start start location of the substring
+ * @return a substring of \p string starting at \p start
+ *
+ * @see cx_strsubsl_m()
+ * @see cx_strsubs()
+ * @see cx_strsubsl()
+ */
+__attribute__((__warn_unused_result__))
+cxmutstr cx_strsubs_m(
+ cxmutstr string,
+ size_t start
+);
+
+/**
+ * Returns a substring starting at the specified location.
+ *
+ * The returned string will be limited to \p length bytes or the number
+ * of bytes available in \p string, whichever is smaller.
+ *
+ * \attention the new string references the same memory area as the
+ * input string and is usually \em not zero-terminated.
+ * Use cx_strdup() to get a copy.
+ *
+ * @param string input string
+ * @param start start location of the substring
+ * @param length the maximum length of the returned string
+ * @return a substring of \p string starting at \p start
+ *
+ * @see cx_strsubs_m()
+ * @see cx_strsubs()
+ * @see cx_strsubsl()
+ */
+__attribute__((__warn_unused_result__))
+cxmutstr cx_strsubsl_m(
+ cxmutstr string,
+ size_t start,
+ size_t length
+);
+
+/**
+ * Returns a substring starting at the location of the first occurrence of the
+ * specified character.
+ *
+ * If the string does not contain the character, an empty string is returned.
+ *
+ * @param string the string where to locate the character
+ * @param chr the character to locate
+ * @return a substring starting at the first location of \p chr
+ *
+ * @see cx_strchr_m()
+ */
+__attribute__((__warn_unused_result__))
+cxstring cx_strchr(
+ cxstring string,
+ int chr
+);
+
+/**
+ * Returns a substring starting at the location of the first occurrence of the
+ * specified character.
+ *
+ * If the string does not contain the character, an empty string is returned.
+ *
+ * @param string the string where to locate the character
+ * @param chr the character to locate
+ * @return a substring starting at the first location of \p chr
+ *
+ * @see cx_strchr()
+ */
+__attribute__((__warn_unused_result__))
+cxmutstr cx_strchr_m(
+ cxmutstr string,
+ int chr
+);
+
+/**
+ * Returns a substring starting at the location of the last occurrence of the
+ * specified character.
+ *
+ * If the string does not contain the character, an empty string is returned.
+ *
+ * @param string the string where to locate the character
+ * @param chr the character to locate
+ * @return a substring starting at the last location of \p chr
+ *
+ * @see cx_strrchr_m()
+ */
+__attribute__((__warn_unused_result__))
+cxstring cx_strrchr(
+ cxstring string,
+ int chr
+);
+
+/**
+ * Returns a substring starting at the location of the last occurrence of the
+ * specified character.
+ *
+ * If the string does not contain the character, an empty string is returned.
+ *
+ * @param string the string where to locate the character
+ * @param chr the character to locate
+ * @return a substring starting at the last location of \p chr
+ *
+ * @see cx_strrchr()
+ */
+__attribute__((__warn_unused_result__))
+cxmutstr cx_strrchr_m(
+ cxmutstr string,
+ int chr
+);
+
+/**
+ * Returns a substring starting at the location of the first occurrence of the
+ * specified string.
+ *
+ * If \p haystack does not contain \p needle, an empty string is returned.
+ *
+ * If \p needle is an empty string, the complete \p haystack is
+ * returned.
+ *
+ * @param haystack the string to be scanned
+ * @param needle string containing the sequence of characters to match
+ * @return a substring starting at the first occurrence of
+ * \p needle, or an empty string, if the sequence is not
+ * contained
+ * @see cx_strstr_m()
+ */
+__attribute__((__warn_unused_result__))
+cxstring cx_strstr(
+ cxstring haystack,
+ cxstring needle
+);
+
+/**
+ * Returns a substring starting at the location of the first occurrence of the
+ * specified string.
+ *
+ * If \p haystack does not contain \p needle, an empty string is returned.
+ *
+ * If \p needle is an empty string, the complete \p haystack is
+ * returned.
+ *
+ * @param haystack the string to be scanned
+ * @param needle string containing the sequence of characters to match
+ * @return a substring starting at the first occurrence of
+ * \p needle, or an empty string, if the sequence is not
+ * contained
+ * @see cx_strstr()
+ */
+__attribute__((__warn_unused_result__))
+cxmutstr cx_strstr_m(
+ cxmutstr haystack,
+ cxstring needle
+);
+
+/**
+ * Splits a given string using a delimiter string.
+ *
+ * \note The resulting array contains strings that point to the source
+ * \p string. Use cx_strdup() to get copies.
+ *
+ * @param string the string to split
+ * @param delim the delimiter
+ * @param limit the maximum number of split items
+ * @param output a pre-allocated array of at least \p limit length
+ * @return the actual number of split items
+ */
+__attribute__((__warn_unused_result__, __nonnull__))
+size_t cx_strsplit(
+ cxstring string,
+ cxstring delim,
+ size_t limit,
+ cxstring *output
+);
+
+/**
+ * Splits a given string using a delimiter string.
+ *
+ * The array pointed to by \p output will be allocated by \p allocator.
+ *
+ * \note The resulting array contains strings that point to the source
+ * \p string. Use cx_strdup() to get copies.
+ *
+ * \attention If allocation fails, the \c NULL pointer will be written to
+ * \p output and the number returned will be zero.
+ *
+ * @param allocator the allocator to use for allocating the resulting array
+ * @param string the string to split
+ * @param delim the delimiter
+ * @param limit the maximum number of split items
+ * @param output a pointer where the address of the allocated array shall be
+ * written to
+ * @return the actual number of split items
+ */
+__attribute__((__warn_unused_result__, __nonnull__))
+size_t cx_strsplit_a(
+ CxAllocator *allocator,
+ cxstring string,
+ cxstring delim,
+ size_t limit,
+ cxstring **output
+);
+
+
+/**
+ * Splits a given string using a delimiter string.
+ *
+ * \note The resulting array contains strings that point to the source
+ * \p string. Use cx_strdup() to get copies.
+ *
+ * @param string the string to split
+ * @param delim the delimiter
+ * @param limit the maximum number of split items
+ * @param output a pre-allocated array of at least \p limit length
+ * @return the actual number of split items
+ */
+__attribute__((__warn_unused_result__, __nonnull__))
+size_t cx_strsplit_m(
+ cxmutstr string,
+ cxstring delim,
+ size_t limit,
+ cxmutstr *output
+);
+
+/**
+ * Splits a given string using a delimiter string.
+ *
+ * The array pointed to by \p output will be allocated by \p allocator.
+ *
+ * \note The resulting array contains strings that point to the source
+ * \p string. Use cx_strdup() to get copies.
+ *
+ * \attention If allocation fails, the \c NULL pointer will be written to
+ * \p output and the number returned will be zero.
+ *
+ * @param allocator the allocator to use for allocating the resulting array
+ * @param string the string to split
+ * @param delim the delimiter
+ * @param limit the maximum number of split items
+ * @param output a pointer where the address of the allocated array shall be
+ * written to
+ * @return the actual number of split items
+ */
+__attribute__((__warn_unused_result__, __nonnull__))
+size_t cx_strsplit_ma(
+ CxAllocator *allocator,
+ cxmutstr string,
+ cxstring delim,
+ size_t limit,
+ cxmutstr **output
+);
+
+/**
+ * Compares two strings.
+ *
+ * @param s1 the first string
+ * @param s2 the second string
+ * @return negative if \p s1 is smaller than \p s2, positive if \p s1 is larger
+ * than \p s2, zero if both strings equal
+ */
+__attribute__((__warn_unused_result__))
+int cx_strcmp(
+ cxstring s1,
+ cxstring s2
+);
+
+/**
+ * Compares two strings ignoring case.
+ *
+ * @param s1 the first string
+ * @param s2 the second string
+ * @return negative if \p s1 is smaller than \p s2, positive if \p s1 is larger
+ * than \p s2, zero if both strings equal ignoring case
+ */
+__attribute__((__warn_unused_result__))
+int cx_strcasecmp(
+ cxstring s1,
+ cxstring s2
+);
+
+/**
+ * Compares two strings.
+ *
+ * This function has a compatible signature for the use as a CxListComparator.
+ *
+ * @param s1 the first string
+ * @param s2 the second string
+ * @return negative if \p s1 is smaller than \p s2, positive if \p s1 is larger
+ * than \p s2, zero if both strings equal
+ */
+__attribute__((__warn_unused_result__, __nonnull__))
+int cx_strcmp_p(
+ void const *s1,
+ void const *s2
+);
+
+/**
+ * Compares two strings ignoring case.
+ *
+ * This function has a compatible signature for the use as a CxListComparator.
+ *
+ * @param s1 the first string
+ * @param s2 the second string
+ * @return negative if \p s1 is smaller than \p s2, positive if \p s1 is larger
+ * than \p s2, zero if both strings equal ignoring case
+ */
+__attribute__((__warn_unused_result__, __nonnull__))
+int cx_strcasecmp_p(
+ void const *s1,
+ void const *s2
+);
+
+
+/**
+ * Creates a duplicate of the specified string.
+ *
+ * The new string will contain a copy allocated by \p allocator.
+ *
+ * \note The returned string is guaranteed to be zero-terminated.
+ *
+ * @param allocator the allocator to use
+ * @param string the string to duplicate
+ * @return a duplicate of the string
+ * @see cx_strdup()
+ */
+__attribute__((__warn_unused_result__, __nonnull__))
+cxmutstr cx_strdup_a(
+ CxAllocator *allocator,
+ cxstring string
+);
+
+/**
+ * Creates a duplicate of the specified string.
+ *
+ * The new string will contain a copy allocated by standard
+ * \c malloc(). So developers \em must pass the return value to cx_strfree().
+ *
+ * \note The returned string is guaranteed to be zero-terminated.
+ *
+ * @param string the string to duplicate
+ * @return a duplicate of the string
+ * @see cx_strdup_a()
+ */
+#define cx_strdup(string) cx_strdup_a(cxDefaultAllocator, string)
+
+/**
+ * Omits leading and trailing spaces.
+ *
+ * \note the returned string references the same memory, thus you
+ * must \em not free the returned memory.
+ *
+ * @param string the string that shall be trimmed
+ * @return the trimmed string
+ */
+__attribute__((__warn_unused_result__))
+cxstring cx_strtrim(cxstring string);
+
+/**
+ * Omits leading and trailing spaces.
+ *
+ * \note the returned string references the same memory, thus you
+ * must \em not free the returned memory.
+ *
+ * @param string the string that shall be trimmed
+ * @return the trimmed string
+ */
+__attribute__((__warn_unused_result__))
+cxmutstr cx_strtrim_m(cxmutstr string);
+
+/**
+ * Checks, if a string has a specific prefix.
+ *
+ * @param string the string to check
+ * @param prefix the prefix the string should have
+ * @return \c true, if and only if the string has the specified prefix,
+ * \c false otherwise
+ */
+__attribute__((__warn_unused_result__))
+bool cx_strprefix(
+ cxstring string,
+ cxstring prefix
+);
+
+/**
+ * Checks, if a string has a specific suffix.
+ *
+ * @param string the string to check
+ * @param suffix the suffix the string should have
+ * @return \c true, if and only if the string has the specified suffix,
+ * \c false otherwise
+ */
+__attribute__((__warn_unused_result__))
+bool cx_strsuffix(
+ cxstring string,
+ cxstring suffix
+);
+
+/**
+ * Checks, if a string has a specific prefix, ignoring the case.
+ *
+ * @param string the string to check
+ * @param prefix the prefix the string should have
+ * @return \c true, if and only if the string has the specified prefix,
+ * \c false otherwise
+ */
+__attribute__((__warn_unused_result__))
+bool cx_strcaseprefix(
+ cxstring string,
+ cxstring prefix
+);
+
+/**
+ * Checks, if a string has a specific suffix, ignoring the case.
+ *
+ * @param string the string to check
+ * @param suffix the suffix the string should have
+ * @return \c true, if and only if the string has the specified suffix,
+ * \c false otherwise
+ */
+__attribute__((__warn_unused_result__))
+bool cx_strcasesuffix(
+ cxstring string,
+ cxstring suffix
+);
+
+/**
+ * Converts the string to lower case.
+ *
+ * The change is made in-place. If you want a copy, use cx_strdup(), first.
+ *
+ * @param string the string to modify
+ * @see cx_strdup()
+ */
+void cx_strlower(cxmutstr string);
+
+/**
+ * Converts the string to upper case.
+ *
+ * The change is made in-place. If you want a copy, use cx_strdup(), first.
+ *
+ * @param string the string to modify
+ * @see cx_strdup()
+ */
+void cx_strupper(cxmutstr string);
+
+/**
+ * Replaces a pattern in a string with another string.
+ *
+ * The pattern is taken literally and is no regular expression.
+ * Replaces at most \p replmax occurrences.
+ *
+ * The returned string will be allocated by \p allocator and is guaranteed
+ * to be zero-terminated.
+ *
+ * If allocation fails, or the input string is empty,
+ * the returned string will be empty.
+ *
+ * @param allocator the allocator to use
+ * @param str the string where replacements should be applied
+ * @param pattern the pattern to search for
+ * @param replacement the replacement string
+ * @param replmax maximum number of replacements
+ * @return the resulting string after applying the replacements
+ */
+__attribute__((__warn_unused_result__, __nonnull__))
+cxmutstr cx_strreplacen_a(
+ CxAllocator *allocator,
+ cxstring str,
+ cxstring pattern,
+ cxstring replacement,
+ size_t replmax
+);
+
+/**
+ * Replaces a pattern in a string with another string.
+ *
+ * The pattern is taken literally and is no regular expression.
+ * Replaces at most \p replmax occurrences.
+ *
+ * The returned string will be allocated by \c malloc() and is guaranteed
+ * to be zero-terminated.
+ *
+ * If allocation fails, or the input string is empty,
+ * the returned string will be empty.
+ *
+ * @param str the string where replacements should be applied
+ * @param pattern the pattern to search for
+ * @param replacement the replacement string
+ * @param replmax maximum number of replacements
+ * @return the resulting string after applying the replacements
+ */
+#define cx_strreplacen(str, pattern, replacement, replmax) \
+cx_strreplacen_a(cxDefaultAllocator, str, pattern, replacement, replmax)
+
+/**
+ * Replaces a pattern in a string with another string.
+ *
+ * The pattern is taken literally and is no regular expression.
+ *
+ * The returned string will be allocated by \p allocator and is guaranteed
+ * to be zero-terminated.
+ *
+ * If allocation fails, or the input string is empty,
+ * the returned string will be empty.
+ *
+ * @param allocator the allocator to use
+ * @param str the string where replacements should be applied
+ * @param pattern the pattern to search for
+ * @param replacement the replacement string
+ * @return the resulting string after applying the replacements
+ */
+#define cx_strreplace_a(allocator, str, pattern, replacement) \
+cx_strreplacen_a(allocator, str, pattern, replacement, SIZE_MAX)
+
+/**
+ * Replaces a pattern in a string with another string.
+ *
+ * The pattern is taken literally and is no regular expression.
+ * Replaces at most \p replmax occurrences.
+ *
+ * The returned string will be allocated by \c malloc() and is guaranteed
+ * to be zero-terminated.
+ *
+ * If allocation fails, or the input string is empty,
+ * the returned string will be empty.
+ *
+ * @param str the string where replacements should be applied
+ * @param pattern the pattern to search for
+ * @param replacement the replacement string
+ * @return the resulting string after applying the replacements
+ */
+#define cx_strreplace(str, pattern, replacement) \
+cx_strreplacen_a(cxDefaultAllocator, str, pattern, replacement, SIZE_MAX)
+
+/**
+ * Creates a string tokenization context.
+ *
+ * @param str the string to tokenize
+ * @param delim the delimiter (must not be empty)
+ * @param limit the maximum number of tokens that shall be returned
+ * @return a new string tokenization context
+ */
+__attribute__((__warn_unused_result__))
+CxStrtokCtx cx_strtok(
+ cxstring str,
+ cxstring delim,
+ size_t limit
+);
+
+/**
+* Creates a string tokenization context for a mutable string.
+*
+* @param str the string to tokenize
+* @param delim the delimiter (must not be empty)
+* @param limit the maximum number of tokens that shall be returned
+* @return a new string tokenization context
+*/
+__attribute__((__warn_unused_result__))
+CxStrtokCtx cx_strtok_m(
+ cxmutstr str,
+ cxstring delim,
+ size_t limit
+);
+
+/**
+ * Returns the next token.
+ *
+ * The token will point to the source string.
+ *
+ * @param ctx the tokenization context
+ * @param token a pointer to memory where the next token shall be stored
+ * @return true if successful, false if the limit or the end of the string
+ * has been reached
+ */
+__attribute__((__warn_unused_result__, __nonnull__))
+bool cx_strtok_next(
+ CxStrtokCtx *ctx,
+ cxstring *token
+);
+
+/**
+ * Returns the next token of a mutable string.
+ *
+ * The token will point to the source string.
+ * If the context was not initialized over a mutable string, modifying
+ * the data of the returned token is undefined behavior.
+ *
+ * @param ctx the tokenization context
+ * @param token a pointer to memory where the next token shall be stored
+ * @return true if successful, false if the limit or the end of the string
+ * has been reached
+ */
+__attribute__((__warn_unused_result__, __nonnull__))
+bool cx_strtok_next_m(
+ CxStrtokCtx *ctx,
+ cxmutstr *token
+);
+
+/**
+ * Defines an array of more delimiters for the specified tokenization context.
+ *
+ * @param ctx the tokenization context
+ * @param delim array of more delimiters
+ * @param count number of elements in the array
+ */
+__attribute__((__nonnull__))
+void cx_strtok_delim(
+ CxStrtokCtx *ctx,
+ cxstring const *delim,
+ size_t count
+);
+
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif //UCX_STRING_H
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * \file tree.h
+ * \brief Interface for tree implementations.
+ * \author Olaf Wintermann
+ * \author Mike Becker
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+#ifndef UCX_TREE_H
+#define UCX_TREE_H
+
+#include "common.h"
+#include "allocator.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Adds a sibling to the current tree node.
+ *
+ * In case your struct does not have a \p prev or a \p parent pointer,
+ * specify a negative location. The location of the \p next pointer is
+ * mandatory.
+ *
+ * \attention Do not use this function to add siblings in a tree where the
+ * nodes store a pointer to the last sibling because that would not be modified by this function.
+ *
+ * \remark If yo do not provide a location to the parent pointer, a call to this function is
+ * effectively the same as a call to cx_linked_list_add().
+ *
+ * @param node a pointer to the node
+ * @param loc_prev the location of a \c prev pointer within your node struct
+ * @param loc_next the location of a \c next pointer within your node struct
+ * @param loc_parent the location of a \c parent pointer within your node struct
+ * @param new_node the new node that shall be added as a sibling
+ */
+void cx_tree_add_sibling(void *node,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next,
+ ptrdiff_t loc_parent,
+ void *new_node)
+__attribute__((__nonnull__));
+
+/**
+ * Adds a node to the list of children.
+ *
+ * \par Example with a full structure
+ * A full tree node structure may look like this:
+ * \code
+ * typedef struct MyTreeNode MyTreeNode;
+ * struct MyTreeNode {
+ * MyTreeNode* parent;
+ * MyTreeNode* first_child;
+ * MyTreeNode* last_child;
+ * MyTreeNode* prev_sibling;
+ * MyTreeNode* next_sibling;
+ * // ...contents...
+ * }
+ * \endcode
+ * Adding a new child to a node with the above structure can be performed with the following call:
+ * \code
+ * MyTreeNode *node, *child; // given
+ * cx_tree_add_child(&node->first_child, &node->last_child,
+ * offsetof(MyTreeNode, prev_sibling), offsetof(MyTreeNode, next_sibling),
+ * child, offsetof(MyTreeNode, parent), node);
+ * \endcode
+ *
+ * \par Example with a reduced structure
+ * The minimal reasonable structure with parent pointer looks like this:
+ * \code
+ * typedef struct MyTreeNode MyTreeNode;
+ * struct MyTreeNode {
+ * MyTreeNode* parent;
+ * MyTreeNode* children;
+ * MyTreeNode* next_sibling;
+ * // ...contents...
+ * }
+ * \endcode
+ * This simplifies the function call to:
+ * \code
+ * MyTreeNode *node, *child; // given
+ * cx_tree_add_child(&node->children, NULL, -1, offsetof(MyTreeNode, next_sibling),
+ * child, offsetof(MyTreeNode, parent), node);
+ * \endcode
+ *
+ * \remark If your tree structure does not possess a parent pointer, a call to this function is
+ * effectively the same as a call to cx_linked_list_add().
+ *
+ * @param children_begin a pointer to the begin node pointer (if your list has one)
+ * @param children_end a pointer to the end node pointer (if your list has one)
+ * @param loc_prev the location of a \c prev pointer within your node struct
+ * @param loc_next the location of a \c next pointer within your node struct
+ * @param new_node a pointer to the node that shall be appended
+ * @param loc_parent the location of a \c parent pointer within your node struct
+ * @param parent the parent node
+ */
+void cx_tree_add_child(void **children_begin, void **children_end,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node,
+ ptrdiff_t loc_parent, void *parent)
+__attribute__((__nonnull__ (5)));
+
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // UCX_TREE_H
+
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file utils.h
+ *
+ * \brief General purpose utility functions.
+ *
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+#ifndef UCX_UTILS_H
+#define UCX_UTILS_H
+
+#include "common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Convenience macro for a for loop that counts from zero to n-1.
+ */
+#define cx_for_n(varname, n) for (size_t varname = 0 ; (varname) < (n) ; (varname)++)
+
+/**
+ * Convenience macro for swapping two pointers.
+ */
+#ifdef __cplusplus
+#define cx_swap_ptr(left, right) do {auto cx_tmp_swap_var = left; left = right; right = cx_tmp_swap_var;} while(0)
+#else
+#define cx_swap_ptr(left, right) do {void *cx_tmp_swap_var = left; left = right; right = cx_tmp_swap_var;} while(0)
+#endif
+
+// cx_szmul() definition
+
+#if (__GNUC__ >= 5 || defined(__clang__)) && !defined(CX_NO_SZMUL_BUILTIN)
+#define CX_SZMUL_BUILTIN
+
+/**
+ * Alias for \c __builtin_mul_overflow.
+ *
+ * Performs a multiplication of size_t values and checks for overflow.
+ *
+ * @param a first operand
+ * @param b second operand
+ * @param result a pointer to a size_t, where the result should
+ * be stored
+ * @return zero, if no overflow occurred and the result is correct, non-zero
+ * otherwise
+ */
+#define cx_szmul(a, b, result) __builtin_mul_overflow(a, b, result)
+
+#else // no GNUC or clang bultin
+
+/**
+ * Performs a multiplication of size_t values and checks for overflow.
+ *
+ * @param a first operand
+ * @param b second operand
+ * @param result a pointer to a size_t, where the result should
+ * be stored
+ * @return zero, if no overflow occurred and the result is correct, non-zero
+ * otherwise
+ */
+#define cx_szmul(a, b, result) cx_szmul_impl(a, b, result)
+
+/**
+ * Performs a multiplication of size_t values and checks for overflow.
+ *
+ * This is a custom implementation in case there is no compiler builtin
+ * available.
+ *
+ * @param a first operand
+ * @param b second operand
+ * @param result a pointer to a size_t where the result should be stored
+ * @return zero, if no overflow occurred and the result is correct, non-zero
+ * otherwise
+ */
+int cx_szmul_impl(size_t a, size_t b, size_t *result);
+
+#endif // cx_szmul
+
+
+/**
+ * Reads data from a stream and writes it to another stream.
+ *
+ * @param src the source stream
+ * @param dest the destination stream
+ * @param rfnc the read function
+ * @param wfnc the write function
+ * @param buf a pointer to the copy buffer or \c NULL if a buffer
+ * shall be implicitly created on the heap
+ * @param bufsize the size of the copy buffer - if \p buf is \c NULL you can
+ * set this to zero to let the implementation decide
+ * @param n the maximum number of bytes that shall be copied.
+ * If this is larger than \p bufsize, the content is copied over multiple
+ * iterations.
+ * @return the total number of bytes copied
+ */
+__attribute__((__nonnull__(1, 2, 3, 4)))
+size_t cx_stream_bncopy(
+ void *src,
+ void *dest,
+ cx_read_func rfnc,
+ cx_write_func wfnc,
+ char *buf,
+ size_t bufsize,
+ size_t n
+);
+
+/**
+ * Reads data from a stream and writes it to another stream.
+ *
+ * @param src the source stream
+ * @param dest the destination stream
+ * @param rfnc the read function
+ * @param wfnc the write function
+ * @param buf a pointer to the copy buffer or \c NULL if a buffer
+ * shall be implicitly created on the heap
+ * @param bufsize the size of the copy buffer - if \p buf is \c NULL you can
+ * set this to zero to let the implementation decide
+ * @return total number of bytes copied
+ */
+#define cx_stream_bcopy(src, dest, rfnc, wfnc, buf, bufsize) \
+ cx_stream_bncopy(src, dest, rfnc, wfnc, buf, bufsize, SIZE_MAX)
+
+/**
+ * Reads data from a stream and writes it to another stream.
+ *
+ * The data is temporarily stored in a stack allocated buffer.
+ *
+ * @param src the source stream
+ * @param dest the destination stream
+ * @param rfnc the read function
+ * @param wfnc the write function
+ * @param n the maximum number of bytes that shall be copied.
+ * @return total number of bytes copied
+ */
+__attribute__((__nonnull__))
+size_t cx_stream_ncopy(
+ void *src,
+ void *dest,
+ cx_read_func rfnc,
+ cx_write_func wfnc,
+ size_t n
+);
+
+/**
+ * Reads data from a stream and writes it to another stream.
+ *
+ * The data is temporarily stored in a stack allocated buffer.
+ *
+ * @param src the source stream
+ * @param dest the destination stream
+ * @param rfnc the read function
+ * @param wfnc the write function
+ * @param n the maximum number of bytes that shall be copied.
+ * @return total number of bytes copied
+ */
+#define cx_stream_copy(src, dest, rfnc, wfnc) \
+ cx_stream_ncopy(src, dest, rfnc, wfnc, SIZE_MAX)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // UCX_UTILS_H
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "cx/hash_key.h"
+#include <string.h>
+
+void cx_hash_murmur(CxHashKey *key) {
+ unsigned char const *data = key->data.cbytes;
+ if (data == NULL) {
+ // extension: special value for NULL
+ key->hash = 1574210520u;
+ return;
+ }
+ size_t len = key->len;
+
+ unsigned m = 0x5bd1e995;
+ unsigned r = 24;
+ unsigned h = 25 ^ len;
+ unsigned i = 0;
+ while (len >= 4) {
+ unsigned k = data[i + 0] & 0xFF;
+ k |= (data[i + 1] & 0xFF) << 8;
+ k |= (data[i + 2] & 0xFF) << 16;
+ k |= (data[i + 3] & 0xFF) << 24;
+
+ k *= m;
+ k ^= k >> r;
+ k *= m;
+
+ h *= m;
+ h ^= k;
+
+ i += 4;
+ len -= 4;
+ }
+
+ switch (len) {
+ case 3:
+ h ^= (data[i + 2] & 0xFF) << 16;
+ __attribute__((__fallthrough__));
+ case 2:
+ h ^= (data[i + 1] & 0xFF) << 8;
+ __attribute__((__fallthrough__));
+ case 1:
+ h ^= (data[i + 0] & 0xFF);
+ h *= m;
+ __attribute__((__fallthrough__));
+ default: // do nothing
+ ;
+ }
+
+ h ^= h >> 13;
+ h *= m;
+ h ^= h >> 15;
+
+ key->hash = h;
+}
+
+CxHashKey cx_hash_key_str(char const *str) {
+ CxHashKey key;
+ key.data.cstr = str;
+ key.len = str == NULL ? 0 : strlen(str);
+ cx_hash_murmur(&key);
+ return key;
+}
+
+CxHashKey cx_hash_key_bytes(
+ unsigned char const *bytes,
+ size_t len
+) {
+ CxHashKey key;
+ key.data.cbytes = bytes;
+ key.len = len;
+ cx_hash_murmur(&key);
+ return key;
+}
+
+CxHashKey cx_hash_key(
+ void const *obj,
+ size_t len
+) {
+ CxHashKey key;
+ key.data.cobj = obj;
+ key.len = len;
+ cx_hash_murmur(&key);
+ return key;
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include "cx/hash_map.h"
+#include "cx/utils.h"
+
+struct cx_hash_map_element_s {
+ /** A pointer to the next element in the current bucket. */
+ struct cx_hash_map_element_s *next;
+
+ /** The corresponding key. */
+ CxHashKey key;
+
+ /** The value data. */
+ char data[];
+};
+
+static void cx_hash_map_clear(struct cx_map_s *map) {
+ struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
+ cx_for_n(i, hash_map->bucket_count) {
+ struct cx_hash_map_element_s *elem = hash_map->buckets[i];
+ if (elem != NULL) {
+ do {
+ struct cx_hash_map_element_s *next = elem->next;
+ // free the key data
+ cxFree(map->allocator, elem->key.data.obj);
+ // free the node
+ cxFree(map->allocator, elem);
+ // proceed
+ elem = next;
+ } while (elem != NULL);
+
+ // do not leave a dangling pointer
+ hash_map->buckets[i] = NULL;
+ }
+ }
+ map->size = 0;
+}
+
+static void cx_hash_map_destructor(struct cx_map_s *map) {
+ struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
+
+ // free the buckets
+ cx_hash_map_clear(map);
+ cxFree(map->allocator, hash_map->buckets);
+
+ // free the map structure
+ cxFree(map->allocator, map);
+}
+
+static int cx_hash_map_put(
+ CxMap *map,
+ CxHashKey key,
+ void *value
+) {
+ struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
+ CxAllocator *allocator = map->allocator;
+
+ unsigned hash = key.hash;
+ if (hash == 0) {
+ cx_hash_murmur(&key);
+ hash = key.hash;
+ }
+
+ size_t slot = hash % hash_map->bucket_count;
+ struct cx_hash_map_element_s *elm = hash_map->buckets[slot];
+ struct cx_hash_map_element_s *prev = NULL;
+
+ while (elm != NULL && elm->key.hash < hash) {
+ prev = elm;
+ elm = elm->next;
+ }
+
+ if (elm != NULL && elm->key.hash == hash && elm->key.len == key.len &&
+ memcmp(elm->key.data.obj, key.data.obj, key.len) == 0) {
+ // overwrite existing element
+ if (map->store_pointers) {
+ memcpy(elm->data, &value, sizeof(void *));
+ } else {
+ memcpy(elm->data, value, map->itemsize);
+ }
+ } else {
+ // allocate new element
+ struct cx_hash_map_element_s *e = cxMalloc(
+ allocator,
+ sizeof(struct cx_hash_map_element_s) + map->itemsize
+ );
+ if (e == NULL) {
+ return -1;
+ }
+
+ // write the value
+ if (map->store_pointers) {
+ memcpy(e->data, &value, sizeof(void *));
+ } else {
+ memcpy(e->data, value, map->itemsize);
+ }
+
+ // copy the key
+ void *kd = cxMalloc(allocator, key.len);
+ if (kd == NULL) {
+ return -1;
+ }
+ memcpy(kd, key.data.obj, key.len);
+ e->key.data.obj = kd;
+ e->key.len = key.len;
+ e->key.hash = hash;
+
+ // insert the element into the linked list
+ if (prev == NULL) {
+ hash_map->buckets[slot] = e;
+ } else {
+ prev->next = e;
+ }
+ e->next = elm;
+
+ // increase the size
+ map->size++;
+ }
+
+ return 0;
+}
+
+static void cx_hash_map_unlink(
+ struct cx_hash_map_s *hash_map,
+ size_t slot,
+ struct cx_hash_map_element_s *prev,
+ struct cx_hash_map_element_s *elm
+) {
+ // unlink
+ if (prev == NULL) {
+ hash_map->buckets[slot] = elm->next;
+ } else {
+ prev->next = elm->next;
+ }
+ // free element
+ cxFree(hash_map->base.allocator, elm->key.data.obj);
+ cxFree(hash_map->base.allocator, elm);
+ // decrease size
+ hash_map->base.size--;
+}
+
+/**
+ * Helper function to avoid code duplication.
+ *
+ * @param map the map
+ * @param key the key to look up
+ * @param remove flag indicating whether the looked up entry shall be removed
+ * @return a pointer to the value corresponding to the key or \c NULL
+ */
+static void *cx_hash_map_get_remove(
+ CxMap *map,
+ CxHashKey key,
+ bool remove
+) {
+ struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
+
+ unsigned hash = key.hash;
+ if (hash == 0) {
+ cx_hash_murmur(&key);
+ hash = key.hash;
+ }
+
+ size_t slot = hash % hash_map->bucket_count;
+ struct cx_hash_map_element_s *elm = hash_map->buckets[slot];
+ struct cx_hash_map_element_s *prev = NULL;
+ while (elm && elm->key.hash <= hash) {
+ if (elm->key.hash == hash && elm->key.len == key.len) {
+ if (memcmp(elm->key.data.obj, key.data.obj, key.len) == 0) {
+ void *data = NULL;
+ if (map->store_pointers) {
+ data = *(void **) elm->data;
+ } else if (!remove) {
+ data = elm->data;
+ }
+ if (remove) {
+ cx_hash_map_unlink(hash_map, slot, prev, elm);
+ }
+ return data;
+ }
+ }
+ prev = elm;
+ elm = prev->next;
+ }
+
+ return NULL;
+}
+
+static void *cx_hash_map_get(
+ CxMap const *map,
+ CxHashKey key
+) {
+ // we can safely cast, because we know when remove=false, the map stays untouched
+ return cx_hash_map_get_remove((CxMap *) map, key, false);
+}
+
+static void *cx_hash_map_remove(
+ CxMap *map,
+ CxHashKey key
+) {
+ return cx_hash_map_get_remove(map, key, true);
+}
+
+static void *cx_hash_map_iter_current_entry(void const *it) {
+ struct cx_iterator_s const *iter = it;
+ // struct has to have a compatible signature
+ return (struct cx_map_entry_s *) &(iter->kv_data);
+}
+
+static void *cx_hash_map_iter_current_key(void const *it) {
+ struct cx_iterator_s const *iter = it;
+ struct cx_hash_map_element_s *elm = iter->elem_handle;
+ return &elm->key;
+}
+
+static void *cx_hash_map_iter_current_value(void const *it) {
+ struct cx_iterator_s const *iter = it;
+ struct cx_hash_map_s const *map = iter->src_handle;
+ struct cx_hash_map_element_s *elm = iter->elem_handle;
+ if (map->base.store_pointers) {
+ return *(void **) elm->data;
+ } else {
+ return elm->data;
+ }
+}
+
+static bool cx_hash_map_iter_valid(void const *it) {
+ struct cx_iterator_s const *iter = it;
+ return iter->elem_handle != NULL;
+}
+
+static void cx_hash_map_iter_next(void *it) {
+ struct cx_iterator_s *iter = it;
+ struct cx_hash_map_element_s *elm = iter->elem_handle;
+
+ // remove current element, if asked
+ if (iter->base.remove) {
+ // obtain mutable pointer to the map
+ struct cx_mut_iterator_s *miter = it;
+ struct cx_hash_map_s *map = miter->src_handle;
+
+ // clear the flag
+ iter->base.remove = false;
+
+ // determine the next element
+ struct cx_hash_map_element_s *next = elm->next;
+
+ // search the previous element
+ struct cx_hash_map_element_s *prev = NULL;
+ if (map->buckets[iter->slot] != elm) {
+ prev = map->buckets[iter->slot];
+ while (prev->next != elm) {
+ prev = prev->next;
+ }
+ }
+
+ // unlink
+ cx_hash_map_unlink(map, iter->slot, prev, elm);
+
+ // advance
+ elm = next;
+ } else {
+ // just advance
+ elm = elm->next;
+ iter->index++;
+ }
+
+ // search the next bucket, if required
+ struct cx_hash_map_s const *map = iter->src_handle;
+ while (elm == NULL && ++iter->slot < map->bucket_count) {
+ elm = map->buckets[iter->slot];
+ }
+
+ // fill the struct with the next element
+ iter->elem_handle = elm;
+ if (elm == NULL) {
+ iter->kv_data.key = NULL;
+ iter->kv_data.value = NULL;
+ } else {
+ iter->kv_data.key = &elm->key;
+ if (map->base.store_pointers) {
+ iter->kv_data.value = *(void **) elm->data;
+ } else {
+ iter->kv_data.value = elm->data;
+ }
+ }
+}
+
+static bool cx_hash_map_iter_flag_rm(void *it) {
+ struct cx_iterator_base_s *iter = it;
+ if (iter->mutating) {
+ iter->remove = true;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static CxIterator cx_hash_map_iterator(CxMap const *map) {
+ CxIterator iter;
+
+ iter.src_handle = map;
+ iter.base.valid = cx_hash_map_iter_valid;
+ iter.base.next = cx_hash_map_iter_next;
+ iter.base.current = cx_hash_map_iter_current_entry;
+ iter.base.flag_removal = cx_hash_map_iter_flag_rm;
+ iter.base.remove = false;
+ iter.base.mutating = false;
+
+ iter.slot = 0;
+ iter.index = 0;
+
+ if (map->size > 0) {
+ struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
+ struct cx_hash_map_element_s *elm = hash_map->buckets[0];
+ while (elm == NULL) {
+ elm = hash_map->buckets[++iter.slot];
+ }
+ iter.elem_handle = elm;
+ iter.kv_data.key = &elm->key;
+ if (map->store_pointers) {
+ iter.kv_data.value = *(void **) elm->data;
+ } else {
+ iter.kv_data.value = elm->data;
+ }
+ } else {
+ iter.elem_handle = NULL;
+ iter.kv_data.key = NULL;
+ iter.kv_data.value = NULL;
+ }
+
+ return iter;
+}
+
+static CxIterator cx_hash_map_iterator_keys(CxMap const *map) {
+ CxIterator iter = cx_hash_map_iterator(map);
+ iter.base.current = cx_hash_map_iter_current_key;
+ return iter;
+}
+
+static CxIterator cx_hash_map_iterator_values(CxMap const *map) {
+ CxIterator iter = cx_hash_map_iterator(map);
+ iter.base.current = cx_hash_map_iter_current_value;
+ return iter;
+}
+
+static CxMutIterator cx_hash_map_mut_iterator(CxMap *map) {
+ CxIterator it = cx_hash_map_iterator(map);
+ it.base.mutating = true;
+
+ // we know the iterators share the same memory layout
+ CxMutIterator iter;
+ memcpy(&iter, &it, sizeof(CxMutIterator));
+ return iter;
+}
+
+static CxMutIterator cx_hash_map_mut_iterator_keys(CxMap *map) {
+ CxMutIterator iter = cx_hash_map_mut_iterator(map);
+ iter.base.current = cx_hash_map_iter_current_key;
+ return iter;
+}
+
+static CxMutIterator cx_hash_map_mut_iterator_values(CxMap *map) {
+ CxMutIterator iter = cx_hash_map_mut_iterator(map);
+ iter.base.current = cx_hash_map_iter_current_value;
+ return iter;
+}
+
+static cx_map_class cx_hash_map_class = {
+ cx_hash_map_destructor,
+ cx_hash_map_clear,
+ cx_hash_map_put,
+ cx_hash_map_get,
+ cx_hash_map_remove,
+ cx_hash_map_iterator,
+ cx_hash_map_iterator_keys,
+ cx_hash_map_iterator_values,
+ cx_hash_map_mut_iterator,
+ cx_hash_map_mut_iterator_keys,
+ cx_hash_map_mut_iterator_values,
+};
+
+CxMap *cxHashMapCreate(
+ CxAllocator *allocator,
+ size_t itemsize,
+ size_t buckets
+) {
+ if (buckets == 0) {
+ // implementation defined default
+ buckets = 16;
+ }
+
+ struct cx_hash_map_s *map = cxMalloc(allocator, sizeof(struct cx_hash_map_s));
+ if (map == NULL) return NULL;
+
+ // initialize hash map members
+ map->bucket_count = buckets;
+ map->buckets = cxCalloc(allocator, buckets, sizeof(struct cx_hash_map_element_s *));
+ if (map->buckets == NULL) {
+ cxFree(allocator, map);
+ return NULL;
+ }
+
+ // initialize base members
+ map->base.cl = &cx_hash_map_class;
+ map->base.allocator = allocator;
+ map->base.size = 0;
+
+ if (itemsize > 0) {
+ map->base.store_pointers = false;
+ map->base.itemsize = itemsize;
+ } else {
+ map->base.store_pointers = true;
+ map->base.itemsize = sizeof(void *);
+ }
+
+ return (CxMap *) map;
+}
+
+int cxMapRehash(CxMap *map) {
+ struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
+ if (map->size > ((hash_map->bucket_count * 3) >> 2)) {
+
+ size_t new_bucket_count = (map->size * 5) >> 1;
+ struct cx_hash_map_element_s **new_buckets = cxCalloc(map->allocator,
+ new_bucket_count, sizeof(struct cx_hash_map_element_s *));
+
+ if (new_buckets == NULL) {
+ return 1;
+ }
+
+ // iterate through the elements and assign them to their new slots
+ cx_for_n(slot, hash_map->bucket_count) {
+ struct cx_hash_map_element_s *elm = hash_map->buckets[slot];
+ while (elm != NULL) {
+ struct cx_hash_map_element_s *next = elm->next;
+ size_t new_slot = elm->key.hash % new_bucket_count;
+
+ // find position where to insert
+ struct cx_hash_map_element_s *bucket_next = new_buckets[new_slot];
+ struct cx_hash_map_element_s *bucket_prev = NULL;
+ while (bucket_next != NULL && bucket_next->key.hash < elm->key.hash) {
+ bucket_prev = bucket_next;
+ bucket_next = bucket_next->next;
+ }
+
+ // insert
+ if (bucket_prev == NULL) {
+ elm->next = new_buckets[new_slot];
+ new_buckets[new_slot] = elm;
+ } else {
+ bucket_prev->next = elm;
+ elm->next = bucket_next;
+ }
+
+ // advance
+ elm = next;
+ }
+ }
+
+ // assign result to the map
+ hash_map->bucket_count = new_bucket_count;
+ cxFree(map->allocator, hash_map->buckets);
+ hash_map->buckets = new_buckets;
+ }
+ return 0;
+}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "cx/linked_list.h"
+#include "cx/utils.h"
+#include <string.h>
+#include <assert.h>
+
+// LOW LEVEL LINKED LIST FUNCTIONS
+
+#define CX_LL_PTR(cur, off) (*(void**)(((char*)(cur))+(off)))
+#define ll_prev(node) CX_LL_PTR(node, loc_prev)
+#define ll_next(node) CX_LL_PTR(node, loc_next)
+#define ll_advance(node) CX_LL_PTR(node, loc_advance)
+#define ll_data(node) (((char*)(node))+loc_data)
+
+void *cx_linked_list_at(
+ void const *start,
+ size_t start_index,
+ ptrdiff_t loc_advance,
+ size_t index
+) {
+ assert(start != NULL);
+ assert(loc_advance >= 0);
+ size_t i = start_index;
+ void const *cur = start;
+ while (i != index && cur != NULL) {
+ cur = ll_advance(cur);
+ i < index ? i++ : i--;
+ }
+ return (void *) cur;
+}
+
+size_t cx_linked_list_find(
+ void const *start,
+ ptrdiff_t loc_advance,
+ ptrdiff_t loc_data,
+ CxListComparator cmp_func,
+ void const *elem
+) {
+ assert(start != NULL);
+ assert(loc_advance >= 0);
+ assert(loc_data >= 0);
+ assert(cmp_func);
+
+ void const *node = start;
+ size_t index = 0;
+ do {
+ void *current = ll_data(node);
+ if (cmp_func(current, elem) == 0) {
+ return index;
+ }
+ node = ll_advance(node);
+ index++;
+ } while (node != NULL);
+ return index;
+}
+
+void *cx_linked_list_first(
+ void const *node,
+ ptrdiff_t loc_prev
+) {
+ return cx_linked_list_last(node, loc_prev);
+}
+
+void *cx_linked_list_last(
+ void const *node,
+ ptrdiff_t loc_next
+) {
+ assert(node != NULL);
+ assert(loc_next >= 0);
+
+ void const *cur = node;
+ void const *last;
+ do {
+ last = cur;
+ } while ((cur = ll_next(cur)) != NULL);
+
+ return (void *) last;
+}
+
+void *cx_linked_list_prev(
+ void const *begin,
+ ptrdiff_t loc_next,
+ void const *node
+) {
+ assert(begin != NULL);
+ assert(node != NULL);
+ assert(loc_next >= 0);
+ if (begin == node) return NULL;
+ void const *cur = begin;
+ void const *next;
+ while (1) {
+ next = ll_next(cur);
+ if (next == node) return (void *) cur;
+ cur = next;
+ }
+}
+
+void cx_linked_list_link(
+ void *left,
+ void *right,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next
+) {
+ assert(loc_next >= 0);
+ ll_next(left) = right;
+ if (loc_prev >= 0) {
+ ll_prev(right) = left;
+ }
+}
+
+void cx_linked_list_unlink(
+ void *left,
+ void *right,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next
+) {
+ assert (loc_next >= 0);
+ assert(ll_next(left) == right);
+ ll_next(left) = NULL;
+ if (loc_prev >= 0) {
+ assert(ll_prev(right) == left);
+ ll_prev(right) = NULL;
+ }
+}
+
+void cx_linked_list_add(
+ void **begin,
+ void **end,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next,
+ void *new_node
+) {
+ void *last;
+ if (end == NULL) {
+ assert(begin != NULL);
+ last = *begin == NULL ? NULL : cx_linked_list_last(*begin, loc_next);
+ } else {
+ last = *end;
+ }
+ cx_linked_list_insert_chain(begin, end, loc_prev, loc_next, last, new_node, new_node);
+}
+
+void cx_linked_list_prepend(
+ void **begin,
+ void **end,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next,
+ void *new_node
+) {
+ cx_linked_list_insert_chain(begin, end, loc_prev, loc_next, NULL, new_node, new_node);
+}
+
+void cx_linked_list_insert(
+ void **begin,
+ void **end,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next,
+ void *node,
+ void *new_node
+) {
+ cx_linked_list_insert_chain(begin, end, loc_prev, loc_next, node, new_node, new_node);
+}
+
+void cx_linked_list_insert_chain(
+ void **begin,
+ void **end,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next,
+ void *node,
+ void *insert_begin,
+ void *insert_end
+) {
+ // find the end of the chain, if not specified
+ if (insert_end == NULL) {
+ insert_end = cx_linked_list_last(insert_begin, loc_next);
+ }
+
+ // determine the successor
+ void *successor;
+ if (node == NULL) {
+ assert(begin != NULL || (end != NULL && loc_prev >= 0));
+ if (begin != NULL) {
+ successor = *begin;
+ *begin = insert_begin;
+ } else {
+ successor = *end == NULL ? NULL : cx_linked_list_first(*end, loc_prev);
+ }
+ } else {
+ successor = ll_next(node);
+ cx_linked_list_link(node, insert_begin, loc_prev, loc_next);
+ }
+
+ if (successor == NULL) {
+ // the list ends with the new chain
+ if (end != NULL) {
+ *end = insert_end;
+ }
+ } else {
+ cx_linked_list_link(insert_end, successor, loc_prev, loc_next);
+ }
+}
+
+void cx_linked_list_remove(
+ void **begin,
+ void **end,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next,
+ void *node
+) {
+ assert(node != NULL);
+ assert(loc_next >= 0);
+ assert(loc_prev >= 0 || begin != NULL);
+
+ // find adjacent nodes
+ void *next = ll_next(node);
+ void *prev;
+ if (loc_prev >= 0) {
+ prev = ll_prev(node);
+ } else {
+ prev = cx_linked_list_prev(*begin, loc_next, node);
+ }
+
+ // update next pointer of prev node, or set begin
+ if (prev == NULL) {
+ if (begin != NULL) {
+ *begin = next;
+ }
+ } else {
+ ll_next(prev) = next;
+ }
+
+ // update prev pointer of next node, or set end
+ if (next == NULL) {
+ if (end != NULL) {
+ *end = prev;
+ }
+ } else if (loc_prev >= 0) {
+ ll_prev(next) = prev;
+ }
+}
+
+size_t cx_linked_list_size(
+ void const *node,
+ ptrdiff_t loc_next
+) {
+ assert(loc_next >= 0);
+ size_t size = 0;
+ while (node != NULL) {
+ node = ll_next(node);
+ size++;
+ }
+ return size;
+}
+
+#ifndef CX_LINKED_LIST_SORT_SBO_SIZE
+#define CX_LINKED_LIST_SORT_SBO_SIZE 1024
+#endif
+
+static void *cx_linked_list_sort_merge(
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next,
+ ptrdiff_t loc_data,
+ size_t length,
+ void *ls,
+ void *le,
+ void *re,
+ CxListComparator cmp_func
+) {
+ void *sbo[CX_LINKED_LIST_SORT_SBO_SIZE];
+ void **sorted = length >= CX_LINKED_LIST_SORT_SBO_SIZE ?
+ malloc(sizeof(void *) * length) : sbo;
+ if (sorted == NULL) abort();
+ void *rc, *lc;
+
+ lc = ls;
+ rc = le;
+ size_t n = 0;
+ while (lc && lc != le && rc != re) {
+ if (cmp_func(ll_data(lc), ll_data(rc)) <= 0) {
+ sorted[n] = lc;
+ lc = ll_next(lc);
+ } else {
+ sorted[n] = rc;
+ rc = ll_next(rc);
+ }
+ n++;
+ }
+ while (lc && lc != le) {
+ sorted[n] = lc;
+ lc = ll_next(lc);
+ n++;
+ }
+ while (rc && rc != re) {
+ sorted[n] = rc;
+ rc = ll_next(rc);
+ n++;
+ }
+
+ // Update pointer
+ if (loc_prev >= 0) ll_prev(sorted[0]) = NULL;
+ cx_for_n (i, length - 1) {
+ cx_linked_list_link(sorted[i], sorted[i + 1], loc_prev, loc_next);
+ }
+ ll_next(sorted[length - 1]) = NULL;
+
+ void *ret = sorted[0];
+ if (sorted != sbo) {
+ free(sorted);
+ }
+ return ret;
+}
+
+void cx_linked_list_sort( // NOLINT(misc-no-recursion) - purposely recursive function
+ void **begin,
+ void **end,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next,
+ ptrdiff_t loc_data,
+ CxListComparator cmp_func
+) {
+ assert(begin != NULL);
+ assert(loc_next >= 0);
+ assert(loc_data >= 0);
+ assert(cmp_func);
+
+ void *lc, *ls, *le, *re;
+
+ // set start node
+ ls = *begin;
+
+ // check how many elements are already sorted
+ lc = ls;
+ size_t ln = 1;
+ while (ll_next(lc) != NULL && cmp_func(ll_data(ll_next(lc)), ll_data(lc)) > 0) {
+ lc = ll_next(lc);
+ ln++;
+ }
+ le = ll_next(lc);
+
+ // if first unsorted node is NULL, the list is already completely sorted
+ if (le != NULL) {
+ void *rc;
+ size_t rn = 1;
+ rc = le;
+ // skip already sorted elements
+ while (ll_next(rc) != NULL && cmp_func(ll_data(ll_next(rc)), ll_data(rc)) > 0) {
+ rc = ll_next(rc);
+ rn++;
+ }
+ re = ll_next(rc);
+
+ // {ls,...,le->prev} and {rs,...,re->prev} are sorted - merge them
+ void *sorted = cx_linked_list_sort_merge(loc_prev, loc_next, loc_data,
+ ln + rn, ls, le, re, cmp_func);
+
+ // Something left? Sort it!
+ size_t remainder_length = cx_linked_list_size(re, loc_next);
+ if (remainder_length > 0) {
+ void *remainder = re;
+ cx_linked_list_sort(&remainder, NULL, loc_prev, loc_next, loc_data, cmp_func);
+
+ // merge sorted list with (also sorted) remainder
+ *begin = cx_linked_list_sort_merge(loc_prev, loc_next, loc_data,
+ ln + rn + remainder_length,
+ sorted, remainder, NULL, cmp_func);
+ } else {
+ // no remainder - we've got our sorted list
+ *begin = sorted;
+ }
+ if (end) *end = cx_linked_list_last(sorted, loc_next);
+ }
+}
+
+int cx_linked_list_compare(
+ void const *begin_left,
+ void const *begin_right,
+ ptrdiff_t loc_advance,
+ ptrdiff_t loc_data,
+ CxListComparator cmp_func
+) {
+ void const *left = begin_left, *right = begin_right;
+
+ while (left != NULL && right != NULL) {
+ void const *left_data = ll_data(left);
+ void const *right_data = ll_data(right);
+ int result = cmp_func(left_data, right_data);
+ if (result != 0) return result;
+ left = ll_advance(left);
+ right = ll_advance(right);
+ }
+
+ if (left != NULL) { return 1; }
+ else if (right != NULL) { return -1; }
+ else { return 0; }
+}
+
+void cx_linked_list_reverse(
+ void **begin,
+ void **end,
+ ptrdiff_t loc_prev,
+ ptrdiff_t loc_next
+) {
+ assert(begin != NULL);
+ assert(loc_next >= 0);
+
+ // swap all links
+ void *prev = NULL;
+ void *cur = *begin;
+ while (cur != NULL) {
+ void *next = ll_next(cur);
+
+ ll_next(cur) = prev;
+ if (loc_prev >= 0) {
+ ll_prev(cur) = next;
+ }
+
+ prev = cur;
+ cur = next;
+ }
+
+ // update begin and end
+ if (end != NULL) {
+ *end = *begin;
+ }
+ *begin = prev;
+}
+
+// HIGH LEVEL LINKED LIST IMPLEMENTATION
+
+bool CX_DISABLE_LINKED_LIST_SWAP_SBO = false;
+
+typedef struct cx_linked_list_node cx_linked_list_node;
+struct cx_linked_list_node {
+ cx_linked_list_node *prev;
+ cx_linked_list_node *next;
+ char payload[];
+};
+
+#define CX_LL_LOC_PREV offsetof(cx_linked_list_node, prev)
+#define CX_LL_LOC_NEXT offsetof(cx_linked_list_node, next)
+#define CX_LL_LOC_DATA offsetof(cx_linked_list_node, payload)
+
+typedef struct {
+ struct cx_list_s base;
+ cx_linked_list_node *begin;
+ cx_linked_list_node *end;
+} cx_linked_list;
+
+static cx_linked_list_node *cx_ll_node_at(
+ cx_linked_list const *list,
+ size_t index
+) {
+ if (index >= list->base.size) {
+ return NULL;
+ } else if (index > list->base.size / 2) {
+ return cx_linked_list_at(list->end, list->base.size - 1, CX_LL_LOC_PREV, index);
+ } else {
+ return cx_linked_list_at(list->begin, 0, CX_LL_LOC_NEXT, index);
+ }
+}
+
+static int cx_ll_insert_at(
+ struct cx_list_s *list,
+ cx_linked_list_node *node,
+ void const *elem
+) {
+
+ // create the new new_node
+ cx_linked_list_node *new_node = cxMalloc(list->allocator,
+ sizeof(cx_linked_list_node) + list->itemsize);
+
+ // sortir if failed
+ if (new_node == NULL) return 1;
+
+ // initialize new new_node
+ new_node->prev = new_node->next = NULL;
+ memcpy(new_node->payload, elem, list->itemsize);
+
+ // insert
+ cx_linked_list *ll = (cx_linked_list *) list;
+ cx_linked_list_insert_chain(
+ (void **) &ll->begin, (void **) &ll->end,
+ CX_LL_LOC_PREV, CX_LL_LOC_NEXT,
+ node, new_node, new_node
+ );
+
+ // increase the size and return
+ list->size++;
+ return 0;
+}
+
+static size_t cx_ll_insert_array(
+ struct cx_list_s *list,
+ size_t index,
+ void const *array,
+ size_t n
+) {
+ // out-of bounds and corner case check
+ if (index > list->size || n == 0) return 0;
+
+ // find position efficiently
+ cx_linked_list_node *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1);
+
+ // perform first insert
+ if (0 != cx_ll_insert_at(list, node, array)) {
+ return 1;
+ }
+
+ // is there more?
+ if (n == 1) return 1;
+
+ // we now know exactly where we are
+ node = node == NULL ? ((cx_linked_list *) list)->begin : node->next;
+
+ // we can add the remaining nodes and immedately advance to the inserted node
+ char const *source = array;
+ for (size_t i = 1; i < n; i++) {
+ source += list->itemsize;
+ if (0 != cx_ll_insert_at(list, node, source)) {
+ return i;
+ }
+ node = node->next;
+ }
+ return n;
+}
+
+static int cx_ll_insert_element(
+ struct cx_list_s *list,
+ size_t index,
+ void const *element
+) {
+ return 1 != cx_ll_insert_array(list, index, element, 1);
+}
+
+static int cx_ll_remove(
+ struct cx_list_s *list,
+ size_t index
+) {
+ cx_linked_list *ll = (cx_linked_list *) list;
+ cx_linked_list_node *node = cx_ll_node_at(ll, index);
+
+ // out-of-bounds check
+ if (node == NULL) return 1;
+
+ // element destruction
+ if (list->content_destructor_type != CX_DESTRUCTOR_NONE) {
+ cx_list_invoke_destructor(list, node->payload);
+ }
+
+ // remove
+ cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end,
+ CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node);
+
+ // adjust size
+ list->size--;
+
+ // free and return
+ cxFree(list->allocator, node);
+
+ return 0;
+}
+
+static void cx_ll_clear(struct cx_list_s *list) {
+ if (list->size == 0) return;
+
+ cx_linked_list *ll = (cx_linked_list *) list;
+ cx_linked_list_node *node = ll->begin;
+
+ // looks super redundant, but avoids repeatedly checking
+ // the destructor type for each element
+ switch (list->content_destructor_type) {
+ case CX_DESTRUCTOR_SIMPLE: {
+ while (node != NULL) {
+ cx_list_invoke_simple_destructor(list, node->payload);
+ cx_linked_list_node *next = node->next;
+ cxFree(list->allocator, node);
+ node = next;
+ }
+ break;
+ }
+ case CX_DESTRUCTOR_ADVANCED: {
+ while (node != NULL) {
+ cx_list_invoke_advanced_destructor(list, node->payload);
+ cx_linked_list_node *next = node->next;
+ cxFree(list->allocator, node);
+ node = next;
+ }
+ break;
+ }
+ case CX_DESTRUCTOR_NONE: {
+ while (node != NULL) {
+ cx_linked_list_node *next = node->next;
+ cxFree(list->allocator, node);
+ node = next;
+ }
+ break;
+ }
+ }
+
+ ll->begin = ll->end = NULL;
+ list->size = 0;
+}
+
+#ifndef CX_LINKED_LIST_SWAP_SBO_SIZE
+#define CX_LINKED_LIST_SWAP_SBO_SIZE 16
+#endif
+
+static int cx_ll_swap(
+ struct cx_list_s *list,
+ size_t i,
+ size_t j
+) {
+ if (i >= list->size || j >= list->size) return 1;
+ if (i == j) return 0;
+
+ // perform an optimized search that finds both elements in one run
+ cx_linked_list *ll = (cx_linked_list *) list;
+ size_t mid = list->size / 2;
+ size_t left, right;
+ if (i < j) {
+ left = i;
+ right = j;
+ } else {
+ left = j;
+ right = i;
+ }
+ cx_linked_list_node *nleft, *nright;
+ if (left < mid && right < mid) {
+ // case 1: both items left from mid
+ nleft = cx_ll_node_at(ll, left);
+ nright = nleft;
+ for (size_t c = left; c < right; c++) {
+ nright = nright->next;
+ }
+ } else if (left >= mid && right >= mid) {
+ // case 2: both items right from mid
+ nright = cx_ll_node_at(ll, right);
+ nleft = nright;
+ for (size_t c = right; c > left; c--) {
+ nleft = nleft->prev;
+ }
+ } else {
+ // case 3: one item left, one item right
+
+ // chose the closest to begin / end
+ size_t closest;
+ size_t other;
+ size_t diff2boundary = list->size - right - 1;
+ if (left <= diff2boundary) {
+ closest = left;
+ other = right;
+ nleft = cx_ll_node_at(ll, left);
+ } else {
+ closest = right;
+ other = left;
+ diff2boundary = left;
+ nright = cx_ll_node_at(ll, right);
+ }
+
+ // is other element closer to us or closer to boundary?
+ if (right - left <= diff2boundary) {
+ // search other element starting from already found element
+ if (closest == left) {
+ nright = nleft;
+ for (size_t c = left; c < right; c++) {
+ nright = nright->next;
+ }
+ } else {
+ nleft = nright;
+ for (size_t c = right; c > left; c--) {
+ nleft = nleft->prev;
+ }
+ }
+ } else {
+ // search other element starting at the boundary
+ if (closest == left) {
+ nright = cx_ll_node_at(ll, other);
+ } else {
+ nleft = cx_ll_node_at(ll, other);
+ }
+ }
+ }
+
+ if (list->itemsize > CX_LINKED_LIST_SWAP_SBO_SIZE || CX_DISABLE_LINKED_LIST_SWAP_SBO) {
+ cx_linked_list_node *prev = nleft->prev;
+ cx_linked_list_node *next = nright->next;
+ cx_linked_list_node *midstart = nleft->next;
+ cx_linked_list_node *midend = nright->prev;
+
+ if (prev == NULL) {
+ ll->begin = nright;
+ } else {
+ prev->next = nright;
+ }
+ nright->prev = prev;
+ if (midstart == nright) {
+ // special case: both nodes are adjacent
+ nright->next = nleft;
+ nleft->prev = nright;
+ } else {
+ // likely case: a chain is between the two nodes
+ nright->next = midstart;
+ midstart->prev = nright;
+ midend->next = nleft;
+ nleft->prev = midend;
+ }
+ nleft->next = next;
+ if (next == NULL) {
+ ll->end = nleft;
+ } else {
+ next->prev = nleft;
+ }
+ } else {
+ // swap payloads to avoid relinking
+ char buf[CX_LINKED_LIST_SWAP_SBO_SIZE];
+ memcpy(buf, nleft->payload, list->itemsize);
+ memcpy(nleft->payload, nright->payload, list->itemsize);
+ memcpy(nright->payload, buf, list->itemsize);
+ }
+
+ return 0;
+}
+
+static void *cx_ll_at(
+ struct cx_list_s const *list,
+ size_t index
+) {
+ cx_linked_list *ll = (cx_linked_list *) list;
+ cx_linked_list_node *node = cx_ll_node_at(ll, index);
+ return node == NULL ? NULL : node->payload;
+}
+
+static size_t cx_ll_find(
+ struct cx_list_s const *list,
+ void const *elem
+) {
+ return cx_linked_list_find(((cx_linked_list *) list)->begin,
+ CX_LL_LOC_NEXT, CX_LL_LOC_DATA,
+ list->cmpfunc, elem);
+}
+
+static void cx_ll_sort(struct cx_list_s *list) {
+ cx_linked_list *ll = (cx_linked_list *) list;
+ cx_linked_list_sort((void **) &ll->begin, (void **) &ll->end,
+ CX_LL_LOC_PREV, CX_LL_LOC_NEXT, CX_LL_LOC_DATA,
+ list->cmpfunc);
+}
+
+static void cx_ll_reverse(struct cx_list_s *list) {
+ cx_linked_list *ll = (cx_linked_list *) list;
+ cx_linked_list_reverse((void **) &ll->begin, (void **) &ll->end, CX_LL_LOC_PREV, CX_LL_LOC_NEXT);
+}
+
+static int cx_ll_compare(
+ struct cx_list_s const *list,
+ struct cx_list_s const *other
+) {
+ cx_linked_list *left = (cx_linked_list *) list;
+ cx_linked_list *right = (cx_linked_list *) other;
+ return cx_linked_list_compare(left->begin, right->begin,
+ CX_LL_LOC_NEXT, CX_LL_LOC_DATA,
+ list->cmpfunc);
+}
+
+static bool cx_ll_iter_valid(void const *it) {
+ struct cx_iterator_s const *iter = it;
+ return iter->elem_handle != NULL;
+}
+
+static void cx_ll_iter_next(void *it) {
+ struct cx_iterator_base_s *itbase = it;
+ if (itbase->remove) {
+ itbase->remove = false;
+ struct cx_mut_iterator_s *iter = it;
+ struct cx_list_s *list = iter->src_handle;
+ cx_linked_list *ll = iter->src_handle;
+ cx_linked_list_node *node = iter->elem_handle;
+ iter->elem_handle = node->next;
+ if (list->content_destructor_type != CX_DESTRUCTOR_NONE) {
+ cx_list_invoke_destructor(list, node->payload);
+ }
+ cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end,
+ CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node);
+ list->size--;
+ cxFree(list->allocator, node);
+ } else {
+ struct cx_iterator_s *iter = it;
+ iter->index++;
+ cx_linked_list_node *node = iter->elem_handle;
+ iter->elem_handle = node->next;
+ }
+}
+
+static void cx_ll_iter_prev(void *it) {
+ struct cx_iterator_base_s *itbase = it;
+ if (itbase->remove) {
+ itbase->remove = false;
+ struct cx_mut_iterator_s *iter = it;
+ struct cx_list_s *list = iter->src_handle;
+ cx_linked_list *ll = iter->src_handle;
+ cx_linked_list_node *node = iter->elem_handle;
+ iter->elem_handle = node->prev;
+ iter->index--;
+ if (list->content_destructor_type != CX_DESTRUCTOR_NONE) {
+ cx_list_invoke_destructor(list, node->payload);
+ }
+ cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end,
+ CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node);
+ list->size--;
+ cxFree(list->allocator, node);
+ } else {
+ struct cx_iterator_s *iter = it;
+ iter->index--;
+ cx_linked_list_node *node = iter->elem_handle;
+ iter->elem_handle = node->prev;
+ }
+}
+
+static void *cx_ll_iter_current(void const *it) {
+ struct cx_iterator_s const *iter = it;
+ cx_linked_list_node *node = iter->elem_handle;
+ return node->payload;
+}
+
+static bool cx_ll_iter_flag_rm(void *it) {
+ struct cx_iterator_base_s *iter = it;
+ if (iter->mutating) {
+ iter->remove = true;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static CxIterator cx_ll_iterator(
+ struct cx_list_s const *list,
+ size_t index,
+ bool backwards
+) {
+ CxIterator iter;
+ iter.index = index;
+ iter.src_handle = list;
+ iter.elem_handle = cx_ll_node_at((cx_linked_list const *) list, index);
+ iter.base.valid = cx_ll_iter_valid;
+ iter.base.current = cx_ll_iter_current;
+ iter.base.next = backwards ? cx_ll_iter_prev : cx_ll_iter_next;
+ iter.base.flag_removal = cx_ll_iter_flag_rm;
+ iter.base.mutating = false;
+ iter.base.remove = false;
+ return iter;
+}
+
+static int cx_ll_insert_iter(
+ CxMutIterator *iter,
+ void const *elem,
+ int prepend
+) {
+ struct cx_list_s *list = iter->src_handle;
+ cx_linked_list_node *node = iter->elem_handle;
+ if (node != NULL) {
+ assert(prepend >= 0 && prepend <= 1);
+ cx_linked_list_node *choice[2] = {node, node->prev};
+ int result = cx_ll_insert_at(list, choice[prepend], elem);
+ iter->index += prepend * (0 == result);
+ return result;
+ } else {
+ int result = cx_ll_insert_element(list, list->size, elem);
+ iter->index = list->size;
+ return result;
+ }
+}
+
+static void cx_ll_destructor(CxList *list) {
+ cx_linked_list *ll = (cx_linked_list *) list;
+
+ cx_linked_list_node *node = ll->begin;
+ while (node) {
+ void *next = node->next;
+ cxFree(list->allocator, node);
+ node = next;
+ }
+ // do not free the list pointer, this is just a destructor!
+}
+
+static cx_list_class cx_linked_list_class = {
+ cx_ll_destructor,
+ cx_ll_insert_element,
+ cx_ll_insert_array,
+ cx_ll_insert_iter,
+ cx_ll_remove,
+ cx_ll_clear,
+ cx_ll_swap,
+ cx_ll_at,
+ cx_ll_find,
+ cx_ll_sort,
+ cx_ll_compare,
+ cx_ll_reverse,
+ cx_ll_iterator,
+};
+
+CxList *cxLinkedListCreate(
+ CxAllocator const *allocator,
+ CxListComparator comparator,
+ size_t item_size
+) {
+ if (allocator == NULL) {
+ allocator = cxDefaultAllocator;
+ }
+
+ cx_linked_list *list = cxCalloc(allocator, 1, sizeof(cx_linked_list));
+ if (list == NULL) return NULL;
+
+ list->base.cl = &cx_linked_list_class;
+ list->base.allocator = allocator;
+ list->base.cmpfunc = comparator;
+ list->base.capacity = SIZE_MAX;
+
+ if (item_size > 0) {
+ list->base.itemsize = item_size;
+ } else {
+ cxListStorePointers((CxList *) list);
+ }
+
+ return (CxList *) list;
+}
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* POSSIBILITY OF SUCH DAMAGE.
*/
-#include "ucx/list.h"
+#include "cx/list.h"
-UcxList *ucx_list_clone(const UcxList *l, copy_func fnc, void *data) {
- return ucx_list_clone_a(ucx_default_allocator(), l, fnc, data);
-}
+#include <string.h>
-UcxList *ucx_list_clone_a(UcxAllocator *alloc, const UcxList *l,
- copy_func fnc, void *data) {
- UcxList *ret = NULL;
- while (l) {
- if (fnc) {
- ret = ucx_list_append_a(alloc, ret, fnc(l->data, data));
- } else {
- ret = ucx_list_append_a(alloc, ret, l->data);
- }
- l = l->next;
- }
- return ret;
-}
+// <editor-fold desc="Store Pointers Functionality">
-int ucx_list_equals(const UcxList *l1, const UcxList *l2,
- cmp_func fnc, void* data) {
- if (l1 == l2) return 1;
-
- while (l1 != NULL && l2 != NULL) {
- if (fnc == NULL) {
- if (l1->data != l2->data) return 0;
- } else {
- if (fnc(l1->data, l2->data, data) != 0) return 0;
- }
- l1 = l1->next;
- l2 = l2->next;
- }
-
- return (l1 == NULL && l2 == NULL);
-}
+static _Thread_local CxListComparator cx_pl_cmpfunc_impl;
-void ucx_list_free(UcxList *l) {
- ucx_list_free_a(ucx_default_allocator(), l);
+static int cx_pl_cmpfunc(
+ void const *l,
+ void const *r
+) {
+ void *const *lptr = l;
+ void *const *rptr = r;
+ void const *left = lptr == NULL ? NULL : *lptr;
+ void const *right = rptr == NULL ? NULL : *rptr;
+ return cx_pl_cmpfunc_impl(left, right);
}
-void ucx_list_free_a(UcxAllocator *alloc, UcxList *l) {
- UcxList *e = l, *f;
- while (e != NULL) {
- f = e;
- e = e->next;
- alfree(alloc, f);
- }
+static void cx_pl_hack_cmpfunc(struct cx_list_s const *list) {
+ // cast away const - this is the hacky thing
+ struct cx_list_s *l = (struct cx_list_s *) list;
+ cx_pl_cmpfunc_impl = l->cmpfunc;
+ l->cmpfunc = cx_pl_cmpfunc;
}
-void ucx_list_free_content(UcxList* list, ucx_destructor destr) {
- if (!destr) destr = free;
- while (list != NULL) {
- destr(list->data);
- list = list->next;
- }
+static void cx_pl_unhack_cmpfunc(struct cx_list_s const *list) {
+ // cast away const - this is the hacky thing
+ struct cx_list_s *l = (struct cx_list_s *) list;
+ l->cmpfunc = cx_pl_cmpfunc_impl;
}
-UcxList *ucx_list_append(UcxList *l, void *data) {
- return ucx_list_append_a(ucx_default_allocator(), l, data);
+static void cx_pl_destructor(struct cx_list_s *list) {
+ list->climpl->destructor(list);
}
-UcxList *ucx_list_append_a(UcxAllocator *alloc, UcxList *l, void *data) {
- UcxList *nl = (UcxList*) almalloc(alloc, sizeof(UcxList));
- if (!nl) {
- return NULL;
- }
-
- nl->data = data;
- nl->next = NULL;
- if (l) {
- UcxList *t = ucx_list_last(l);
- t->next = nl;
- nl->prev = t;
- return l;
- } else {
- nl->prev = NULL;
- return nl;
- }
+static int cx_pl_insert_element(
+ struct cx_list_s *list,
+ size_t index,
+ void const *element
+) {
+ return list->climpl->insert_element(list, index, &element);
}
-UcxList *ucx_list_prepend(UcxList *l, void *data) {
- return ucx_list_prepend_a(ucx_default_allocator(), l, data);
+static size_t cx_pl_insert_array(
+ struct cx_list_s *list,
+ size_t index,
+ void const *array,
+ size_t n
+) {
+ return list->climpl->insert_array(list, index, array, n);
}
-UcxList *ucx_list_prepend_a(UcxAllocator *alloc, UcxList *l, void *data) {
- UcxList *nl = ucx_list_append_a(alloc, NULL, data);
- if (!nl) {
- return NULL;
- }
- l = ucx_list_first(l);
-
- if (l) {
- nl->next = l;
- l->prev = nl;
- }
- return nl;
+static int cx_pl_insert_iter(
+ struct cx_mut_iterator_s *iter,
+ void const *elem,
+ int prepend
+) {
+ struct cx_list_s *list = iter->src_handle;
+ return list->climpl->insert_iter(iter, &elem, prepend);
}
-UcxList *ucx_list_concat(UcxList *l1, UcxList *l2) {
- if (l1) {
- UcxList *last = ucx_list_last(l1);
- last->next = l2;
- if (l2) {
- l2->prev = last;
- }
- return l1;
- } else {
- return l2;
- }
+static int cx_pl_remove(
+ struct cx_list_s *list,
+ size_t index
+) {
+ return list->climpl->remove(list, index);
}
-UcxList *ucx_list_last(const UcxList *l) {
- if (l == NULL) return NULL;
-
- const UcxList *e = l;
- while (e->next != NULL) {
- e = e->next;
- }
- return (UcxList*)e;
+static void cx_pl_clear(struct cx_list_s *list) {
+ list->climpl->clear(list);
}
-ssize_t ucx_list_indexof(const UcxList *list, const UcxList *elem) {
- ssize_t index = 0;
- while (list) {
- if (list == elem) {
- return index;
- }
- list = list->next;
- index++;
- }
- return -1;
+static int cx_pl_swap(
+ struct cx_list_s *list,
+ size_t i,
+ size_t j
+) {
+ return list->climpl->swap(list, i, j);
}
-UcxList *ucx_list_get(const UcxList *l, size_t index) {
- if (l == NULL) return NULL;
-
- const UcxList *e = l;
- while (e->next && index > 0) {
- e = e->next;
- index--;
- }
-
- return (UcxList*)(index == 0 ? e : NULL);
+static void *cx_pl_at(
+ struct cx_list_s const *list,
+ size_t index
+) {
+ void **ptr = list->climpl->at(list, index);
+ return ptr == NULL ? NULL : *ptr;
}
-ssize_t ucx_list_find(const UcxList *l, void *elem,
- cmp_func fnc, void *cmpdata) {
- ssize_t index = 0;
- UCX_FOREACH(e, l) {
- if (fnc) {
- if (fnc(elem, e->data, cmpdata) == 0) {
- return index;
- }
- } else {
- if (elem == e->data) {
- return index;
- }
- }
- index++;
- }
- return -1;
+static size_t cx_pl_find(
+ struct cx_list_s const *list,
+ void const *elem
+) {
+ cx_pl_hack_cmpfunc(list);
+ size_t ret = list->climpl->find(list, &elem);
+ cx_pl_unhack_cmpfunc(list);
+ return ret;
}
-int ucx_list_contains(const UcxList *l, void *elem,
- cmp_func fnc, void *cmpdata) {
- return ucx_list_find(l, elem, fnc, cmpdata) > -1;
+static void cx_pl_sort(struct cx_list_s *list) {
+ cx_pl_hack_cmpfunc(list);
+ list->climpl->sort(list);
+ cx_pl_unhack_cmpfunc(list);
}
-size_t ucx_list_size(const UcxList *l) {
- if (l == NULL) return 0;
-
- const UcxList *e = l;
- size_t s = 1;
- while (e->next != NULL) {
- e = e->next;
- s++;
- }
-
- return s;
+static int cx_pl_compare(
+ struct cx_list_s const *list,
+ struct cx_list_s const *other
+) {
+ cx_pl_hack_cmpfunc(list);
+ int ret = list->climpl->compare(list, other);
+ cx_pl_unhack_cmpfunc(list);
+ return ret;
}
-static UcxList *ucx_list_sort_merge(size_t length,
- UcxList* ls, UcxList* le, UcxList* re,
- cmp_func fnc, void* data) {
+static void cx_pl_reverse(struct cx_list_s *list) {
+ list->climpl->reverse(list);
+}
- UcxList** sorted = (UcxList**) malloc(sizeof(UcxList*)*length);
- UcxList *rc, *lc;
+static void *cx_pl_iter_current(void const *it) {
+ struct cx_iterator_s const *iter = it;
+ void **ptr = iter->base.current_impl(it);
+ return ptr == NULL ? NULL : *ptr;
+}
- lc = ls; rc = le;
- size_t n = 0;
- while (lc && lc != le && rc != re) {
- if (fnc(lc->data, rc->data, data) <= 0) {
- sorted[n] = lc;
- lc = lc->next;
- } else {
- sorted[n] = rc;
- rc = rc->next;
- }
- n++;
- }
- while (lc && lc != le) {
- sorted[n] = lc;
- lc = lc->next;
- n++;
- }
- while (rc && rc != re) {
- sorted[n] = rc;
- rc = rc->next;
- n++;
- }
+static struct cx_iterator_s cx_pl_iterator(
+ struct cx_list_s const *list,
+ size_t index,
+ bool backwards
+) {
+ struct cx_iterator_s iter = list->climpl->iterator(list, index, backwards);
+ iter.base.current_impl = iter.base.current;
+ iter.base.current = cx_pl_iter_current;
+ return iter;
+}
- // Update pointer
- sorted[0]->prev = NULL;
- for (int i = 0 ; i < length-1 ; i++) {
- sorted[i]->next = sorted[i+1];
- sorted[i+1]->prev = sorted[i];
+static cx_list_class cx_pointer_list_class = {
+ cx_pl_destructor,
+ cx_pl_insert_element,
+ cx_pl_insert_array,
+ cx_pl_insert_iter,
+ cx_pl_remove,
+ cx_pl_clear,
+ cx_pl_swap,
+ cx_pl_at,
+ cx_pl_find,
+ cx_pl_sort,
+ cx_pl_compare,
+ cx_pl_reverse,
+ cx_pl_iterator,
+};
+
+void cxListStoreObjects(CxList *list) {
+ if (list->climpl != NULL) {
+ list->cl = list->climpl;
+ list->climpl = NULL;
}
- sorted[length-1]->next = NULL;
-
- UcxList *ret = sorted[0];
- free(sorted);
- return ret;
}
-UcxList *ucx_list_sort(UcxList *l, cmp_func fnc, void *data) {
- if (l == NULL) {
- return NULL;
- }
+void cxListStorePointers(CxList *list) {
+ list->itemsize = sizeof(void *);
+ list->climpl = list->cl;
+ list->cl = &cx_pointer_list_class;
+}
- UcxList *lc;
- size_t ln = 1;
+bool cxListIsStoringPointers(CxList const *list) {
+ return list->climpl != NULL;
+}
- UcxList *ls = l, *le, *re;
-
- // check how many elements are already sorted
- lc = ls;
- while (lc->next != NULL && fnc(lc->next->data, lc->data, data) > 0) {
- lc = lc->next;
- ln++;
- }
- le = lc->next;
+// </editor-fold>
- if (le == NULL) {
- return l; // this list is already sorted :)
- } else {
- UcxList *rc;
- size_t rn = 1;
- rc = le;
- // skip already sorted elements
- while (rc->next != NULL && fnc(rc->next->data, rc->data, data) > 0) {
- rc = rc->next;
- rn++;
+void cx_list_invoke_destructor(
+ CxList const *list,
+ void *elem
+) {
+ switch (list->content_destructor_type) {
+ case CX_DESTRUCTOR_SIMPLE: {
+ cx_list_invoke_simple_destructor(list, elem);
+ break;
}
- re = rc->next;
-
- // {ls,...,le->prev} and {rs,...,re->prev} are sorted - merge them
- UcxList *sorted = ucx_list_sort_merge(ln+rn,
- ls, le, re,
- fnc, data);
-
- // Something left? Sort it!
- size_t remainder_length = ucx_list_size(re);
- if (remainder_length > 0) {
- UcxList *remainder = ucx_list_sort(re, fnc, data);
-
- // merge sorted list with (also sorted) remainder
- l = ucx_list_sort_merge(ln+rn+remainder_length,
- sorted, remainder, NULL, fnc, data);
- } else {
- // no remainder - we've got our sorted list
- l = sorted;
+ case CX_DESTRUCTOR_ADVANCED: {
+ cx_list_invoke_advanced_destructor(list, elem);
+ break;
}
-
- return l;
+ case CX_DESTRUCTOR_NONE:
+ break; // nothing
}
}
-UcxList *ucx_list_first(const UcxList *l) {
- if (!l) {
- return NULL;
- }
-
- const UcxList *e = l;
- while (e->prev) {
- e = e->prev;
+void cx_list_invoke_simple_destructor(
+ CxList const *list,
+ void *elem
+) {
+ if (cxListIsStoringPointers(list)) {
+ elem = *((void **) elem);
}
- return (UcxList *)e;
+ list->simple_destructor(elem);
}
-UcxList *ucx_list_remove(UcxList *l, UcxList *e) {
- return ucx_list_remove_a(ucx_default_allocator(), l, e);
-}
-
-UcxList *ucx_list_remove_a(UcxAllocator *alloc, UcxList *l, UcxList *e) {
- if (l == e) {
- l = e->next;
- }
-
- if (e->next) {
- e->next->prev = e->prev;
+void cx_list_invoke_advanced_destructor(
+ CxList const *list,
+ void *elem
+) {
+ if (cxListIsStoringPointers(list)) {
+ elem = *((void **) elem);
}
-
- if (e->prev) {
- e->prev->next = e->next;
- }
-
- alfree(alloc, e);
- return l;
+ list->advanced_destructor.func(list->advanced_destructor.data, elem);
}
-
-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;
+void cxListDestroy(CxList *list) {
+ switch (list->content_destructor_type) {
+ case CX_DESTRUCTOR_SIMPLE: {
+ CxIterator iter = cxListIterator(list);
+ cx_foreach(void*, elem, iter) {
+ // already correctly resolved pointer - immediately invoke dtor
+ list->simple_destructor(elem);
}
+ break;
}
- 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);
-}
+ case CX_DESTRUCTOR_ADVANCED: {
+ CxIterator iter = cxListIterator(list);
+ cx_foreach(void*, elem, iter) {
+ // already correctly resolved pointer - immediately invoke dtor
+ list->advanced_destructor.func(list->advanced_destructor.data, elem);
+ }
+ break;
+ }
+ case CX_DESTRUCTOR_NONE:
+ break; // nothing
+ }
-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);
+ list->cl->destructor(list);
+ cxFree(list->allocator, list);
}
-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);
+int cxListCompare(
+ CxList const *list,
+ CxList const *other
+) {
+ if (list->cl->compare == other->cl->compare) {
+ // same compare function, lists are compatible
+ return list->cl->compare(list, other);
+ } else {
+ // different compare functions, use iterator
+ if (list->size == other->size) {
+ CxIterator left = cxListIterator(list);
+ CxIterator right = cxListIterator(other);
+ for (size_t i = 0; i < list->size; i++) {
+ void *leftValue = cxIteratorCurrent(left);
+ void *rightValue = cxIteratorCurrent(right);
+ int d = list->cmpfunc(leftValue, rightValue);
+ if (d != 0) {
+ return d;
+ }
+ cxIteratorNext(left);
+ cxIteratorNext(right);
+ }
+ return 0;
+ } else {
+ return list->size < other->size ? -1 : 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);
+CxMutIterator cxListMutIteratorAt(
+ CxList *list,
+ size_t index
+) {
+ CxIterator it = list->cl->iterator(list, index, false);
+ it.base.mutating = true;
+
+ // we know the iterators share the same memory layout
+ CxMutIterator iter;
+ memcpy(&iter, &it, sizeof(CxMutIterator));
+ return iter;
}
-UcxList* ucx_list_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);
+CxMutIterator cxListMutBackwardsIteratorAt(
+ CxList *list,
+ size_t index
+) {
+ CxIterator it = list->cl->iterator(list, index, true);
+ it.base.mutating = true;
+
+ // we know the iterators share the same memory layout
+ CxMutIterator iter;
+ memcpy(&iter, &it, sizeof(CxMutIterator));
+ return iter;
}
+++ /dev/null
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "ucx/logging.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-#include <time.h>
-
-UcxLogger *ucx_logger_new(void *stream, unsigned int level, unsigned int mask) {
- UcxLogger *logger = (UcxLogger*) malloc(sizeof(UcxLogger));
- if (logger != NULL) {
- logger->stream = stream;
- logger->writer = (write_func)fwrite;
- logger->dateformat = (char*) "%F %T %z ";
- logger->level = level;
- logger->mask = mask;
- logger->levels = ucx_map_new(8);
-
- unsigned int l;
- l = UCX_LOGGER_ERROR;
- ucx_map_int_put(logger->levels, l, (void*) "[ERROR]");
- l = UCX_LOGGER_WARN;
- ucx_map_int_put(logger->levels, l, (void*) "[WARNING]");
- l = UCX_LOGGER_INFO;
- ucx_map_int_put(logger->levels, l, (void*) "[INFO]");
- l = UCX_LOGGER_DEBUG;
- ucx_map_int_put(logger->levels, l, (void*) "[DEBUG]");
- l = UCX_LOGGER_TRACE;
- ucx_map_int_put(logger->levels, l, (void*) "[TRACE]");
- }
-
- return logger;
-}
-
-void ucx_logger_free(UcxLogger *logger) {
- ucx_map_free(logger->levels);
- free(logger);
-}
-
-// estimated max. message length (documented)
-#define UCX_LOGGER_MSGMAX 4096
-
-void ucx_logger_logf(UcxLogger *logger, unsigned int level, const char* file,
- const unsigned int line, const char *format, ...) {
- if (level <= logger->level) {
- char msg[UCX_LOGGER_MSGMAX];
- const char *text;
- size_t k = 0;
- size_t n;
-
- if ((logger->mask & UCX_LOGGER_LEVEL) > 0) {
- text = (const char*) ucx_map_int_get(logger->levels, level);
- if (!text) {
- text = "[UNKNOWN]";
- }
- n = strlen(text);
- n = n > 256 ? 256 : n;
- memcpy(msg+k, text, n);
- k += n;
- msg[k++] = ' ';
- }
- if ((logger->mask & UCX_LOGGER_TIMESTAMP) > 0) {
- time_t now = time(NULL);
- k += strftime(msg+k, 128, logger->dateformat, localtime(&now));
- }
- if ((logger->mask & UCX_LOGGER_SOURCE) > 0) {
- char *fpart = strrchr(file, '/');
- if (fpart) file = fpart+1;
- fpart = strrchr(file, '\\');
- if (fpart) file = fpart+1;
- n = strlen(file);
- memcpy(msg+k, file, n);
- k += n;
- k += sprintf(msg+k, ":%u ", line);
- }
-
- if (k > 0) {
- msg[k++] = '-'; msg[k++] = ' ';
- }
-
- va_list args;
- va_start (args, format);
- k += vsnprintf(msg+k, UCX_LOGGER_MSGMAX-k-1, format, args);
- va_end (args);
-
- msg[k++] = '\n';
-
- logger->writer(msg, 1, k, logger->stream);
- }
-}
+++ /dev/null
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "ucx/map.h"
-
-#include <stdlib.h>
-#include <string.h>
-
-UcxMap *ucx_map_new(size_t size) {
- return ucx_map_new_a(NULL, size);
-}
-
-UcxMap *ucx_map_new_a(UcxAllocator *allocator, size_t size) {
- if(size == 0) {
- size = 16;
- }
-
- if(!allocator) {
- allocator = ucx_default_allocator();
- }
-
- UcxMap *map = (UcxMap*)almalloc(allocator, sizeof(UcxMap));
- if (!map) {
- return NULL;
- }
-
- map->allocator = allocator;
- map->map = (UcxMapElement**)alcalloc(
- allocator, size, sizeof(UcxMapElement*));
- if(map->map == NULL) {
- alfree(allocator, map);
- return NULL;
- }
- map->size = size;
- map->count = 0;
-
- return map;
-}
-
-static void ucx_map_free_elmlist_contents(UcxMap *map) {
- for (size_t n = 0 ; n < map->size ; n++) {
- UcxMapElement *elem = map->map[n];
- if (elem != NULL) {
- do {
- UcxMapElement *next = elem->next;
- alfree(map->allocator, elem->key.data);
- alfree(map->allocator, elem);
- elem = next;
- } while (elem != NULL);
- }
- }
-}
-
-void ucx_map_free(UcxMap *map) {
- ucx_map_free_elmlist_contents(map);
- alfree(map->allocator, map->map);
- alfree(map->allocator, map);
-}
-
-void ucx_map_free_content(UcxMap *map, ucx_destructor destr) {
- UcxMapIterator iter = ucx_map_iterator(map);
- void *val;
- UCX_MAP_FOREACH(key, val, iter) {
- if (destr) {
- destr(val);
- } else {
- alfree(map->allocator, val);
- }
- }
-}
-
-void ucx_map_clear(UcxMap *map) {
- if (map->count == 0) {
- return; // nothing to do
- }
- ucx_map_free_elmlist_contents(map);
- memset(map->map, 0, map->size*sizeof(UcxMapElement*));
- map->count = 0;
-}
-
-int ucx_map_copy(UcxMap const *from, UcxMap *to, copy_func fnc, void *data) {
- UcxMapIterator i = ucx_map_iterator(from);
- void *value;
- UCX_MAP_FOREACH(key, value, i) {
- if (ucx_map_put(to, key, fnc ? fnc(value, data) : value)) {
- return 1;
- }
- }
- return 0;
-}
-
-UcxMap *ucx_map_clone(UcxMap const *map, copy_func fnc, void *data) {
- return ucx_map_clone_a(ucx_default_allocator(), map, fnc, data);
-}
-
-UcxMap *ucx_map_clone_a(UcxAllocator *allocator,
- UcxMap const *map, copy_func fnc, void *data) {
- size_t bs = (map->count * 5) >> 1;
- UcxMap *newmap = ucx_map_new_a(allocator, bs > map->size ? bs : map->size);
- if (!newmap) {
- return NULL;
- }
- ucx_map_copy(map, newmap, fnc, data);
- return newmap;
-}
-
-int ucx_map_rehash(UcxMap *map) {
- size_t load = (map->size * 3) >> 2;
- if (map->count > load) {
- UcxMap oldmap;
- oldmap.map = map->map;
- oldmap.size = map->size;
- oldmap.count = map->count;
- oldmap.allocator = map->allocator;
-
- map->size = (map->count * 5) >> 1;
- map->map = (UcxMapElement**)alcalloc(
- map->allocator, map->size, sizeof(UcxMapElement*));
- if (!map->map) {
- *map = oldmap;
- return 1;
- }
- map->count = 0;
- ucx_map_copy(&oldmap, map, NULL, NULL);
-
- /* free the UcxMapElement list of oldmap */
- ucx_map_free_elmlist_contents(&oldmap);
- alfree(map->allocator, oldmap.map);
- }
- return 0;
-}
-
-int ucx_map_put(UcxMap *map, UcxKey key, void *data) {
- UcxAllocator *allocator = map->allocator;
-
- if (key.hash == 0) {
- key.hash = ucx_hash((const char*)key.data, key.len);
- }
-
- struct UcxMapKey mapkey;
- mapkey.hash = key.hash;
-
- size_t slot = mapkey.hash%map->size;
- UcxMapElement *elm = map->map[slot];
- UcxMapElement *prev = NULL;
-
- while (elm && elm->key.hash < mapkey.hash) {
- prev = elm;
- elm = elm->next;
- }
-
- if (!elm || elm->key.hash != mapkey.hash) {
- UcxMapElement *e = (UcxMapElement*)almalloc(
- allocator, sizeof(UcxMapElement));
- if (!e) {
- return -1;
- }
- e->key.data = NULL;
- if (prev) {
- prev->next = e;
- } else {
- map->map[slot] = e;
- }
- e->next = elm;
- elm = e;
- }
-
- if (!elm->key.data) {
- void *kd = almalloc(allocator, key.len);
- if (!kd) {
- return -1;
- }
- memcpy(kd, key.data, key.len);
- mapkey.data = kd;
- mapkey.len = key.len;
- elm->key = mapkey;
- map->count++;
- }
- elm->data = data;
-
- return 0;
-}
-
-static void* ucx_map_get_and_remove(UcxMap *map, UcxKey key, int remove) {
- if(key.hash == 0) {
- key.hash = ucx_hash((const char*)key.data, key.len);
- }
-
- size_t slot = key.hash%map->size;
- UcxMapElement *elm = map->map[slot];
- UcxMapElement *pelm = NULL;
- while (elm && elm->key.hash <= key.hash) {
- if(elm->key.hash == key.hash) {
- int n = (key.len > elm->key.len) ? elm->key.len : key.len;
- if (memcmp(elm->key.data, key.data, n) == 0) {
- void *data = elm->data;
- if (remove) {
- if (pelm) {
- pelm->next = elm->next;
- } else {
- map->map[slot] = elm->next;
- }
- alfree(map->allocator, elm->key.data);
- alfree(map->allocator, elm);
- map->count--;
- }
-
- return data;
- }
- }
- pelm = elm;
- elm = pelm->next;
- }
-
- return NULL;
-}
-
-void *ucx_map_get(UcxMap const *map, UcxKey key) {
- return ucx_map_get_and_remove((UcxMap *)map, key, 0);
-}
-
-void *ucx_map_remove(UcxMap *map, UcxKey key) {
- return ucx_map_get_and_remove(map, key, 1);
-}
-
-UcxKey ucx_key(const void *data, size_t len) {
- UcxKey key;
- key.data = data;
- key.len = len;
- key.hash = ucx_hash((const char*)data, len);
- return key;
-}
-
-
-int ucx_hash(const char *data, size_t len) {
- /* murmur hash 2 */
-
- int m = 0x5bd1e995;
- int r = 24;
-
- int h = 25 ^ len;
-
- int i = 0;
- while (len >= 4) {
- int k = data[i + 0] & 0xFF;
- k |= (data[i + 1] & 0xFF) << 8;
- k |= (data[i + 2] & 0xFF) << 16;
- k |= (data[i + 3] & 0xFF) << 24;
-
- k *= m;
- k ^= k >> r;
- k *= m;
-
- h *= m;
- h ^= k;
-
- i += 4;
- len -= 4;
- }
-
- switch (len) {
- case 3: h ^= (data[i + 2] & 0xFF) << 16;
- /* no break */
- case 2: h ^= (data[i + 1] & 0xFF) << 8;
- /* no break */
- case 1: h ^= (data[i + 0] & 0xFF); h *= m;
- /* no break */
- }
-
- h ^= h >> 13;
- h *= m;
- h ^= h >> 15;
-
- return h;
-}
-
-UcxMapIterator ucx_map_iterator(UcxMap const *map) {
- UcxMapIterator i;
- i.map = map;
- i.cur = NULL;
- i.index = 0;
- return i;
-}
-
-int ucx_map_iter_next(UcxMapIterator *i, UcxKey *key, void **elm) {
- UcxMapElement *e = i->cur;
-
- if (e) {
- e = e->next;
- } else {
- e = i->map->map[0];
- }
-
- while (i->index < i->map->size) {
- if (e) {
- if (e->data) {
- i->cur = e;
- *elm = e->data;
- key->data = e->key.data;
- key->hash = e->key.hash;
- key->len = e->key.len;
- return 1;
- }
-
- e = e->next;
- } else {
- i->index++;
-
- if (i->index < i->map->size) {
- e = i->map->map[i->index];
- }
- }
- }
-
- return 0;
-}
-
-UcxMap* ucx_map_union(const UcxMap *first, const UcxMap *second,
- copy_func cpfnc, void* cpdata) {
- return ucx_map_union_a(ucx_default_allocator(),
- first, second, cpfnc, cpdata);
-}
-
-UcxMap* ucx_map_union_a(UcxAllocator *allocator,
- const UcxMap *first, const UcxMap *second,
- copy_func cpfnc, void* cpdata) {
- UcxMap* result = ucx_map_clone_a(allocator, first, cpfnc, cpdata);
- ucx_map_copy(second, result, cpfnc, cpdata);
- return result;
-}
-
-UcxMap* ucx_map_intersection(const UcxMap *first, const UcxMap *second,
- copy_func cpfnc, void* cpdata) {
- return ucx_map_intersection_a(ucx_default_allocator(),
- first, second, cpfnc, cpdata);
-}
-
-UcxMap* ucx_map_intersection_a(UcxAllocator *allocator,
- const UcxMap *first, const UcxMap *second,
- copy_func cpfnc, void* cpdata) {
- UcxMap *result = ucx_map_new_a(allocator, first->size < second->size ?
- first->size : second->size);
-
- UcxMapIterator iter = ucx_map_iterator(first);
- void* value;
- UCX_MAP_FOREACH(key, value, iter) {
- if (ucx_map_get(second, key)) {
- ucx_map_put(result, key, cpfnc ? cpfnc(value, cpdata) : value);
- }
- }
-
- return result;
-}
-
-UcxMap* ucx_map_difference(const UcxMap *first, const UcxMap *second,
- copy_func cpfnc, void* cpdata) {
- return ucx_map_difference_a(ucx_default_allocator(),
- first, second, cpfnc, cpdata);
-}
-
-UcxMap* ucx_map_difference_a(UcxAllocator *allocator,
- const UcxMap *first, const UcxMap *second,
- copy_func cpfnc, void* cpdata) {
-
- UcxMap *result = ucx_map_new_a(allocator, first->size - second->count);
-
- UcxMapIterator iter = ucx_map_iterator(first);
- void* value;
- UCX_MAP_FOREACH(key, value, iter) {
- if (!ucx_map_get(second, key)) {
- ucx_map_put(result, key, cpfnc ? cpfnc(value, cpdata) : value);
- }
- }
-
- ucx_map_rehash(result);
- return result;
-}
\ No newline at end of file
+++ /dev/null
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "ucx/mempool.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#ifdef __cplusplus
-#define __STDC_FORMAT_MACROS
-#endif
-#include <inttypes.h>
-
-/** Capsule for destructible memory chunks. */
-typedef struct {
- /** The destructor for the memory chunk. */
- ucx_destructor destructor;
- /**
- * First byte of the memory chunk.
- * Note, that the address <code>&c</code> is also the address
- * of the whole memory chunk.
- */
- char c;
-} ucx_memchunk;
-
-/** Capsule for data and its destructor. */
-typedef struct {
- /** The destructor for the data. */
- ucx_destructor destructor;
- /** A pointer to the data. */
- void *ptr;
-} ucx_regdestr;
-
-#ifdef __cplusplus
-extern "C"
-#endif
-void ucx_mempool_shared_destr(void* ptr) {
- ucx_regdestr *rd = (ucx_regdestr*)ptr;
- rd->destructor(rd->ptr);
-}
-
-UcxMempool *ucx_mempool_new(size_t n) {
- size_t poolsz;
- if(ucx_szmul(n, sizeof(void*), &poolsz)) {
- return NULL;
- }
-
- UcxMempool *pool = (UcxMempool*)malloc(sizeof(UcxMempool));
- if (!pool) {
- return NULL;
- }
-
- pool->data = (void**) malloc(poolsz);
- if (pool->data == NULL) {
- free(pool);
- return NULL;
- }
-
- pool->ndata = 0;
- pool->size = n;
-
- UcxAllocator *allocator = (UcxAllocator*)malloc(sizeof(UcxAllocator));
- if(!allocator) {
- free(pool->data);
- free(pool);
- return NULL;
- }
- allocator->malloc = (ucx_allocator_malloc)ucx_mempool_malloc;
- allocator->calloc = (ucx_allocator_calloc)ucx_mempool_calloc;
- allocator->realloc = (ucx_allocator_realloc)ucx_mempool_realloc;
- allocator->free = (ucx_allocator_free)ucx_mempool_free;
- allocator->pool = pool;
- pool->allocator = allocator;
-
- return pool;
-}
-
-int ucx_mempool_chcap(UcxMempool *pool, size_t newcap) {
- if (newcap < pool->ndata) {
- return 1;
- }
-
- size_t newcapsz;
- if(ucx_szmul(newcap, sizeof(void*), &newcapsz)) {
- return 1;
- }
-
- void **data = (void**) realloc(pool->data, newcapsz);
- if (data) {
- pool->data = data;
- pool->size = newcap;
- return 0;
- } else {
- return 1;
- }
-}
-
-void *ucx_mempool_malloc(UcxMempool *pool, size_t n) {
- if(((size_t)-1) - sizeof(ucx_destructor) < n) {
- return NULL;
- }
-
- if (pool->ndata >= pool->size) {
- size_t newcap = pool->size*2;
- if (newcap < pool->size || ucx_mempool_chcap(pool, newcap)) {
- return NULL;
- }
- }
-
- void *p = malloc(sizeof(ucx_destructor) + n);
- ucx_memchunk *mem = (ucx_memchunk*)p;
- if (!mem) {
- return NULL;
- }
-
- mem->destructor = NULL;
- pool->data[pool->ndata] = mem;
- pool->ndata++;
-
- return &(mem->c);
-}
-
-void *ucx_mempool_calloc(UcxMempool *pool, size_t nelem, size_t elsize) {
- size_t msz;
- if(ucx_szmul(nelem, elsize, &msz)) {
- return NULL;
- }
-
- void *ptr = ucx_mempool_malloc(pool, msz);
- if (!ptr) {
- return NULL;
- }
- memset(ptr, 0, nelem * elsize);
- return ptr;
-}
-
-void *ucx_mempool_realloc(UcxMempool *pool, void *ptr, size_t n) {
- if(((size_t)-1) - sizeof(ucx_destructor) < n) {
- return NULL;
- }
-
- char *mem = ((char*)ptr) - sizeof(ucx_destructor);
- char *newm = (char*) realloc(mem, n + sizeof(ucx_destructor));
- if (!newm) {
- return NULL;
- }
- if (mem != newm) {
- for(size_t i=0 ; i < pool->ndata ; i++) {
- if(pool->data[i] == mem) {
- pool->data[i] = newm;
- return newm + sizeof(ucx_destructor);
- }
- }
- fprintf(stderr, "FATAL: 0x%08" PRIxPTR" not in mpool 0x%08" PRIxPTR"\n",
- (intptr_t)ptr, (intptr_t)pool);
- abort();
- } else {
- return newm + sizeof(ucx_destructor);
- }
-}
-
-void ucx_mempool_free(UcxMempool *pool, void *ptr) {
- ucx_memchunk *chunk = (ucx_memchunk*)((char*)ptr-sizeof(ucx_destructor));
- for(size_t i=0 ; i<pool->ndata ; i++) {
- if(chunk == pool->data[i]) {
- if(chunk->destructor != NULL) {
- chunk->destructor(&(chunk->c));
- }
- free(chunk);
- size_t last_index = pool->ndata - 1;
- if(i != last_index) {
- pool->data[i] = pool->data[last_index];
- pool->data[last_index] = NULL;
- }
- pool->ndata--;
- return;
- }
- }
- fprintf(stderr, "FATAL: 0x%08" PRIxPTR" not in mpool 0x%08" PRIxPTR"\n",
- (intptr_t)ptr, (intptr_t)pool);
- abort();
-}
-
-void ucx_mempool_destroy(UcxMempool *pool) {
- ucx_memchunk *chunk;
- for(size_t i=0 ; i<pool->ndata ; i++) {
- chunk = (ucx_memchunk*) pool->data[i];
- if(chunk) {
- if(chunk->destructor) {
- chunk->destructor(&(chunk->c));
- }
- free(chunk);
- }
- }
- free(pool->data);
- free(pool->allocator);
- free(pool);
-}
-
-void ucx_mempool_set_destr(void *ptr, ucx_destructor func) {
- *(ucx_destructor*)((char*)ptr-sizeof(ucx_destructor)) = func;
-}
-
-void ucx_mempool_reg_destr(UcxMempool *pool, void *ptr, ucx_destructor destr) {
- ucx_regdestr *rd = (ucx_regdestr*)ucx_mempool_malloc(
- pool,
- sizeof(ucx_regdestr));
- rd->destructor = destr;
- rd->ptr = ptr;
- ucx_mempool_set_destr(rd, ucx_mempool_shared_destr);
-}
-
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "cx/printf.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#ifndef CX_PRINTF_SBO_SIZE
+#define CX_PRINTF_SBO_SIZE 512
+#endif
+
+int cx_fprintf(
+ void *stream,
+ cx_write_func wfc,
+ char const *fmt,
+ ...
+) {
+ int ret;
+ va_list ap;
+ va_start(ap, fmt);
+ ret = cx_vfprintf(stream, wfc, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+int cx_vfprintf(
+ void *stream,
+ cx_write_func wfc,
+ char const *fmt,
+ va_list ap
+) {
+ char buf[CX_PRINTF_SBO_SIZE];
+ va_list ap2;
+ va_copy(ap2, ap);
+ int ret = vsnprintf(buf, CX_PRINTF_SBO_SIZE, fmt, ap);
+ if (ret < 0) {
+ return ret;
+ } else if (ret < CX_PRINTF_SBO_SIZE) {
+ return (int) wfc(buf, 1, ret, stream);
+ } else {
+ int len = ret + 1;
+ char *newbuf = malloc(len);
+ if (!newbuf) {
+ return -1;
+ }
+
+ ret = vsnprintf(newbuf, len, fmt, ap2);
+ if (ret > 0) {
+ ret = (int) wfc(newbuf, 1, ret, stream);
+ }
+ free(newbuf);
+ }
+ return ret;
+}
+
+cxmutstr cx_asprintf_a(
+ CxAllocator *allocator,
+ char const *fmt,
+ ...
+) {
+ va_list ap;
+ cxmutstr ret;
+ va_start(ap, fmt);
+ ret = cx_vasprintf_a(allocator, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+cxmutstr cx_vasprintf_a(
+ CxAllocator *a,
+ char const *fmt,
+ va_list ap
+) {
+ cxmutstr s;
+ s.ptr = NULL;
+ s.length = 0;
+ char buf[CX_PRINTF_SBO_SIZE];
+ va_list ap2;
+ va_copy(ap2, ap);
+ int ret = vsnprintf(buf, CX_PRINTF_SBO_SIZE, fmt, ap);
+ if (ret > 0 && ret < CX_PRINTF_SBO_SIZE) {
+ s.ptr = cxMalloc(a, ret + 1);
+ if (s.ptr) {
+ s.length = (size_t) ret;
+ memcpy(s.ptr, buf, ret);
+ s.ptr[s.length] = '\0';
+ }
+ } else {
+ int len = ret + 1;
+ s.ptr = cxMalloc(a, len);
+ if (s.ptr) {
+ ret = vsnprintf(s.ptr, len, fmt, ap2);
+ if (ret < 0) {
+ free(s.ptr);
+ s.ptr = NULL;
+ } else {
+ s.length = (size_t) ret;
+ }
+ }
+ }
+ return s;
+}
+
+++ /dev/null
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "ucx/stack.h"
-
-#include <string.h>
-
-static size_t ucx_stack_align(size_t n) {
- int align = n % sizeof(void*);
- if (align) {
- n += sizeof(void*) - align;
- }
- return n;
-}
-
-void ucx_stack_init(UcxStack *stack, char* space, size_t size) {
- stack->size = size - size % sizeof(void*);
- stack->space = space;
- stack->top = NULL;
-
- stack->allocator.pool = stack;
- stack->allocator.malloc = (ucx_allocator_malloc) ucx_stack_malloc;
- stack->allocator.calloc = (ucx_allocator_calloc) ucx_stack_calloc;
- stack->allocator.realloc = (ucx_allocator_realloc) ucx_stack_realloc;
- stack->allocator.free = (ucx_allocator_free) ucx_stack_free;
-}
-
-void *ucx_stack_malloc(UcxStack *stack, size_t n) {
-
- if (ucx_stack_avail(stack) < ucx_stack_align(n)) {
- return NULL;
- } else {
- char *prev = stack->top;
- if (stack->top) {
- stack->top += ucx_stack_align(ucx_stack_topsize(stack));
- } else {
- stack->top = stack->space;
- }
-
- ((struct ucx_stack_metadata*)stack->top)->prev = prev;
- ((struct ucx_stack_metadata*)stack->top)->size = n;
- stack->top += sizeof(struct ucx_stack_metadata);
-
- return stack->top;
- }
-}
-
-void *ucx_stack_calloc(UcxStack *stack, size_t nelem, size_t elsize) {
- void *mem = ucx_stack_malloc(stack, nelem*elsize);
- memset(mem, 0, nelem*elsize);
- return mem;
-}
-
-void *ucx_stack_realloc(UcxStack *stack, void *ptr, size_t n) {
- if (ptr == stack->top) {
- if (stack->size - (stack->top - stack->space) < ucx_stack_align(n)) {
- return NULL;
- } else {
- ((struct ucx_stack_metadata*)stack->top - 1)->size = n;
- return ptr;
- }
- } else {
- if (ucx_stack_align(((struct ucx_stack_metadata*)ptr - 1)->size) <
- ucx_stack_align(n)) {
- void *nptr = ucx_stack_malloc(stack, n);
- if (nptr) {
- memcpy(nptr, ptr, n);
- ucx_stack_free(stack, ptr);
-
- return nptr;
- } else {
- return NULL;
- }
- } else {
- ((struct ucx_stack_metadata*)ptr - 1)->size = n;
- return ptr;
- }
- }
-}
-
-void ucx_stack_free(UcxStack *stack, void *ptr) {
- if (ptr == stack->top) {
- stack->top = ((struct ucx_stack_metadata*) stack->top - 1)->prev;
- } else {
- struct ucx_stack_metadata *next = (struct ucx_stack_metadata*)(
- (char*)ptr +
- ucx_stack_align(((struct ucx_stack_metadata*) ptr - 1)->size)
- );
- next->prev = ((struct ucx_stack_metadata*) ptr - 1)->prev;
- }
-}
-
-void ucx_stack_popn(UcxStack *stack, void *dest, size_t n) {
- if (ucx_stack_empty(stack)) {
- return;
- }
-
- if (dest) {
- size_t len = ucx_stack_topsize(stack);
- if (len > n) {
- len = n;
- }
-
- memcpy(dest, stack->top, len);
- }
-
- ucx_stack_free(stack, stack->top);
-}
-
-size_t ucx_stack_avail(UcxStack *stack) {
- size_t avail = ((stack->top ? (stack->size
- - (stack->top - stack->space)
- - ucx_stack_align(ucx_stack_topsize(stack)))
- : stack->size));
-
- if (avail > sizeof(struct ucx_stack_metadata)) {
- return avail - sizeof(struct ucx_stack_metadata);
- } else {
- return 0;
- }
-}
-
-void *ucx_stack_push(UcxStack *stack, size_t n, const void *data) {
- void *space = ucx_stack_malloc(stack, n);
- if (space) {
- memcpy(space, data, n);
- }
- return space;
-}
-
-void *ucx_stack_pusharr(UcxStack *stack,
- size_t nelem, size_t elsize, const void *data) {
-
- // skip the memset by using malloc
- void *space = ucx_stack_malloc(stack, nelem*elsize);
- if (space) {
- memcpy(space, data, nelem*elsize);
- }
- return space;
-}
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* POSSIBILITY OF SUCH DAMAGE.
*/
-#include "ucx/string.h"
+#include "cx/string.h"
+#include "cx/utils.h"
-#include "ucx/allocator.h"
-
-#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
-#include <stdint.h>
#include <ctype.h>
#ifndef _WIN32
-#include <strings.h> /* for strncasecmp() */
-#endif /* _WIN32 */
-sstr_t sstr(char *cstring) {
- sstr_t string;
- string.ptr = cstring;
- string.length = strlen(cstring);
- return string;
+#include <strings.h> // for strncasecmp()
+
+#endif // _WIN32
+
+cxmutstr cx_mutstr(char *cstring) {
+ return (cxmutstr) {cstring, strlen(cstring)};
+}
+
+cxmutstr cx_mutstrn(
+ char *cstring,
+ size_t length
+) {
+ return (cxmutstr) {cstring, length};
}
-sstr_t sstrn(char *cstring, size_t length) {
- sstr_t string;
- string.ptr = cstring;
- string.length = length;
- return string;
+cxstring cx_str(const char *cstring) {
+ return (cxstring) {cstring, strlen(cstring)};
}
-scstr_t scstr(const char *cstring) {
- scstr_t string;
- string.ptr = cstring;
- string.length = strlen(cstring);
- return string;
+cxstring cx_strn(
+ const char *cstring,
+ size_t length
+) {
+ return (cxstring) {cstring, length};
}
-scstr_t scstrn(const char *cstring, size_t length) {
- scstr_t string;
- string.ptr = cstring;
- string.length = length;
- return string;
+cxstring cx_strcast(cxmutstr str) {
+ return (cxstring) {str.ptr, str.length};
}
+void cx_strfree(cxmutstr *str) {
+ free(str->ptr);
+ str->ptr = NULL;
+ str->length = 0;
+}
+
+void cx_strfree_a(
+ CxAllocator *alloc,
+ cxmutstr *str
+) {
+ cxFree(alloc, str->ptr);
+ str->ptr = NULL;
+ str->length = 0;
+}
+
+size_t cx_strlen(
+ size_t count,
+ ...
+) {
+ if (count == 0) return 0;
-size_t scstrnlen(size_t n, ...) {
- if (n == 0) return 0;
-
va_list ap;
- va_start(ap, n);
-
+ va_start(ap, count);
size_t size = 0;
-
- for (size_t i = 0 ; i < n ; i++) {
- scstr_t str = va_arg(ap, scstr_t);
- if(SIZE_MAX - str.length < size) {
- size = SIZE_MAX;
- break;
- }
+ cx_for_n(i, count) {
+ cxstring str = va_arg(ap, cxstring);
size += str.length;
}
va_end(ap);
return size;
}
-static sstr_t sstrvcat_a(
- UcxAllocator *a,
+cxmutstr cx_strcat_a(
+ CxAllocator *alloc,
size_t count,
- scstr_t s1,
- va_list ap) {
- sstr_t str;
- str.ptr = NULL;
- str.length = 0;
- if(count < 2) {
- return str;
- }
-
- scstr_t s2 = va_arg (ap, scstr_t);
-
- if(((size_t)-1) - s1.length < s2.length) {
- return str;
- }
-
- scstr_t *strings = (scstr_t*) calloc(count, sizeof(scstr_t));
- if(!strings) {
- return str;
- }
-
+ ...
+) {
+ cxstring *strings = calloc(count, sizeof(cxstring));
+ if (!strings) abort();
+
+ va_list ap;
+ va_start(ap, count);
+
// get all args and overall length
- strings[0] = s1;
- strings[1] = s2;
- size_t slen = s1.length + s2.length;
- int error = 0;
- for (size_t i=2;i<count;i++) {
- scstr_t s = va_arg (ap, scstr_t);
+ size_t slen = 0;
+ cx_for_n(i, count) {
+ cxstring s = va_arg (ap, cxstring);
strings[i] = s;
- if(((size_t)-1) - s.length < slen) {
- error = 1;
- break;
- }
slen += s.length;
}
- if(error) {
- free(strings);
- return str;
- }
-
+
// create new string
- str.ptr = (char*) almalloc(a, slen + 1);
- str.length = slen;
- if(!str.ptr) {
- free(strings);
- str.length = 0;
- return str;
- }
-
+ cxmutstr result;
+ result.ptr = cxMalloc(alloc, slen + 1);
+ result.length = slen;
+ if (result.ptr == NULL) abort();
+
// concatenate strings
size_t pos = 0;
- for (size_t i=0;i<count;i++) {
- scstr_t s = strings[i];
- memcpy(str.ptr + pos, s.ptr, s.length);
+ cx_for_n(i, count) {
+ cxstring s = strings[i];
+ memcpy(result.ptr + pos, s.ptr, s.length);
pos += s.length;
}
-
- str.ptr[str.length] = '\0';
-
+
+ // terminate string
+ result.ptr[result.length] = '\0';
+
+ // free temporary array
free(strings);
-
- return str;
+
+ return result;
}
-sstr_t scstrcat(size_t count, scstr_t s1, ...) {
- va_list ap;
- va_start(ap, s1);
- sstr_t s = sstrvcat_a(ucx_default_allocator(), count, s1, ap);
- va_end(ap);
- return s;
+cxstring cx_strsubs(
+ cxstring string,
+ size_t start
+) {
+ return cx_strsubsl(string, start, string.length - start);
}
-sstr_t scstrcat_a(UcxAllocator *a, size_t count, scstr_t s1, ...) {
- va_list ap;
- va_start(ap, s1);
- sstr_t s = sstrvcat_a(a, count, s1, ap);
- va_end(ap);
- return s;
+cxmutstr cx_strsubs_m(
+ cxmutstr string,
+ size_t start
+) {
+ return cx_strsubsl_m(string, start, string.length - start);
}
-static int ucx_substring(
- size_t str_length,
+cxstring cx_strsubsl(
+ cxstring string,
size_t start,
- size_t length,
- size_t *newlen,
- size_t *newpos)
-{
- *newlen = 0;
- *newpos = 0;
-
- if(start > str_length) {
- return 0;
+ size_t length
+) {
+ if (start > string.length) {
+ return (cxstring) {NULL, 0};
}
-
- if(length > str_length - start) {
- length = str_length - start;
- }
- *newlen = length;
- *newpos = start;
- return 1;
-}
-
-sstr_t sstrsubs(sstr_t s, size_t start) {
- return sstrsubsl (s, start, s.length-start);
-}
-sstr_t sstrsubsl(sstr_t s, size_t start, size_t length) {
- size_t pos;
- sstr_t ret = { NULL, 0 };
- if(ucx_substring(s.length, start, length, &ret.length, &pos)) {
- ret.ptr = s.ptr + pos;
+ size_t rem_len = string.length - start;
+ if (length > rem_len) {
+ length = rem_len;
}
- return ret;
-}
-scstr_t scstrsubs(scstr_t string, size_t start) {
- return scstrsubsl(string, start, string.length-start);
+ return (cxstring) {string.ptr + start, length};
}
-scstr_t scstrsubsl(scstr_t s, size_t start, size_t length) {
- size_t pos;
- scstr_t ret = { NULL, 0 };
- if(ucx_substring(s.length, start, length, &ret.length, &pos)) {
- ret.ptr = s.ptr + pos;
+cxmutstr cx_strsubsl_m(
+ cxmutstr string,
+ size_t start,
+ size_t length
+) {
+ cxstring result = cx_strsubsl(cx_strcast(string), start, length);
+ return (cxmutstr) {(char *) result.ptr, result.length};
+}
+
+cxstring cx_strchr(
+ cxstring string,
+ int chr
+) {
+ chr = 0xFF & chr;
+ // TODO: improve by comparing multiple bytes at once
+ cx_for_n(i, string.length) {
+ if (string.ptr[i] == chr) {
+ return cx_strsubs(string, i);
+ }
}
- return ret;
+ return (cxstring) {NULL, 0};
}
-
-static int ucx_strchr(const char *str, size_t length, int chr, size_t *pos) {
- for(size_t i=0;i<length;i++) {
- if(str[i] == chr) {
- *pos = i;
- return 1;
- }
- }
- return 0;
+cxmutstr cx_strchr_m(
+ cxmutstr string,
+ int chr
+) {
+ cxstring result = cx_strchr(cx_strcast(string), chr);
+ return (cxmutstr) {(char *) result.ptr, result.length};
}
-static int ucx_strrchr(const char *str, size_t length, int chr, size_t *pos) {
- if(length > 0) {
- for(size_t i=length ; i>0 ; i--) {
- if(str[i-1] == chr) {
- *pos = i-1;
- return 1;
- }
+cxstring cx_strrchr(
+ cxstring string,
+ int chr
+) {
+ chr = 0xFF & chr;
+ size_t i = string.length;
+ while (i > 0) {
+ i--;
+ // TODO: improve by comparing multiple bytes at once
+ if (string.ptr[i] == chr) {
+ return cx_strsubs(string, i);
}
}
- return 0;
+ return (cxstring) {NULL, 0};
}
-sstr_t sstrchr(sstr_t s, int c) {
- size_t pos = 0;
- if(ucx_strchr(s.ptr, s.length, c, &pos)) {
- return sstrsubs(s, pos);
- }
- return sstrn(NULL, 0);
+cxmutstr cx_strrchr_m(
+ cxmutstr string,
+ int chr
+) {
+ cxstring result = cx_strrchr(cx_strcast(string), chr);
+ return (cxmutstr) {(char *) result.ptr, result.length};
}
-sstr_t sstrrchr(sstr_t s, int c) {
- size_t pos = 0;
- if(ucx_strrchr(s.ptr, s.length, c, &pos)) {
- return sstrsubs(s, pos);
- }
- return sstrn(NULL, 0);
-}
+#ifndef CX_STRSTR_SBO_SIZE
+#define CX_STRSTR_SBO_SIZE 512
+#endif
-scstr_t scstrchr(scstr_t s, int c) {
- size_t pos = 0;
- if(ucx_strchr(s.ptr, s.length, c, &pos)) {
- return scstrsubs(s, pos);
+cxstring cx_strstr(
+ cxstring haystack,
+ cxstring needle
+) {
+ if (needle.length == 0) {
+ return haystack;
}
- return scstrn(NULL, 0);
-}
-scstr_t scstrrchr(scstr_t s, int c) {
- size_t pos = 0;
- if(ucx_strrchr(s.ptr, s.length, c, &pos)) {
- return scstrsubs(s, pos);
+ // optimize for single-char needles
+ if (needle.length == 1) {
+ return cx_strchr(haystack, *needle.ptr);
}
- return scstrn(NULL, 0);
-}
-#define ptable_r(dest, useheap, ptable, index) (dest = useheap ? \
- ((size_t*)ptable)[index] : (size_t) ((uint8_t*)ptable)[index])
-
-#define ptable_w(useheap, ptable, index, src) do {\
- if (!useheap) ((uint8_t*)ptable)[index] = (uint8_t) src;\
- else ((size_t*)ptable)[index] = src;\
- } while (0);
-
-
-static const char* ucx_strstr(
- const char *str,
- size_t length,
- const char *match,
- size_t matchlen,
- size_t *newlen)
-{
- *newlen = length;
- if (matchlen == 0) {
- return str;
- }
-
- const char *result = NULL;
- size_t resultlen = 0;
-
/*
* IMPORTANT:
- * our prefix table contains the prefix length PLUS ONE
- * this is our decision, because we want to use the full range of size_t
- * the original algorithm needs a (-1) at one single place
- * and we want to avoid that
+ * Our prefix table contains the prefix length PLUS ONE
+ * this is our decision, because we want to use the full range of size_t.
+ * The original algorithm needs a (-1) at one single place,
+ * and we want to avoid that.
*/
-
- /* static prefix table */
- static uint8_t s_prefix_table[256];
-
- /* check pattern length and use appropriate prefix table */
- /* if the pattern exceeds static prefix table, allocate on the heap */
- register int useheap = matchlen > 255;
- register void* ptable = useheap ?
- calloc(matchlen+1, sizeof(size_t)): s_prefix_table;
-
- /* keep counter in registers */
+
+ // local prefix table
+ size_t s_prefix_table[CX_STRSTR_SBO_SIZE];
+
+ // check needle length and use appropriate prefix table
+ // if the pattern exceeds static prefix table, allocate on the heap
+ bool useheap = needle.length >= CX_STRSTR_SBO_SIZE;
+ register size_t *ptable = useheap ? calloc(needle.length + 1,
+ sizeof(size_t)) : s_prefix_table;
+
+ // keep counter in registers
register size_t i, j;
-
- /* fill prefix table */
- i = 0; j = 0;
- ptable_w(useheap, ptable, i, j);
- while (i < matchlen) {
- while (j >= 1 && match[j-1] != match[i]) {
- ptable_r(j, useheap, ptable, j-1);
- }
- i++; j++;
- ptable_w(useheap, ptable, i, j);
- }
- /* search */
- i = 0; j = 1;
- while (i < length) {
- while (j >= 1 && str[i] != match[j-1]) {
- ptable_r(j, useheap, ptable, j-1);
+ // fill prefix table
+ i = 0;
+ j = 0;
+ ptable[i] = j;
+ while (i < needle.length) {
+ while (j >= 1 && needle.ptr[j - 1] != needle.ptr[i]) {
+ j = ptable[j - 1];
}
- i++; j++;
- if (j-1 == matchlen) {
- size_t start = i - matchlen;
- result = str + start;
- resultlen = length - start;
+ i++;
+ j++;
+ ptable[i] = j;
+ }
+
+ // search
+ cxstring result = {NULL, 0};
+ i = 0;
+ j = 1;
+ while (i < haystack.length) {
+ while (j >= 1 && haystack.ptr[i] != needle.ptr[j - 1]) {
+ j = ptable[j - 1];
+ }
+ i++;
+ j++;
+ if (j - 1 == needle.length) {
+ size_t start = i - needle.length;
+ result.ptr = haystack.ptr + start;
+ result.length = haystack.length - start;
break;
}
}
- /* if prefix table was allocated on the heap, free it */
+ // if prefix table was allocated on the heap, free it
if (ptable != s_prefix_table) {
free(ptable);
}
-
- *newlen = resultlen;
- return result;
-}
-sstr_t scstrsstr(sstr_t string, scstr_t match) {
- sstr_t result;
-
- size_t reslen;
- const char *resstr = ucx_strstr(string.ptr, string.length, match.ptr, match.length, &reslen);
- if(!resstr) {
- result.ptr = NULL;
- result.length = 0;
- return result;
- }
-
- size_t pos = resstr - string.ptr;
- result.ptr = string.ptr + pos;
- result.length = reslen;
-
return result;
}
-scstr_t scstrscstr(scstr_t string, scstr_t match) {
- scstr_t result;
-
- size_t reslen;
- const char *resstr = ucx_strstr(string.ptr, string.length, match.ptr, match.length, &reslen);
- if(!resstr) {
- result.ptr = NULL;
- result.length = 0;
- return result;
- }
-
- size_t pos = resstr - string.ptr;
- result.ptr = string.ptr + pos;
- result.length = reslen;
-
- return result;
+cxmutstr cx_strstr_m(
+ cxmutstr haystack,
+ cxstring needle
+) {
+ cxstring result = cx_strstr(cx_strcast(haystack), needle);
+ return (cxmutstr) {(char *) result.ptr, result.length};
}
-#undef ptable_r
-#undef ptable_w
+size_t cx_strsplit(
+ cxstring string,
+ cxstring delim,
+ size_t limit,
+ cxstring *output
+) {
+ // special case: output limit is zero
+ if (limit == 0) return 0;
-sstr_t* scstrsplit(scstr_t s, scstr_t d, ssize_t *n) {
- return scstrsplit_a(ucx_default_allocator(), s, d, n);
-}
+ // special case: delimiter is empty
+ if (delim.length == 0) {
+ output[0] = string;
+ return 1;
+ }
-sstr_t* scstrsplit_a(UcxAllocator *allocator, scstr_t s, scstr_t d, ssize_t *n) {
- if (s.length == 0 || d.length == 0) {
- *n = -1;
- return NULL;
+ // special cases: delimiter is at least as large as the string
+ if (delim.length >= string.length) {
+ // exact match
+ if (cx_strcmp(string, delim) == 0) {
+ output[0] = cx_strn(string.ptr, 0);
+ output[1] = cx_strn(string.ptr + string.length, 0);
+ return 2;
+ } else {
+ // no match possible
+ output[0] = string;
+ return 1;
+ }
}
-
- /* special cases: delimiter is at least as large as the string */
- if (d.length >= s.length) {
- /* exact match */
- if (sstrcmp(s, d) == 0) {
- *n = 0;
- return NULL;
- } else /* no match possible */ {
- *n = 1;
- sstr_t *result = (sstr_t*) almalloc(allocator, sizeof(sstr_t));
- if(result) {
- *result = sstrdup_a(allocator, s);
+
+ size_t n = 0;
+ cxstring curpos = string;
+ while (1) {
+ ++n;
+ cxstring match = cx_strstr(curpos, delim);
+ if (match.length > 0) {
+ // is the limit reached?
+ if (n < limit) {
+ // copy the current string to the array
+ cxstring item = cx_strn(curpos.ptr, match.ptr - curpos.ptr);
+ output[n - 1] = item;
+ size_t processed = item.length + delim.length;
+ curpos.ptr += processed;
+ curpos.length -= processed;
} else {
- *n = -2;
+ // limit reached, copy the _full_ remaining string
+ output[n - 1] = curpos;
+ break;
}
- return result;
+ } else {
+ // no more matches, copy last string
+ output[n - 1] = curpos;
+ break;
}
}
-
- ssize_t nmax = *n;
- size_t arrlen = 16;
- sstr_t* result = (sstr_t*) alcalloc(allocator, arrlen, sizeof(sstr_t));
-
- if (result) {
- scstr_t curpos = s;
- ssize_t j = 1;
- while (1) {
- scstr_t match;
- /* optimize for one byte delimiters */
- if (d.length == 1) {
- match = curpos;
- for (size_t i = 0 ; i < curpos.length ; i++) {
- if (curpos.ptr[i] == *(d.ptr)) {
- match.ptr = curpos.ptr + i;
- break;
- }
- match.length--;
- }
- } else {
- match = scstrscstr(curpos, d);
- }
- if (match.length > 0) {
- /* is this our last try? */
- if (nmax == 0 || j < nmax) {
- /* copy the current string to the array */
- scstr_t item = scstrn(curpos.ptr, match.ptr - curpos.ptr);
- result[j-1] = sstrdup_a(allocator, item);
- size_t processed = item.length + d.length;
- curpos.ptr += processed;
- curpos.length -= processed;
-
- /* allocate memory for the next string */
- j++;
- if (j > arrlen) {
- arrlen *= 2;
- size_t reallocsz;
- sstr_t* reallocated = NULL;
- if(!ucx_szmul(arrlen, sizeof(sstr_t), &reallocsz)) {
- reallocated = (sstr_t*) alrealloc(
- allocator, result, reallocsz);
- }
- if (reallocated) {
- result = reallocated;
- } else {
- for (ssize_t i = 0 ; i < j-1 ; i++) {
- alfree(allocator, result[i].ptr);
- }
- alfree(allocator, result);
- *n = -2;
- return NULL;
- }
- }
- } else {
- /* nmax reached, copy the _full_ remaining string */
- result[j-1] = sstrdup_a(allocator, curpos);
- break;
- }
+
+ return n;
+}
+
+size_t cx_strsplit_a(
+ CxAllocator *allocator,
+ cxstring string,
+ cxstring delim,
+ size_t limit,
+ cxstring **output
+) {
+ // find out how many splits we're going to make and allocate memory
+ size_t n = 0;
+ cxstring curpos = string;
+ while (1) {
+ ++n;
+ cxstring match = cx_strstr(curpos, delim);
+ if (match.length > 0) {
+ // is the limit reached?
+ if (n < limit) {
+ size_t processed = match.ptr - curpos.ptr + delim.length;
+ curpos.ptr += processed;
+ curpos.length -= processed;
} else {
- /* no more matches, copy last string */
- result[j-1] = sstrdup_a(allocator, curpos);
+ // limit reached
break;
}
+ } else {
+ // no more matches
+ break;
}
- *n = j;
- } else {
- *n = -2;
}
+ *output = cxCalloc(allocator, n, sizeof(cxstring));
+ return cx_strsplit(string, delim, n, *output);
+}
- return result;
+size_t cx_strsplit_m(
+ cxmutstr string,
+ cxstring delim,
+ size_t limit,
+ cxmutstr *output
+) {
+ return cx_strsplit(cx_strcast(string),
+ delim, limit, (cxstring *) output);
+}
+
+size_t cx_strsplit_ma(
+ CxAllocator *allocator,
+ cxmutstr string,
+ cxstring delim,
+ size_t limit,
+ cxmutstr **output
+) {
+ return cx_strsplit_a(allocator, cx_strcast(string),
+ delim, limit, (cxstring **) output);
}
-int scstrcmp(scstr_t s1, scstr_t s2) {
+int cx_strcmp(
+ cxstring s1,
+ cxstring s2
+) {
if (s1.length == s2.length) {
return memcmp(s1.ptr, s2.ptr, s1.length);
} else if (s1.length > s2.length) {
}
}
-int scstrcasecmp(scstr_t s1, scstr_t s2) {
+int cx_strcasecmp(
+ cxstring s1,
+ cxstring s2
+) {
if (s1.length == s2.length) {
#ifdef _WIN32
return _strnicmp(s1.ptr, s2.ptr, s1.length);
}
}
-sstr_t scstrdup(scstr_t s) {
- return sstrdup_a(ucx_default_allocator(), s);
+int cx_strcmp_p(
+ void const *s1,
+ void const *s2
+) {
+ cxstring const *left = s1;
+ cxstring const *right = s2;
+ return cx_strcmp(*left, *right);
}
-sstr_t scstrdup_a(UcxAllocator *allocator, scstr_t s) {
- sstr_t newstring;
- newstring.ptr = (char*)almalloc(allocator, s.length + 1);
- if (newstring.ptr) {
- newstring.length = s.length;
- newstring.ptr[newstring.length] = 0;
-
- memcpy(newstring.ptr, s.ptr, s.length);
- } else {
- newstring.length = 0;
- }
-
- return newstring;
+int cx_strcasecmp_p(
+ void const *s1,
+ void const *s2
+) {
+ cxstring const *left = s1;
+ cxstring const *right = s2;
+ return cx_strcasecmp(*left, *right);
}
-
-static size_t ucx_strtrim(const char *s, size_t len, size_t *newlen) {
- const char *newptr = s;
- size_t length = len;
-
- while(length > 0 && isspace(*newptr)) {
- newptr++;
- length--;
- }
- while(length > 0 && isspace(newptr[length-1])) {
- length--;
+cxmutstr cx_strdup_a(
+ CxAllocator *allocator,
+ cxstring string
+) {
+ cxmutstr result = {
+ cxMalloc(allocator, string.length + 1),
+ string.length
+ };
+ if (result.ptr == NULL) {
+ result.length = 0;
+ return result;
}
-
- *newlen = length;
- return newptr - s;
-}
-
-sstr_t sstrtrim(sstr_t string) {
- sstr_t newstr;
- newstr.ptr = string.ptr
- + ucx_strtrim(string.ptr, string.length, &newstr.length);
- return newstr;
-}
-
-scstr_t scstrtrim(scstr_t string) {
- scstr_t newstr;
- newstr.ptr = string.ptr
- + ucx_strtrim(string.ptr, string.length, &newstr.length);
- return newstr;
+ memcpy(result.ptr, string.ptr, string.length);
+ result.ptr[string.length] = '\0';
+ return result;
}
-int scstrprefix(scstr_t string, scstr_t prefix) {
- if (string.length == 0) {
- return prefix.length == 0;
+cxstring cx_strtrim(cxstring string) {
+ cxstring result = string;
+ // TODO: optimize by comparing multiple bytes at once
+ while (result.length > 0 && isspace(*result.ptr)) {
+ result.ptr++;
+ result.length--;
}
- if (prefix.length == 0) {
- return 1;
- }
-
- if (prefix.length > string.length) {
- return 0;
- } else {
- return memcmp(string.ptr, prefix.ptr, prefix.length) == 0;
+ while (result.length > 0 && isspace(result.ptr[result.length - 1])) {
+ result.length--;
}
+ return result;
}
-int scstrsuffix(scstr_t string, scstr_t suffix) {
- if (string.length == 0) {
- return suffix.length == 0;
- }
- if (suffix.length == 0) {
- return 1;
- }
-
- if (suffix.length > string.length) {
- return 0;
- } else {
- return memcmp(string.ptr+string.length-suffix.length,
- suffix.ptr, suffix.length) == 0;
- }
+cxmutstr cx_strtrim_m(cxmutstr string) {
+ cxstring result = cx_strtrim(cx_strcast(string));
+ return (cxmutstr) {(char *) result.ptr, result.length};
}
-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;
- }
+bool cx_strprefix(
+ cxstring string,
+ cxstring prefix
+) {
+ if (string.length < prefix.length) return false;
+ return memcmp(string.ptr, prefix.ptr, prefix.length) == 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;
- }
+bool cx_strsuffix(
+ cxstring string,
+ cxstring suffix
+) {
+ if (string.length < suffix.length) return false;
+ return memcmp(string.ptr + string.length - suffix.length,
+ suffix.ptr, suffix.length) == 0;
}
-sstr_t scstrlower(scstr_t string) {
- sstr_t ret = sstrdup(string);
- for (size_t i = 0; i < ret.length ; i++) {
- ret.ptr[i] = tolower(ret.ptr[i]);
- }
- return ret;
+bool cx_strcaseprefix(
+ cxstring string,
+ cxstring prefix
+) {
+ if (string.length < prefix.length) return false;
+#ifdef _WIN32
+ return _strnicmp(string.ptr, prefix.ptr, prefix.length) == 0;
+#else
+ return strncasecmp(string.ptr, prefix.ptr, prefix.length) == 0;
+#endif
}
-sstr_t scstrlower_a(UcxAllocator *allocator, scstr_t string) {
- sstr_t ret = sstrdup_a(allocator, string);
- for (size_t i = 0; i < ret.length ; i++) {
- ret.ptr[i] = tolower(ret.ptr[i]);
- }
- return ret;
+bool cx_strcasesuffix(
+ cxstring string,
+ cxstring suffix
+) {
+ if (string.length < suffix.length) return false;
+#ifdef _WIN32
+ return _strnicmp(string.ptr+string.length-suffix.length,
+ suffix.ptr, suffix.length) == 0;
+#else
+ return strncasecmp(string.ptr + string.length - suffix.length,
+ suffix.ptr, suffix.length) == 0;
+#endif
}
-sstr_t scstrupper(scstr_t string) {
- sstr_t ret = sstrdup(string);
- for (size_t i = 0; i < ret.length ; i++) {
- ret.ptr[i] = toupper(ret.ptr[i]);
+void cx_strlower(cxmutstr string) {
+ cx_for_n(i, string.length) {
+ string.ptr[i] = (char) tolower(string.ptr[i]);
}
- return ret;
}
-sstr_t scstrupper_a(UcxAllocator *allocator, scstr_t string) {
- sstr_t ret = sstrdup_a(allocator, string);
- for (size_t i = 0; i < ret.length ; i++) {
- ret.ptr[i] = toupper(ret.ptr[i]);
+void cx_strupper(cxmutstr string) {
+ cx_for_n(i, string.length) {
+ string.ptr[i] = (char) toupper(string.ptr[i]);
}
- return ret;
}
-#define REPLACE_INDEX_BUFFER_MAX 100
+#ifndef CX_STRREPLACE_INDEX_BUFFER_SIZE
+#define CX_STRREPLACE_INDEX_BUFFER_SIZE 64
+#endif
-struct scstrreplace_ibuf {
- size_t* buf;
- unsigned int len; /* small indices */
- struct scstrreplace_ibuf* next;
+struct cx_strreplace_ibuf {
+ size_t *buf;
+ struct cx_strreplace_ibuf *next;
+ unsigned int len;
};
-static void scstrrepl_free_ibuf(struct scstrreplace_ibuf *buf) {
+static void cx_strrepl_free_ibuf(struct cx_strreplace_ibuf *buf) {
while (buf) {
- struct scstrreplace_ibuf *next = buf->next;
+ struct cx_strreplace_ibuf *next = buf->next;
free(buf->buf);
free(buf);
buf = next;
}
}
-sstr_t scstrreplacen_a(UcxAllocator *allocator, scstr_t str,
- scstr_t pattern, scstr_t replacement, size_t replmax) {
+cxmutstr cx_strreplacen_a(
+ CxAllocator *allocator,
+ cxstring str,
+ cxstring pattern,
+ cxstring replacement,
+ size_t replmax
+) {
if (pattern.length == 0 || pattern.length > str.length || replmax == 0)
- return sstrdup(str);
+ return cx_strdup_a(allocator, str);
- /* Compute expected buffer length */
+ // 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;
+ if (ibuflen > CX_STRREPLACE_INDEX_BUFFER_SIZE) {
+ ibuflen = CX_STRREPLACE_INDEX_BUFFER_SIZE;
}
- /* Allocate first index buffer */
- struct scstrreplace_ibuf *firstbuf, *curbuf;
- firstbuf = curbuf = calloc(1, sizeof(struct scstrreplace_ibuf));
- if (!firstbuf) return sstrn(NULL, 0);
+ // Allocate first index buffer
+ struct cx_strreplace_ibuf *firstbuf, *curbuf;
+ firstbuf = curbuf = calloc(1, sizeof(struct cx_strreplace_ibuf));
+ if (!firstbuf) return cx_mutstrn(NULL, 0);
firstbuf->buf = calloc(ibuflen, sizeof(size_t));
if (!firstbuf->buf) {
free(firstbuf);
- return sstrn(NULL, 0);
+ return cx_mutstrn(NULL, 0);
}
- /* Search occurrences */
- scstr_t searchstr = str;
+ // Search occurrences
+ cxstring searchstr = str;
size_t found = 0;
do {
- scstr_t match = scstrscstr(searchstr, pattern);
+ cxstring match = cx_strstr(searchstr, pattern);
if (match.length > 0) {
- /* Allocate next buffer in chain, if required */
+ // Allocate next buffer in chain, if required
if (curbuf->len == ibuflen) {
- struct scstrreplace_ibuf *nextbuf =
- calloc(1, sizeof(struct scstrreplace_ibuf));
+ struct cx_strreplace_ibuf *nextbuf =
+ calloc(1, sizeof(struct cx_strreplace_ibuf));
if (!nextbuf) {
- scstrrepl_free_ibuf(firstbuf);
- return sstrn(NULL, 0);
+ cx_strrepl_free_ibuf(firstbuf);
+ return cx_mutstrn(NULL, 0);
}
nextbuf->buf = calloc(ibuflen, sizeof(size_t));
if (!nextbuf->buf) {
free(nextbuf);
- scstrrepl_free_ibuf(firstbuf);
- return sstrn(NULL, 0);
+ cx_strrepl_free_ibuf(firstbuf);
+ return cx_mutstrn(NULL, 0);
}
curbuf->next = nextbuf;
curbuf = nextbuf;
}
- /* Record match index */
+ // Record match index
found++;
size_t idx = match.ptr - str.ptr;
curbuf->buf[curbuf->len++] = idx;
}
} while (searchstr.length > 0 && found < replmax);
- /* Allocate result string */
- sstr_t result;
+ // Allocate result string
+ cxmutstr result;
{
ssize_t adjlen = (ssize_t) replacement.length - (ssize_t) pattern.length;
size_t rcount = 0;
curbuf = curbuf->next;
} while (curbuf);
result.length = str.length + rcount * adjlen;
- result.ptr = almalloc(allocator, result.length);
+ result.ptr = cxMalloc(allocator, result.length + 1);
if (!result.ptr) {
- scstrrepl_free_ibuf(firstbuf);
- return sstrn(NULL, 0);
+ cx_strrepl_free_ibuf(firstbuf);
+ return cx_mutstrn(NULL, 0);
}
}
- /* Build result string */
+ // Build result string
curbuf = firstbuf;
size_t srcidx = 0;
- char* destptr = result.ptr;
+ char *destptr = result.ptr;
do {
for (size_t i = 0; i < curbuf->len; i++) {
- /* Copy source part up to next match*/
+ // Copy source part up to next match
size_t idx = curbuf->buf[i];
size_t srclen = idx - srcidx;
if (srclen > 0) {
- memcpy(destptr, str.ptr+srcidx, srclen);
+ memcpy(destptr, str.ptr + srcidx, srclen);
destptr += srclen;
srcidx += srclen;
}
- /* Copy the replacement and skip the source pattern */
+ // 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);
+ memcpy(destptr, str.ptr + srcidx, str.length - srcidx);
+
+ // Result is guaranteed to be zero-terminated
+ result.ptr[result.length] = '\0';
- /* Free index buffer */
- scstrrepl_free_ibuf(firstbuf);
+ // Free index buffer
+ cx_strrepl_free_ibuf(firstbuf);
return result;
}
-sstr_t scstrreplacen(scstr_t str, scstr_t pattern,
- scstr_t replacement, size_t replmax) {
- return scstrreplacen_a(ucx_default_allocator(),
- str, pattern, replacement, replmax);
-}
+CxStrtokCtx cx_strtok(
+ cxstring str,
+ cxstring delim,
+ size_t limit
+) {
+ CxStrtokCtx ctx;
+ ctx.str = str;
+ ctx.delim = delim;
+ ctx.limit = limit;
+ ctx.pos = 0;
+ ctx.next_pos = 0;
+ ctx.delim_pos = 0;
+ ctx.found = 0;
+ ctx.delim_more = NULL;
+ ctx.delim_more_count = 0;
+ return ctx;
+}
+
+CxStrtokCtx cx_strtok_m(
+ cxmutstr str,
+ cxstring delim,
+ size_t limit
+) {
+ return cx_strtok(cx_strcast(str), delim, limit);
+}
+
+bool cx_strtok_next(
+ CxStrtokCtx *ctx,
+ cxstring *token
+) {
+ // abortion criteria
+ if (ctx->found >= ctx->limit || ctx->delim_pos >= ctx->str.length) {
+ return false;
+ }
+
+ // determine the search start
+ cxstring haystack = cx_strsubs(ctx->str, ctx->next_pos);
+
+ // search the next delimiter
+ cxstring delim = cx_strstr(haystack, ctx->delim);
+
+ // if found, make delim capture exactly the delimiter
+ if (delim.length > 0) {
+ delim.length = ctx->delim.length;
+ }
+
+ // if more delimiters are specified, check them now
+ if (ctx->delim_more_count > 0) {
+ cx_for_n(i, ctx->delim_more_count) {
+ cxstring d = cx_strstr(haystack, ctx->delim_more[i]);
+ if (d.length > 0 && (delim.length == 0 || d.ptr < delim.ptr)) {
+ delim.ptr = d.ptr;
+ delim.length = ctx->delim_more[i].length;
+ }
+ }
+ }
+ // store the token information and adjust the context
+ ctx->found++;
+ ctx->pos = ctx->next_pos;
+ token->ptr = &ctx->str.ptr[ctx->pos];
+ ctx->delim_pos = delim.length == 0 ?
+ ctx->str.length : (size_t) (delim.ptr - ctx->str.ptr);
+ token->length = ctx->delim_pos - ctx->pos;
+ ctx->next_pos = ctx->delim_pos + delim.length;
-// type adjustment functions
-scstr_t ucx_sc2sc(scstr_t str) {
- return str;
+ return true;
}
-scstr_t ucx_ss2sc(sstr_t str) {
- scstr_t cs;
- cs.ptr = str.ptr;
- cs.length = str.length;
- return cs;
+
+bool cx_strtok_next_m(
+ CxStrtokCtx *ctx,
+ cxmutstr *token
+) {
+ return cx_strtok_next(ctx, (cxstring *) token);
}
-scstr_t ucx_ss2c_s(scstr_t c) {
- return c;
+
+void cx_strtok_delim(
+ CxStrtokCtx *ctx,
+ cxstring const *delim,
+ size_t count
+) {
+ ctx->delim_more = delim;
+ ctx->delim_more_count = count;
}
-/**
- * @mainpage UAP Common Extensions
- * Library with common and useful functions, macros and data structures.
- * <p>
- * Latest available source:<br>
- * <a href="https://sourceforge.net/projects/ucx/files/">
- * https://sourceforge.net/projects/ucx/files/</a>
- * </p>
- *
- * <p>
- * Repositories:<br>
- * <a href="https://sourceforge.net/p/ucx/code">
- * https://sourceforge.net/p/ucx/code</a>
- * - or -
- * <a href="https://develop.uap-core.de/hg/ucx">
- * https://develop.uap-core.de/hg/ucx</a>
- * </p>
- *
- * <h2>LICENCE</h2>
- *
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2023 Mike Becker, Olaf Wintermann All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* POSSIBILITY OF SUCH DAMAGE.
*/
-#include "ucx/ucx.h"
-
-int ucx_szmul_impl(size_t a, size_t b, size_t *result) {
- if(a == 0 || b == 0) {
+int cx_szmul_impl(
+ size_t a,
+ size_t b,
+ size_t *result
+) {
+ if (a == 0 || b == 0) {
*result = 0;
return 0;
}
size_t r = a * b;
- if(r / b == a) {
+ if (r / b == a) {
*result = r;
return 0;
} else {
*result = 0;
return 1;
}
-}
-
+}
\ No newline at end of file
+++ /dev/null
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "ucx/test.h"
-
-UcxTestSuite* ucx_test_suite_new() {
- UcxTestSuite* suite = (UcxTestSuite*) malloc(sizeof(UcxTestSuite));
- if (suite != NULL) {
- suite->success = 0;
- suite->failure = 0;
- suite->tests = NULL;
- }
-
- return suite;
-}
-
-void ucx_test_suite_free(UcxTestSuite* suite) {
- UcxTestList *l = suite->tests;
- while (l != NULL) {
- UcxTestList *e = l;
- l = l->next;
- free(e);
- }
- free(suite);
-}
-
-int ucx_test_register(UcxTestSuite* suite, UcxTest test) {
- if (suite->tests) {
- UcxTestList *newelem = (UcxTestList*) malloc(sizeof(UcxTestList));
- if (newelem) {
- newelem->test = test;
- newelem->next = NULL;
-
- UcxTestList *last = suite->tests;
- while (last->next) {
- last = last->next;
- }
- last->next = newelem;
-
- return EXIT_SUCCESS;
- } else {
- return EXIT_FAILURE;
- }
- } else {
- suite->tests = (UcxTestList*) malloc(sizeof(UcxTestList));
- if (suite->tests) {
- suite->tests->test = test;
- suite->tests->next = NULL;
-
- return EXIT_SUCCESS;
- } else {
- return EXIT_FAILURE;
- }
- }
-}
-
-void ucx_test_run(UcxTestSuite* suite, FILE* output) {
- suite->success = 0;
- suite->failure = 0;
- for (UcxTestList* elem = suite->tests ; elem ; elem = elem->next) {
- elem->test(suite, output);
- }
- fwrite("\nAll test completed.\n", 1, 21, output);
- fprintf(output, " Total: %u\n Success: %u\n Failure: %u\n",
- suite->success+suite->failure, suite->success, suite->failure);
-}
--- /dev/null
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "cx/tree.h"
+#include "cx/linked_list.h"
+
+#define CX_TR_PTR(cur, off) *((void**)(((char*)(cur))+(off)))
+
+void cx_tree_add_sibling(void *node, ptrdiff_t loc_prev, ptrdiff_t loc_next, ptrdiff_t loc_parent, void *new_node) {
+ cx_linked_list_add(&node, NULL, loc_prev, loc_next, new_node);
+
+ // optional parent link
+ if (loc_parent >= 0) {
+ CX_TR_PTR(new_node, loc_parent) = CX_TR_PTR(node, loc_parent);
+ }
+}
+
+void cx_tree_add_child(void **children_begin, void **children_end,
+ ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node,
+ ptrdiff_t loc_parent, void *parent) {
+ cx_linked_list_add(children_begin, children_end, loc_prev, loc_next, new_node);
+
+ // optional parent link
+ if (loc_parent >= 0) {
+ CX_TR_PTR(new_node, loc_parent) = parent;
+ }
+}
+++ /dev/null
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-/**
- * Allocator for custom memory management.
- *
- * A UCX allocator consists of a pointer to the memory area / pool and four
- * function pointers to memory management functions operating on this memory
- * area / pool. These functions shall behave equivalent to the standard libc
- * functions <code>malloc(), calloc(), realloc()</code> and <code>free()</code>.
- *
- * The signature of the memory management functions is based on the signature
- * of the respective libc function but each of them takes the pointer to the
- * memory area / pool as first argument.
- *
- * As the pointer to the memory area / pool can be arbitrarily chosen, any data
- * can be provided to the memory management functions. A UcxMempool is just
- * one example.
- *
- * @see mempool.h
- * @see UcxMap
- *
- * @file allocator.h
- * @author Mike Becker
- * @author Olaf Wintermann
- */
-
-#ifndef UCX_ALLOCATOR_H
-#define UCX_ALLOCATOR_H
-
-#include "ucx.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * A function pointer to the allocators <code>malloc()</code> function.
- * @see UcxAllocator
- */
-typedef void*(*ucx_allocator_malloc)(void *pool, size_t n);
-
-/**
- * A function pointer to the allocators <code>calloc()</code> function.
- * @see UcxAllocator
- */
-typedef void*(*ucx_allocator_calloc)(void *pool, size_t n, size_t size);
-
-/**
- * A function pointer to the allocators <code>realloc()</code> function.
- * @see UcxAllocator
- */
-typedef void*(*ucx_allocator_realloc)(void *pool, void *data, size_t n);
-
-/**
- * A function pointer to the allocators <code>free()</code> function.
- * @see UcxAllocator
- */
-typedef void(*ucx_allocator_free)(void *pool, void *data);
-
-/**
- * UCX allocator data structure containing memory management functions.
- */
-typedef struct {
- /** Pointer to an area of memory or a complex memory pool.
- * This pointer will be passed to any memory management function as first
- * argument.
- */
- void *pool;
- /**
- * The <code>malloc()</code> function for this allocator.
- */
- ucx_allocator_malloc malloc;
- /**
- * The <code>calloc()</code> function for this allocator.
- */
- ucx_allocator_calloc calloc;
- /**
- * The <code>realloc()</code> function for this allocator.
- */
- ucx_allocator_realloc realloc;
- /**
- * The <code>free()</code> function for this allocator.
- */
- ucx_allocator_free free;
-} UcxAllocator;
-
-/**
- * Returns a pointer to the default allocator.
- *
- * The default allocator contains wrappers to the standard libc memory
- * management functions. Use this function to get a pointer to a globally
- * available allocator. You may also define an own UcxAllocator by assigning
- * #UCX_ALLOCATOR_DEFAULT to a variable and pass the address of this variable
- * to any function that takes a UcxAllocator as argument. Note that using
- * this function is the recommended way of passing a default allocator, thus
- * it never runs out of scope.
- *
- * @return a pointer to the default allocator
- *
- * @see UCX_ALLOCATOR_DEFAULT
- */
-UcxAllocator *ucx_default_allocator();
-
-/**
- * A wrapper for the standard libc <code>malloc()</code> function.
- * @param ignore ignored (may be used by allocators for pooled memory)
- * @param n argument passed to <code>malloc()</code>
- * @return return value of <code>malloc()</code>
- */
-void *ucx_default_malloc(void *ignore, size_t n);
-/**
- * A wrapper for the standard libc <code>calloc()</code> function.
- * @param ignore ignored (may be used by allocators for pooled memory)
- * @param n argument passed to <code>calloc()</code>
- * @param size argument passed to <code>calloc()</code>
- * @return return value of <code>calloc()</code>
- */
-void *ucx_default_calloc(void *ignore, size_t n, size_t size);
-/**
- * A wrapper for the standard libc <code>realloc()</code> function.
- * @param ignore ignored (may be used by allocators for pooled memory)
- * @param data argumend passed to <code>realloc()</code>
- * @param n argument passed to <code>realloc()</code>
- * @return return value of <code>realloc()</code>
- */
-void *ucx_default_realloc(void *ignore, void *data, size_t n);
-/**
- * A wrapper for the standard libc <code>free()</code> function.
- * @param ignore ignored (may be used by allocators for pooled memory)
- * @param data argument passed to <code>free()</code>
- */
-void ucx_default_free(void *ignore, void *data);
-
-/**
- * Shorthand for calling an allocators malloc function.
- * @param allocator the allocator to use
- * @param n size of space to allocate
- * @return a pointer to the allocated memory area
- */
-#define almalloc(allocator, n) ((allocator)->malloc((allocator)->pool, n))
-
-/**
- * Shorthand for calling an allocators calloc function.
- * @param allocator the allocator to use
- * @param n the count of elements the space should be allocated for
- * @param size the size of each element
- * @return a pointer to the allocated memory area
- */
-#define alcalloc(allocator, n, size) \
- ((allocator)->calloc((allocator)->pool, n, size))
-
-/**
- * Shorthand for calling an allocators realloc function.
- * @param allocator the allocator to use
- * @param ptr the pointer to the memory area that shall be reallocated
- * @param n the new size of the allocated memory area
- * @return a pointer to the reallocated memory area
- */
-#define alrealloc(allocator, ptr, n) \
- ((allocator)->realloc((allocator)->pool, ptr, n))
-
-/**
- * Shorthand for calling an allocators free function.
- * @param allocator the allocator to use
- * @param ptr the pointer to the memory area that shall be freed
- */
-#define alfree(allocator, ptr) ((allocator)->free((allocator)->pool, ptr))
-
-/**
- * Convenient macro for a default allocator <code>struct</code> definition.
- */
-#define UCX_ALLOCATOR_DEFAULT {NULL, \
- ucx_default_malloc, ucx_default_calloc, ucx_default_realloc, \
- ucx_default_free }
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* UCX_ALLOCATOR_H */
-
+++ /dev/null
-/*
- * 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 */
-
+++ /dev/null
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-
-/**
- * @file avl.h
- *
- * AVL tree implementation.
- *
- * This binary search tree implementation allows average O(1) insertion and
- * removal of elements (excluding binary search time).
- *
- * @author Mike Becker
- * @author Olaf Wintermann
- */
-
-#ifndef UCX_AVL_H
-#define UCX_AVL_H
-
-#include "ucx.h"
-#include "allocator.h"
-#include <inttypes.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * UCX AVL Node type.
- *
- * @see UcxAVLNode
- */
-typedef struct UcxAVLNode UcxAVLNode;
-
-/**
- * UCX AVL Node.
- */
-struct UcxAVLNode {
- /**
- * The key for this node.
- */
- intptr_t key;
- /**
- * Data contained by this node.
- */
- void *value;
- /**
- * The height of this (sub)-tree.
- */
- size_t height;
- /**
- * Parent node.
- */
- UcxAVLNode *parent;
- /**
- * Root node of left subtree.
- */
- UcxAVLNode *left;
- /**
- * Root node of right subtree.
- */
- UcxAVLNode *right;
-};
-
-/**
- * UCX AVL Tree.
- */
-typedef struct {
- /**
- * The UcxAllocator that shall be used to manage the memory for node data.
- */
- UcxAllocator *allocator;
- /**
- * Root node of the tree.
- */
- UcxAVLNode *root;
- /**
- * Compare function that shall be used to compare the UcxAVLNode keys.
- * @see UcxAVLNode.key
- */
- cmp_func cmpfunc;
- /**
- * Custom user data.
- * This data will also be provided to the cmpfunc.
- */
- void *userdata;
-} UcxAVLTree;
-
-/**
- * Initializes a new UcxAVLTree with a default allocator.
- *
- * @param cmpfunc the compare function that shall be used
- * @return a new UcxAVLTree object
- * @see ucx_avl_new_a()
- */
-UcxAVLTree *ucx_avl_new(cmp_func cmpfunc);
-
-/**
- * Initializes a new UcxAVLTree with the specified allocator.
- *
- * The cmpfunc should be capable of comparing two keys within this AVL tree.
- * So if you want to use null terminated strings as keys, you could use the
- * ucx_cmp_str() function here.
- *
- * @param cmpfunc the compare function that shall be used
- * @param allocator the UcxAllocator that shall be used
- * @return a new UcxAVLTree object
- */
-UcxAVLTree *ucx_avl_new_a(cmp_func cmpfunc, UcxAllocator *allocator);
-
-/**
- * Destroys a UcxAVLTree.
- *
- * Note, that the contents are not automatically freed.
- * Use may use #ucx_avl_free_content() before calling this function.
- *
- * @param tree the tree to destroy
- * @see ucx_avl_free_content()
- */
-void ucx_avl_free(UcxAVLTree *tree);
-
-/**
- * Frees the contents of a UcxAVLTree.
- *
- * This is a convenience function that iterates over the tree and passes all
- * values to the specified destructor function.
- *
- * If no destructor is specified (<code>NULL</code>), the free() function of
- * the tree's own allocator is used.
- *
- * You must ensure, that it is valid to pass each value in the map to the same
- * destructor function.
- *
- * You should free the entire tree afterwards, as the contents will be invalid.
- *
- * @param tree for which the contents shall be freed
- * @param destr optional pointer to a destructor function
- * @see ucx_avl_free()
- */
-void ucx_avl_free_content(UcxAVLTree *tree, ucx_destructor destr);
-
-/**
- * Macro for initializing a new UcxAVLTree with the default allocator and a
- * ucx_cmp_ptr() compare function.
- *
- * @return a new default UcxAVLTree object
- */
-#define ucx_avl_default_new() \
- ucx_avl_new_a(ucx_cmp_ptr, ucx_default_allocator())
-
-/**
- * Gets the node from the tree, that is associated with the specified key.
- * @param tree the UcxAVLTree
- * @param key the key
- * @return the node (or <code>NULL</code>, if the key is not present)
- */
-UcxAVLNode *ucx_avl_get_node(UcxAVLTree *tree, intptr_t key);
-
-/**
- * Gets the value from the tree, that is associated with the specified key.
- * @param tree the UcxAVLTree
- * @param key the key
- * @return the value (or <code>NULL</code>, if the key is not present)
- */
-void *ucx_avl_get(UcxAVLTree *tree, intptr_t key);
-
-/**
- * A mode for #ucx_avl_find_node() with the same behavior as
- * #ucx_avl_get_node().
- */
-#define UCX_AVL_FIND_EXACT 0
-/**
- * A mode for #ucx_avl_find_node() finding the node whose key is at least
- * as large as the specified key.
- */
-#define UCX_AVL_FIND_LOWER_BOUNDED 1
-/**
- * A mode for #ucx_avl_find_node() finding the node whose key is at most
- * as large as the specified key.
- */
-#define UCX_AVL_FIND_UPPER_BOUNDED 2
-/**
- * A mode for #ucx_avl_find_node() finding the node with a key that is as close
- * to the specified key as possible. If the key is present, the behavior is
- * like #ucx_avl_get_node(). This mode only returns <code>NULL</code> on
- * empty trees.
- */
-#define UCX_AVL_FIND_CLOSEST 3
-
-/**
- * Finds a node within the tree. The following modes are supported:
- * <ul>
- * <li>#UCX_AVL_FIND_EXACT: the same behavior as #ucx_avl_get_node()</li>
- * <li>#UCX_AVL_FIND_LOWER_BOUNDED: finds the node whose key is at least
- * as large as the specified key</li>
- * <li>#UCX_AVL_FIND_UPPER_BOUNDED: finds the node whose key is at most
- * as large as the specified key</li>
- * <li>#UCX_AVL_FIND_CLOSEST: finds the node with a key that is as close to
- * the specified key as possible. If the key is present, the behavior is
- * like #ucx_avl_get_node(). This mode only returns <code>NULL</code> on
- * empty trees.</li>
- * </ul>
- *
- * The distance function provided MUST agree with the compare function of
- * the AVL tree.
- *
- * @param tree the UcxAVLTree
- * @param key the key
- * @param dfnc the distance function
- * @param mode the find mode
- * @return the node (or <code>NULL</code>, if no node can be found)
- */
-UcxAVLNode *ucx_avl_find_node(UcxAVLTree *tree, intptr_t key,
- distance_func dfnc, int mode);
-
-/**
- * Finds a value within the tree.
- * See #ucx_avl_find_node() for details.
- *
- * @param tree the UcxAVLTree
- * @param key the key
- * @param dfnc the distance function
- * @param mode the find mode
- * @return the value (or <code>NULL</code>, if no value can be found)
- */
-void *ucx_avl_find(UcxAVLTree *tree, intptr_t key,
- distance_func dfnc, int mode);
-
-/**
- * Puts a key/value pair into the tree.
- *
- * Attention: use this function only, if a possible old value does not need
- * to be preserved.
- *
- * @param tree the UcxAVLTree
- * @param key the key
- * @param value the new value
- * @return zero, if and only if the operation succeeded
- */
-int ucx_avl_put(UcxAVLTree *tree, intptr_t key, void *value);
-
-/**
- * Puts a key/value pair into the tree.
- *
- * This is a secure function which saves the old value to the variable pointed
- * at by oldvalue.
- *
- * @param tree the UcxAVLTree
- * @param key the key
- * @param value the new value
- * @param oldvalue optional: a pointer to the location where a possible old
- * value shall be stored
- * @return zero, if and only if the operation succeeded
- */
-int ucx_avl_put_s(UcxAVLTree *tree, intptr_t key, void *value, void **oldvalue);
-
-/**
- * Removes a node from the AVL tree.
- *
- * Note: the specified node is logically removed. The tree implementation
- * decides which memory area is freed. In most cases the here provided node
- * is freed, so its further use is generally undefined.
- *
- * @param tree the UcxAVLTree
- * @param node the node to remove
- * @return zero, if and only if an element has been removed
- */
-int ucx_avl_remove_node(UcxAVLTree *tree, UcxAVLNode *node);
-
-/**
- * Removes an element from the AVL tree.
- *
- * @param tree the UcxAVLTree
- * @param key the key
- * @return zero, if and only if an element has been removed
- */
-int ucx_avl_remove(UcxAVLTree *tree, intptr_t key);
-
-/**
- * Removes an element from the AVL tree.
- *
- * This is a secure function which saves the old key and value data from node
- * to the variables at the location of oldkey and oldvalue (if specified), so
- * they can be freed afterwards (if necessary).
- *
- * Note: the returned key in oldkey is possibly not the same as the provided
- * key for the lookup (in terms of memory location).
- *
- * @param tree the UcxAVLTree
- * @param key the key of the element to remove
- * @param oldkey optional: a pointer to the location where the old key shall be
- * stored
- * @param oldvalue optional: a pointer to the location where the old value
- * shall be stored
- * @return zero, if and only if an element has been removed
- */
-int ucx_avl_remove_s(UcxAVLTree *tree, intptr_t key,
- intptr_t *oldkey, void **oldvalue);
-
-/**
- * Counts the nodes in the specified UcxAVLTree.
- * @param tree the AVL tree
- * @return the node count
- */
-size_t ucx_avl_count(UcxAVLTree *tree);
-
-/**
- * Finds the in-order predecessor of the given node.
- * @param node an AVL node
- * @return the in-order predecessor of the given node, or <code>NULL</code> if
- * the given node is the in-order minimum
- */
-UcxAVLNode* ucx_avl_pred(UcxAVLNode* node);
-
-/**
- * Finds the in-order successor of the given node.
- * @param node an AVL node
- * @return the in-order successor of the given node, or <code>NULL</code> if
- * the given node is the in-order maximum
- */
-UcxAVLNode* ucx_avl_succ(UcxAVLNode* node);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* UCX_AVL_H */
-
+++ /dev/null
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-/**
- * @file buffer.h
- *
- * Advanced buffer implementation.
- *
- * Instances of UcxBuffer can be used to read from or to write to like one
- * would do with a stream. This allows the use of ucx_stream_copy() to copy
- * contents from one buffer to another.
- *
- * Some features for convenient use of the buffer
- * can be enabled. See the documentation of the macro constants for more
- * information.
- *
- * @author Mike Becker
- * @author Olaf Wintermann
- */
-
-#ifndef UCX_BUFFER_H
-#define UCX_BUFFER_H
-
-#include "ucx.h"
-#include <sys/types.h>
-#include <stdio.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * No buffer features enabled (all flags cleared).
- */
-#define UCX_BUFFER_DEFAULT 0x00
-
-/**
- * If this flag is enabled, the buffer will automatically free its contents.
- */
-#define UCX_BUFFER_AUTOFREE 0x01
-
-/**
- * If this flag is enabled, the buffer will automatically extends its capacity.
- */
-#define UCX_BUFFER_AUTOEXTEND 0x02
-
-/** UCX Buffer. */
-typedef struct {
- /** A pointer to the buffer contents. */
- char *space;
- /** Current position of the buffer. */
- size_t pos;
- /** Current capacity (i.e. maximum size) of the buffer. */
- size_t capacity;
- /** Current size of the buffer content. */
- size_t size;
- /**
- * Flag register for buffer features.
- * @see #UCX_BUFFER_DEFAULT
- * @see #UCX_BUFFER_AUTOFREE
- * @see #UCX_BUFFER_AUTOEXTEND
- */
- int flags;
-} UcxBuffer;
-
-/**
- * Creates a new buffer.
- *
- * <b>Note:</b> you may provide <code>NULL</code> as argument for
- * <code>space</code>. Then this function will allocate the space and enforce
- * the #UCX_BUFFER_AUTOFREE flag.
- *
- * @param space pointer to the memory area, or <code>NULL</code> to allocate
- * new memory
- * @param capacity the capacity of the buffer
- * @param flags buffer features (see UcxBuffer.flags)
- * @return the new buffer
- */
-UcxBuffer *ucx_buffer_new(void *space, size_t capacity, int flags);
-
-/**
- * Destroys a buffer.
- *
- * If the #UCX_BUFFER_AUTOFREE feature is enabled, the contents of the buffer
- * are also freed.
- *
- * @param buffer the buffer to destroy
- */
-void ucx_buffer_free(UcxBuffer* buffer);
-
-/**
- * Creates a new buffer and fills it with extracted content from another buffer.
- *
- * <b>Note:</b> the #UCX_BUFFER_AUTOFREE feature is enforced for the new buffer.
- *
- * @param src the source buffer
- * @param start the start position of extraction
- * @param length the count of bytes to extract (must not be zero)
- * @param flags feature mask for the new buffer
- * @return a new buffer containing the extraction
- */
-UcxBuffer* ucx_buffer_extract(UcxBuffer *src,
- size_t start, size_t length, int flags);
-
-/**
- * A shorthand macro for the full extraction of the buffer.
- *
- * @param src the source buffer
- * @param flags feature mask for the new buffer
- * @return a new buffer with the extracted content
- */
-#define ucx_buffer_clone(src,flags) \
- ucx_buffer_extract(src, 0, (src)->capacity, flags)
-
-
-/**
- * Shifts the contents of the buffer by the given offset.
- *
- * If the offset is positive, the contents are shifted to the right.
- * If auto extension is enabled, the buffer grows, if necessary.
- * In case the auto extension fails, this function returns a non-zero value and
- * no contents are changed.
- * If auto extension is disabled, the contents that do not fit into the buffer
- * are discarded.
- *
- * If the offset is negative, the contents are shifted to the left where the
- * first <code>shift</code> bytes are discarded.
- * The new size of the buffer is the old size minus
- * the absolute shift value.
- * If this value is larger than the buffer size, the buffer is emptied (but
- * not cleared, see the security note below).
- *
- * The buffer position gets shifted alongside with the content but is kept
- * within the boundaries of the buffer.
- *
- * <b>Security note:</b> the shifting operation does <em>not</em> erase the
- * previously occupied memory cells. You can easily do that manually, e.g. by
- * calling <code>memset(buffer->space, 0, shift)</code> for a right shift or
- * <code>memset(buffer->size, 0, buffer->capacity-buffer->size)</code>
- * for a left shift.
- *
- * @param buffer the buffer
- * @param shift the shift offset (negative means left shift)
- * @return 0 on success, non-zero if a required auto-extension fails
- */
-int ucx_buffer_shift(UcxBuffer* buffer, off_t shift);
-
-/**
- * Shifts the buffer to the right.
- * See ucx_buffer_shift() for details.
- *
- * @param buffer the buffer
- * @param shift the shift offset
- * @return 0 on success, non-zero if a required auto-extension fails
- * @see ucx_buffer_shift()
- */
-int ucx_buffer_shift_right(UcxBuffer* buffer, size_t shift);
-
-/**
- * Shifts the buffer to the left.
- *
- * See ucx_buffer_shift() for details. Note, however, that this method expects
- * a positive shift offset.
- *
- * Since a left shift cannot fail due to memory allocation problems, this
- * function always returns zero.
- *
- * @param buffer the buffer
- * @param shift the shift offset
- * @return always zero
- * @see ucx_buffer_shift()
- */
-int ucx_buffer_shift_left(UcxBuffer* buffer, size_t shift);
-
-
-/**
- * Moves the position of the buffer.
- *
- * The new position is relative to the <code>whence</code> argument.
- *
- * SEEK_SET marks the start of the buffer.
- * SEEK_CUR marks the current position.
- * SEEK_END marks the end of the buffer.
- *
- * With an offset of zero, this function sets the buffer position to zero
- * (SEEK_SET), the buffer size (SEEK_END) or leaves the buffer position
- * unchanged (SEEK_CUR).
- *
- * @param buffer
- * @param offset position offset relative to <code>whence</code>
- * @param whence one of SEEK_SET, SEEK_CUR or SEEK_END
- * @return 0 on success, non-zero if the position is invalid
- *
- */
-int ucx_buffer_seek(UcxBuffer *buffer, off_t offset, int whence);
-
-/**
- * Clears the buffer by resetting the position and deleting the data.
- *
- * The data is deleted by a zeroing it with call to <code>memset()</code>.
- *
- * @param buffer the buffer to be cleared
- */
-#define ucx_buffer_clear(buffer) memset((buffer)->space, 0, (buffer)->size); \
- (buffer)->size = 0; (buffer)->pos = 0;
-
-/**
- * Tests, if the buffer position has exceeded the buffer capacity.
- *
- * @param buffer the buffer to test
- * @return non-zero, if the current buffer position has exceeded the last
- * available byte of the buffer.
- */
-int ucx_buffer_eof(UcxBuffer *buffer);
-
-
-/**
- * Extends the capacity of the buffer.
- *
- * <b>Note:</b> The buffer capacity increased by a power of two. I.e.
- * the buffer capacity is doubled, as long as it would not hold the current
- * content plus the additional required bytes.
- *
- * <b>Attention:</b> the argument provided is the number of <i>additional</i>
- * bytes the buffer shall hold. It is <b>NOT</b> the total number of bytes the
- * buffer shall hold.
- *
- * @param buffer the buffer to extend
- * @param additional_bytes the number of additional bytes the buffer shall
- * <i>at least</i> hold
- * @return 0 on success or a non-zero value on failure
- */
-int ucx_buffer_extend(UcxBuffer *buffer, size_t additional_bytes);
-
-/**
- * Writes data to a UcxBuffer.
- *
- * The position of the buffer is increased by the number of bytes written.
- *
- * @param ptr a pointer to the memory area containing the bytes to be written
- * @param size the length of one element
- * @param nitems the element count
- * @param buffer the UcxBuffer to write to
- * @return the total count of bytes written
- */
-size_t ucx_buffer_write(const void *ptr, size_t size, size_t nitems,
- UcxBuffer *buffer);
-
-/**
- * Reads data from a UcxBuffer.
- *
- * The position of the buffer is increased by the number of bytes read.
- *
- * @param ptr a pointer to the memory area where to store the read data
- * @param size the length of one element
- * @param nitems the element count
- * @param buffer the UcxBuffer to read from
- * @return the total number of elements read
- */
-size_t ucx_buffer_read(void *ptr, size_t size, size_t nitems,
- UcxBuffer *buffer);
-
-/**
- * Writes a character to a buffer.
- *
- * The least significant byte of the argument is written to the buffer. If the
- * end of the buffer is reached and #UCX_BUFFER_AUTOEXTEND feature is enabled,
- * the buffer capacity is extended by ucx_buffer_extend(). If the feature is
- * disabled or buffer extension fails, <code>EOF</code> is returned.
- *
- * On successful write the position of the buffer is increased.
- *
- * @param buffer the buffer to write to
- * @param c the character to write as <code>int</code> value
- * @return the byte that has bean written as <code>int</code> value or
- * <code>EOF</code> when the end of the stream is reached and automatic
- * extension is not enabled or not possible
- */
-int ucx_buffer_putc(UcxBuffer *buffer, int c);
-
-/**
- * Gets a character from a buffer.
- *
- * The current position of the buffer is increased after a successful read.
- *
- * @param buffer the buffer to read from
- * @return the character as <code>int</code> value or <code>EOF</code>, if the
- * end of the buffer is reached
- */
-int ucx_buffer_getc(UcxBuffer *buffer);
-
-/**
- * Writes a string to a buffer.
- *
- * @param buffer the buffer
- * @param str the string
- * @return the number of bytes written
- */
-size_t ucx_buffer_puts(UcxBuffer *buffer, const char *str);
-
-/**
- * Returns the complete buffer content as sstr_t.
- * @param buffer the buffer
- * @return the result of <code>sstrn()</code> with the buffer space and size
- * as arguments
- */
-#define ucx_buffer_to_sstr(buffer) sstrn((buffer)->space, (buffer)->size)
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* UCX_BUFFER_H */
-
+++ /dev/null
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-/**
- * Doubly linked list implementation.
- *
- * @file list.h
- * @author Mike Becker
- * @author Olaf Wintermann
- */
-
-#ifndef UCX_LIST_H
-#define UCX_LIST_H
-
-#include "ucx.h"
-#include "allocator.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Loop statement for UCX lists.
- *
- * The first argument is the name of the iteration variable. The scope of
- * this variable is limited to the <code>UCX_FOREACH</code> statement.
- *
- * The second argument is a pointer to the list. In most cases this will be the
- * pointer to the first element of the list, but it may also be an arbitrary
- * element of the list. The iteration will then start with that element.
- *
- * @param list The first element of the list
- * @param elem The variable name of the element
- */
-#define UCX_FOREACH(elem,list) \
- for (UcxList* elem = (UcxList*) list ; elem != NULL ; elem = elem->next)
-
-/**
- * UCX list type.
- * @see UcxList
- */
-typedef struct UcxList UcxList;
-
-/**
- * UCX list structure.
- */
-struct UcxList {
- /**
- * List element payload.
- */
- void *data;
- /**
- * Pointer to the next list element or <code>NULL</code>, if this is the
- * last element.
- */
- UcxList *next;
- /**
- * Pointer to the previous list element or <code>NULL</code>, if this is
- * the first element.
- */
- UcxList *prev;
-};
-
-/**
- * Creates an element-wise copy of a list.
- *
- * This function clones the specified list by creating new list elements and
- * copying the data with the specified copy_func(). If no copy_func() is
- * specified, a shallow copy is created and the new list will reference the
- * same data as the source list.
- *
- * @param list the list to copy
- * @param cpyfnc a pointer to the function that shall copy an element (may be
- * <code>NULL</code>)
- * @param data additional data for the copy_func()
- * @return a pointer to the copy
- */
-UcxList *ucx_list_clone(const UcxList *list, copy_func cpyfnc, void* data);
-
-/**
- * Creates an element-wise copy of a list using a UcxAllocator.
- *
- * See ucx_list_clone() for details.
- *
- * You might want to pass the allocator via the <code>data</code> parameter,
- * to access it within the copy function for making deep copies.
- *
- * @param allocator the allocator to use
- * @param list the list to copy
- * @param cpyfnc a pointer to the function that shall copy an element (may be
- * <code>NULL</code>)
- * @param data additional data for the copy_func()
- * @return a pointer to the copy
- * @see ucx_list_clone()
- */
-UcxList *ucx_list_clone_a(UcxAllocator *allocator, const UcxList *list,
- copy_func cpyfnc, void* data);
-
-/**
- * Compares two UCX lists element-wise by using a compare function.
- *
- * Each element of the two specified lists are compared by using the specified
- * compare function and the additional data. The type and content of this
- * additional data depends on the cmp_func() used.
- *
- * If the list pointers denote elements within a list, the lists are compared
- * starting with the denoted elements. Thus any previous elements are not taken
- * into account. This might be useful to check, if certain list tails match
- * each other.
- *
- * @param list1 the first list
- * @param list2 the second list
- * @param cmpfnc the compare function
- * @param data additional data for the compare function
- * @return 1, if and only if the two lists equal element-wise, 0 otherwise
- */
-int ucx_list_equals(const UcxList *list1, const UcxList *list2,
- cmp_func cmpfnc, void* data);
-
-/**
- * Destroys the entire list.
- *
- * The members of the list are not automatically freed, so ensure they are
- * otherwise referenced or destroyed by ucx_list_free_contents().
- * Otherwise, a memory leak is likely to occur.
- *
- * <b>Caution:</b> the argument <b>MUST</b> denote an entire list (i.e. a call
- * to ucx_list_first() on the argument must return the argument itself)
- *
- * @param list the list to free
- * @see ucx_list_free_contents()
- */
-void ucx_list_free(UcxList *list);
-
-/**
- * Destroys the entire list using a UcxAllocator.
- *
- * See ucx_list_free() for details.
- *
- * @param allocator the allocator to use
- * @param list the list to free
- * @see ucx_list_free()
- */
-void ucx_list_free_a(UcxAllocator *allocator, UcxList *list);
-
-/**
- * Destroys the contents of the specified list by calling the specified
- * destructor on each of them.
- *
- * Note, that the contents are not usable afterwards and the list should be
- * destroyed with ucx_list_free().
- *
- * If no destructor is specified (<code>NULL</code>), stdlib's free() is used.
- *
- * @param list the list for which the contents shall be freed
- * @param destr optional destructor function
- * @see ucx_list_free()
- */
-void ucx_list_free_content(UcxList* list, ucx_destructor destr);
-
-
-/**
- * Inserts an element at the end of the list.
- *
- * This is generally an O(n) operation, as the end of the list is retrieved with
- * ucx_list_last().
- *
- * @param list the list where to append the data, or <code>NULL</code> to
- * create a new list
- * @param data the data to insert
- * @return <code>list</code>, if it is not <code>NULL</code> or a pointer to
- * the newly created list otherwise
- */
-UcxList *ucx_list_append(UcxList *list, void *data);
-
-/**
- * Inserts an element at the end of the list using a UcxAllocator.
- *
- * See ucx_list_append() for details.
- *
- * @param allocator the allocator to use
- * @param list the list where to append the data, or <code>NULL</code> to
- * create a new list
- * @param data the data to insert
- * @return <code>list</code>, if it is not <code>NULL</code> or a pointer to
- * the newly created list otherwise
- * @see ucx_list_append()
- */
-UcxList *ucx_list_append_a(UcxAllocator *allocator, UcxList *list, void *data);
-
-
-/**
- * Inserts an element at the beginning of the list.
- *
- * You <i>should</i> overwrite the old list pointer by calling
- * <code>mylist = ucx_list_prepend(mylist, mydata);</code>. However, you may
- * also perform successive calls of ucx_list_prepend() on the same list pointer,
- * as this function always searchs for the head of the list with
- * ucx_list_first().
- *
- * @param list the list where to insert the data or <code>NULL</code> to create
- * a new list
- * @param data the data to insert
- * @return a pointer to the new list head
- */
-UcxList *ucx_list_prepend(UcxList *list, void *data);
-
-/**
- * Inserts an element at the beginning of the list using a UcxAllocator.
- *
- * See ucx_list_prepend() for details.
- *
- * @param allocator the allocator to use
- * @param list the list where to insert the data or <code>NULL</code> to create
- * a new list
- * @param data the data to insert
- * @return a pointer to the new list head
- * @see ucx_list_prepend()
- */
-UcxList *ucx_list_prepend_a(UcxAllocator *allocator, UcxList *list, void *data);
-
-/**
- * Concatenates two lists.
- *
- * Either of the two arguments may be <code>NULL</code>.
- *
- * This function modifies the references to the next/previous element of
- * the last/first element of <code>list1</code>/<code>
- * list2</code>.
- *
- * @param list1 first list
- * @param list2 second list
- * @return if <code>list1</code> is <code>NULL</code>, <code>list2</code> is
- * returned, otherwise <code>list1</code> is returned
- */
-UcxList *ucx_list_concat(UcxList *list1, UcxList *list2);
-
-/**
- * Returns the first element of a list.
- *
- * If the argument is the list pointer, it is directly returned. Otherwise
- * this function traverses to the first element of the list and returns the
- * list pointer.
- *
- * @param elem one element of the list
- * @return the first element of the list, the specified element is a member of
- */
-UcxList *ucx_list_first(const UcxList *elem);
-
-/**
- * Returns the last element of a list.
- *
- * If the argument has no successor, it is the last element and therefore
- * directly returned. Otherwise this function traverses to the last element of
- * the list and returns it.
- *
- * @param elem one element of the list
- * @return the last element of the list, the specified element is a member of
- */
-UcxList *ucx_list_last(const UcxList *elem);
-
-/**
- * Returns the list element at the specified index.
- *
- * @param list the list to retrieve the element from
- * @param index index of the element to return
- * @return the element at the specified index or <code>NULL</code>, if the
- * index is greater than the list size
- */
-UcxList *ucx_list_get(const UcxList *list, size_t index);
-
-/**
- * Returns the index of an element.
- *
- * @param list the list where to search for the element
- * @param elem the element to find
- * @return the index of the element or -1 if the list does not contain the
- * element
- */
-ssize_t ucx_list_indexof(const UcxList *list, const UcxList *elem);
-
-/**
- * Returns the element count of the list.
- *
- * @param list the list whose elements are counted
- * @return the element count
- */
-size_t ucx_list_size(const UcxList *list);
-
-/**
- * Returns the index of an element containing the specified data.
- *
- * This function uses a cmp_func() to compare the data of each list element
- * with the specified data. If no cmp_func is provided, the pointers are
- * compared.
- *
- * If the list contains the data more than once, the index of the first
- * occurrence is returned.
- *
- * @param list the list where to search for the data
- * @param elem the element data
- * @param cmpfnc the compare function
- * @param data additional data for the compare function
- * @return the index of the element containing the specified data or -1 if the
- * data is not found in this list
- */
-ssize_t ucx_list_find(const UcxList *list, void *elem,
- cmp_func cmpfnc, void *data);
-
-/**
- * Checks, if a list contains a specific element.
- *
- * An element is found, if ucx_list_find() returns a value greater than -1.
- *
- * @param list the list where to search for the data
- * @param elem the element data
- * @param cmpfnc the compare function
- * @param data additional data for the compare function
- * @return 1, if and only if the list contains the specified element data
- * @see ucx_list_find()
- */
-int ucx_list_contains(const UcxList *list, void *elem,
- cmp_func cmpfnc, void *data);
-
-/**
- * Sorts a UcxList with natural merge sort.
- *
- * This function uses O(n) additional temporary memory for merge operations
- * that is automatically freed after each merge.
- *
- * As the head of the list might change, you <b>MUST</b> call this function
- * as follows: <code>mylist = ucx_list_sort(mylist, mycmpfnc, mydata);</code>.
- *
- * @param list the list to sort
- * @param cmpfnc the function that shall be used to compare the element data
- * @param data additional data for the cmp_func()
- * @return the sorted list
- */
-UcxList *ucx_list_sort(UcxList *list, cmp_func cmpfnc, void *data);
-
-/**
- * Removes an element from the list.
- *
- * If the first element is removed, the list pointer changes. So it is
- * <i>highly recommended</i> to <i>always</i> update the pointer by calling
- * <code>mylist = ucx_list_remove(mylist, myelem);</code>.
- *
- * @param list the list from which the element shall be removed
- * @param element the element to remove
- * @return returns the updated list pointer or <code>NULL</code>, if the list
- * is now empty
- */
-UcxList *ucx_list_remove(UcxList *list, UcxList *element);
-
-/**
- * Removes an element from the list using a UcxAllocator.
- *
- * See ucx_list_remove() for details.
- *
- * @param allocator the allocator to use
- * @param list the list from which the element shall be removed
- * @param element the element to remove
- * @return returns the updated list pointer or <code>NULL</code>, if the list
- * @see ucx_list_remove()
- */
-UcxList *ucx_list_remove_a(UcxAllocator *allocator, UcxList *list,
- UcxList *element);
-
-/**
- * Returns the union of two lists.
- *
- * The union is a list of unique elements regarding cmpfnc obtained from
- * both source lists.
- *
- * @param left the left source list
- * @param right the right source list
- * @param cmpfnc a function to compare elements
- * @param cmpdata additional data for the compare function
- * @param cpfnc a function to copy the elements
- * @param cpdata additional data for the copy function
- * @return a new list containing the union
- */
-UcxList* ucx_list_union(const UcxList *left, const UcxList *right,
- cmp_func cmpfnc, void* cmpdata,
- copy_func cpfnc, void* cpdata);
-
-/**
- * Returns the union of two lists.
- *
- * The union is a list of unique elements regarding cmpfnc obtained from
- * both source lists.
- *
- * @param allocator allocates the new list elements
- * @param left the left source list
- * @param right the right source list
- * @param cmpfnc a function to compare elements
- * @param cmpdata additional data for the compare function
- * @param cpfnc a function to copy the elements
- * @param cpdata additional data for the copy function
- * @return a new list containing the union
- */
-UcxList* ucx_list_union_a(UcxAllocator *allocator,
- const UcxList *left, const UcxList *right,
- cmp_func cmpfnc, void* cmpdata,
- copy_func cpfnc, void* cpdata);
-
-/**
- * Returns the intersection of two lists.
- *
- * The intersection contains all elements of the left list
- * (including duplicates) that can be found in the right list.
- *
- * @param left the left source list
- * @param right the right source list
- * @param cmpfnc a function to compare elements
- * @param cmpdata additional data for the compare function
- * @param cpfnc a function to copy the elements
- * @param cpdata additional data for the copy function
- * @return a new list containing the intersection
- */
-UcxList* ucx_list_intersection(const UcxList *left, const UcxList *right,
- cmp_func cmpfnc, void* cmpdata,
- copy_func cpfnc, void* cpdata);
-
-/**
- * Returns the intersection of two lists.
- *
- * The intersection contains all elements of the left list
- * (including duplicates) that can be found in the right list.
- *
- * @param allocator allocates the new list elements
- * @param left the left source list
- * @param right the right source list
- * @param cmpfnc a function to compare elements
- * @param cmpdata additional data for the compare function
- * @param cpfnc a function to copy the elements
- * @param cpdata additional data for the copy function
- * @return a new list containing the intersection
- */
-UcxList* ucx_list_intersection_a(UcxAllocator *allocator,
- const UcxList *left, const UcxList *right,
- cmp_func cmpfnc, void* cmpdata,
- copy_func cpfnc, void* cpdata);
-
-/**
- * Returns the difference of two lists.
- *
- * The difference contains all elements of the left list
- * (including duplicates) that are not equal to any element of the right list.
- *
- * @param left the left source list
- * @param right the right source list
- * @param cmpfnc a function to compare elements
- * @param cmpdata additional data for the compare function
- * @param cpfnc a function to copy the elements
- * @param cpdata additional data for the copy function
- * @return a new list containing the difference
- */
-UcxList* ucx_list_difference(const UcxList *left, const UcxList *right,
- cmp_func cmpfnc, void* cmpdata,
- copy_func cpfnc, void* cpdata);
-
-/**
- * Returns the difference of two lists.
- *
- * The difference contains all elements of the left list
- * (including duplicates) that are not equal to any element of the right list.
- *
- * @param allocator allocates the new list elements
- * @param left the left source list
- * @param right the right source list
- * @param cmpfnc a function to compare elements
- * @param cmpdata additional data for the compare function
- * @param cpfnc a function to copy the elements
- * @param cpdata additional data for the copy function
- * @return a new list containing the difference
- */
-UcxList* ucx_list_difference_a(UcxAllocator *allocator,
- const UcxList *left, const UcxList *right,
- cmp_func cmpfnc, void* cmpdata,
- copy_func cpfnc, void* cpdata);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* UCX_LIST_H */
-
+++ /dev/null
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-/**
- * Logging API.
- *
- * @file logging.h
- * @author Mike Becker, Olaf Wintermann
- */
-#ifndef UCX_LOGGING_H
-#define UCX_LOGGING_H
-
-#include "ucx.h"
-#include "map.h"
-#include "string.h"
-#include <stdio.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* leave enough space for custom log levels */
-
-/** Log level for error messages. */
-#define UCX_LOGGER_ERROR 0x00
-
-/** Log level for warning messages. */
-#define UCX_LOGGER_WARN 0x10
-
-/** Log level for information messages. */
-#define UCX_LOGGER_INFO 0x20
-
-/** Log level for debug messages. */
-#define UCX_LOGGER_DEBUG 0x30
-
-/** Log level for trace messages. */
-#define UCX_LOGGER_TRACE 0x40
-
-/**
- * Output flag for the log level.
- * If this flag is set, the log message will contain the log level.
- * @see UcxLogger.mask
- */
-#define UCX_LOGGER_LEVEL 0x01
-
-/**
- * Output flag for the timestmap.
- * If this flag is set, the log message will contain the timestmap.
- * @see UcxLogger.mask
- */
-#define UCX_LOGGER_TIMESTAMP 0x02
-
-/**
- * Output flag for the source.
- * If this flag is set, the log message will contain the source file and line
- * number.
- * @see UcxLogger.mask
- */
-#define UCX_LOGGER_SOURCE 0x04
-
-/**
- * The UCX Logger object.
- */
-typedef struct {
- /** The stream this logger writes its messages to.*/
- void *stream;
-
- /**
- * The write function that shall be used.
- * For standard file or stdout loggers this might be standard fwrite
- * (default).
- */
- write_func writer;
-
- /**
- * The date format for timestamp outputs including the delimiter
- * (default: <code>"%F %T %z "</code>).
- * @see UCX_LOGGER_TIMESTAMP
- */
- char *dateformat;
-
- /**
- * The level, this logger operates on.
- * If a log command is issued, the message will only be logged, if the log
- * level of the message is less or equal than the log level of the logger.
- */
- unsigned int level;
-
- /**
- * A configuration mask for automatic output.
- * For each flag that is set, the logger automatically outputs some extra
- * information like the timestamp or the source file and line number.
- * See the documentation for the flags for details.
- */
- unsigned int mask;
-
- /**
- * A map of valid log levels for this logger.
- *
- * The keys represent all valid log levels and the values provide string
- * representations, that are used, if the UCX_LOGGER_LEVEL flag is set.
- *
- * The exact data types are <code>unsigned int</code> for the key and
- * <code>const char*</code> for the value.
- *
- * @see UCX_LOGGER_LEVEL
- */
- UcxMap* levels;
-} UcxLogger;
-
-/**
- * Creates a new logger.
- * @param stream the stream, which the logger shall write to
- * @param level the level on which the logger shall operate
- * @param mask configuration mask (cf. UcxLogger.mask)
- * @return a new logger object
- */
-UcxLogger *ucx_logger_new(void *stream, unsigned int level, unsigned int mask);
-
-/**
- * Destroys the logger.
- *
- * The map containing the valid log levels is also automatically destroyed.
- *
- * @param logger the logger to destroy
- */
-void ucx_logger_free(UcxLogger* logger);
-
-/**
- * Internal log function - use macros instead.
- *
- * This function uses the <code>format</code> and variadic arguments for a
- * printf()-style output of the log message.
- *
- * Dependent on the UcxLogger.mask some information is prepended. The complete
- * format is:
- *
- * <code>[LEVEL] [TIMESTAMP] [SOURCEFILE]:[LINENO] message</code>
- *
- * The source file name is reduced to the actual file name. This is necessary to
- * get consistent behavior over different definitions of the __FILE__ macro.
- *
- * <b>Attention:</b> the message (including automatically generated information)
- * is limited to 4096 characters. The level description is limited to
- * 256 characters and the timestamp string is limited to 128 characters.
- *
- * @param logger the logger to use
- * @param level the level to log on
- * @param file information about the source file
- * @param line information about the source line number
- * @param format format string
- * @param ... arguments
- * @see ucx_logger_log()
- */
-void ucx_logger_logf(UcxLogger *logger, unsigned int level, const char* file,
- const unsigned int line, const char* format, ...);
-
-/**
- * Registers a custom log level.
- * @param logger the logger
- * @param level the log level as unsigned integer
- * @param name a string literal describing the level
- */
-#define ucx_logger_register_level(logger, level, name) {\
- unsigned int l; \
- l = level; \
- ucx_map_int_put(logger->levels, l, (void*) "[" name "]"); \
- } while (0);
-
-/**
- * Logs a message at the specified level.
- * @param logger the logger to use
- * @param level the level to log the message on
- * @param ... format string and arguments
- * @see ucx_logger_logf()
- */
-#define ucx_logger_log(logger, level, ...) \
- ucx_logger_logf(logger, level, __FILE__, __LINE__, __VA_ARGS__)
-
-/**
- * Shortcut for logging an error message.
- * @param logger the logger to use
- * @param ... format string and arguments
- * @see ucx_logger_logf()
- */
-#define ucx_logger_error(logger, ...) \
- ucx_logger_log(logger, UCX_LOGGER_ERROR, __VA_ARGS__)
-
-/**
- * Shortcut for logging an information message.
- * @param logger the logger to use
- * @param ... format string and arguments
- * @see ucx_logger_logf()
- */
-#define ucx_logger_info(logger, ...) \
- ucx_logger_log(logger, UCX_LOGGER_INFO, __VA_ARGS__)
-
-/**
- * Shortcut for logging a warning message.
- * @param logger the logger to use
- * @param ... format string and arguments
- * @see ucx_logger_logf()
- */
-#define ucx_logger_warn(logger, ...) \
- ucx_logger_log(logger, UCX_LOGGER_WARN, __VA_ARGS__)
-
-/**
- * Shortcut for logging a debug message.
- * @param logger the logger to use
- * @param ... format string and arguments
- * @see ucx_logger_logf()
- */
-#define ucx_logger_debug(logger, ...) \
- ucx_logger_log(logger, UCX_LOGGER_DEBUG, __VA_ARGS__)
-
-/**
- * Shortcut for logging a trace message.
- * @param logger the logger to use
- * @param ... format string and arguments
- * @see ucx_logger_logf()
- */
-#define ucx_logger_trace(logger, ...) \
- ucx_logger_log(logger, UCX_LOGGER_TRACE, __VA_ARGS__)
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* UCX_LOGGING_H */
+++ /dev/null
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-/**
- * @file map.h
- *
- * Hash map implementation.
- *
- * This implementation uses murmur hash 2 and separate chaining with linked
- * lists.
- *
- * @author Mike Becker
- * @author Olaf Wintermann
- */
-
-#ifndef UCX_MAP_H
-#define UCX_MAP_H
-
-#include "ucx.h"
-#include "string.h"
-#include "allocator.h"
-#include <stdio.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Loop statement for UCX maps.
- *
- * The <code>key</code> variable is implicitly defined, but the
- * <code>value</code> variable must be already declared as type information
- * cannot be inferred.
- *
- * @param key the variable name for the key
- * @param value the variable name for the value
- * @param iter a UcxMapIterator
- * @see ucx_map_iterator()
- */
-#define UCX_MAP_FOREACH(key,value,iter) \
- for(UcxKey key;ucx_map_iter_next(&iter,&key, (void**)&value);)
-
-/** Type for the UCX map. @see UcxMap */
-typedef struct UcxMap UcxMap;
-
-/** Type for a key of a UcxMap. @see UcxKey */
-typedef struct UcxKey UcxKey;
-
-/** Type for an element of a UcxMap. @see UcxMapElement */
-typedef struct UcxMapElement UcxMapElement;
-
-/** Type for an iterator over a UcxMap. @see UcxMapIterator */
-typedef struct UcxMapIterator UcxMapIterator;
-
-/** Structure for the UCX map. */
-struct UcxMap {
- /** An allocator that is used for the map elements. */
- UcxAllocator *allocator;
- /** The array of map element lists. */
- UcxMapElement **map;
- /** The size of the map is the length of the element list array. */
- size_t size;
- /** The count of elements currently stored in this map. */
- size_t count;
-};
-
-/** Structure to publicly denote a key of a UcxMap. */
-struct UcxKey {
- /** The key data. */
- const void *data;
- /** The length of the key data. */
- size_t len;
- /** A cache for the hash value of the key data. */
- int hash;
-};
-
-/** Internal structure for a key of a UcxMap. */
-struct UcxMapKey {
- /** The key data. */
- void *data;
- /** The length of the key data. */
- size_t len;
- /** The hash value of the key data. */
- int hash;
-};
-
-/** Structure for an element of a UcxMap. */
-struct UcxMapElement {
- /** The value data. */
- void *data;
-
- /** A pointer to the next element in the current list. */
- UcxMapElement *next;
-
- /** The corresponding key. */
- struct UcxMapKey key;
-};
-
-/** Structure for an iterator over a UcxMap. */
-struct UcxMapIterator {
- /** The map to iterate over. */
- UcxMap const *map;
-
- /** The current map element. */
- UcxMapElement *cur;
-
- /**
- * The current index of the element list array.
- * <b>Attention: </b> this is <b>NOT</b> the element index! Do <b>NOT</b>
- * manually iterate over the map by increasing this index. Use
- * ucx_map_iter_next().
- * @see UcxMap.map*/
- size_t index;
-};
-
-/**
- * Creates a new hash map with the specified size.
- * @param size the size of the hash map
- * @return a pointer to the new hash map
- */
-UcxMap *ucx_map_new(size_t size);
-
-/**
- * Creates a new hash map with the specified size using a UcxAllocator.
- * @param allocator the allocator to use
- * @param size the size of the hash map
- * @return a pointer to the new hash map
- */
-UcxMap *ucx_map_new_a(UcxAllocator *allocator, size_t size);
-
-/**
- * Frees a hash map.
- *
- * <b>Note:</b> the contents are <b>not</b> freed, use ucx_map_free_content()
- * before calling this function to achieve that.
- *
- * @param map the map to be freed
- * @see ucx_map_free_content()
- */
-void ucx_map_free(UcxMap *map);
-
-/**
- * Frees the contents of a hash map.
- *
- * This is a convenience function that iterates over the map and passes all
- * values to the specified destructor function.
- *
- * If no destructor is specified (<code>NULL</code>), the free() function of
- * the map's own allocator is used.
- *
- * You must ensure, that it is valid to pass each value in the map to the same
- * destructor function.
- *
- * You should free or clear the map afterwards, as the contents will be invalid.
- *
- * @param map for which the contents shall be freed
- * @param destr optional pointer to a destructor function
- * @see ucx_map_free()
- * @see ucx_map_clear()
- */
-void ucx_map_free_content(UcxMap *map, ucx_destructor destr);
-
-/**
- * Clears a hash map.
- *
- * <b>Note:</b> the contents are <b>not</b> freed, use ucx_map_free_content()
- * before calling this function to achieve that.
- *
- * @param map the map to be cleared
- * @see ucx_map_free_content()
- */
-void ucx_map_clear(UcxMap *map);
-
-
-/**
- * Copies contents from a map to another map using a copy function.
- *
- * <b>Note:</b> The destination map does not need to be empty. However, if it
- * contains data with keys that are also present in the source map, the contents
- * are overwritten.
- *
- * @param from the source map
- * @param to the destination map
- * @param fnc the copy function or <code>NULL</code> if the pointer address
- * shall be copied
- * @param data additional data for the copy function
- * @return 0 on success or a non-zero value on memory allocation errors
- */
-int ucx_map_copy(UcxMap const *from, UcxMap *to, copy_func fnc, void *data);
-
-/**
- * Clones the map and rehashes if necessary.
- *
- * <b>Note:</b> In contrast to ucx_map_rehash() the load factor is irrelevant.
- * This function <i>always</i> ensures a new UcxMap.size of at least
- * 2.5*UcxMap.count.
- *
- * @param map the map to clone
- * @param fnc the copy function to use or <code>NULL</code> if the new and
- * the old map shall share the data pointers
- * @param data additional data for the copy function
- * @return the cloned map
- * @see ucx_map_copy()
- */
-UcxMap *ucx_map_clone(UcxMap const *map, copy_func fnc, void *data);
-
-/**
- * Clones the map and rehashes if necessary.
- *
- * <b>Note:</b> In contrast to ucx_map_rehash() the load factor is irrelevant.
- * This function <i>always</i> ensures a new UcxMap.size of at least
- * 2.5*UcxMap.count.
- *
- * @param allocator the allocator to use for the cloned map
- * @param map the map to clone
- * @param fnc the copy function to use or <code>NULL</code> if the new and
- * the old map shall share the data pointers
- * @param data additional data for the copy function
- * @return the cloned map
- * @see ucx_map_copy()
- */
-UcxMap *ucx_map_clone_a(UcxAllocator *allocator,
- UcxMap const *map, copy_func fnc, void *data);
-
-/**
- * Increases size of the hash map, if necessary.
- *
- * The load value is 0.75*UcxMap.size. If the element count exceeds the load
- * value, the map needs to be rehashed. Otherwise no action is performed and
- * this function simply returns 0.
- *
- * The rehashing process ensures, that the UcxMap.size is at least
- * 2.5*UcxMap.count. So there is enough room for additional elements without
- * the need of another soon rehashing.
- *
- * You can use this function to dramatically increase access performance.
- *
- * @param map the map to rehash
- * @return 1, if a memory allocation error occurred, 0 otherwise
- */
-int ucx_map_rehash(UcxMap *map);
-
-/**
- * Puts a key/value-pair into the map.
- *
- * @param map the map
- * @param key the key
- * @param value the value
- * @return 0 on success, non-zero value on failure
- */
-int ucx_map_put(UcxMap *map, UcxKey key, void *value);
-
-/**
- * Retrieves a value by using a key.
- *
- * @param map the map
- * @param key the key
- * @return the value
- */
-void* ucx_map_get(UcxMap const *map, UcxKey key);
-
-/**
- * Removes a key/value-pair from the map by using the key.
- *
- * @param map the map
- * @param key the key
- * @return the removed value
- */
-void* ucx_map_remove(UcxMap *map, UcxKey key);
-
-/**
- * Shorthand for putting data with a sstr_t key into the map.
- * @param map the map
- * @param key the key
- * @param value the value
- * @return 0 on success, non-zero value on failure
- * @see ucx_map_put()
- */
-#define ucx_map_sstr_put(map, key, value) \
- ucx_map_put(map, ucx_key(key.ptr, key.length), (void*)value)
-
-/**
- * Shorthand for putting data with a C string key into the map.
- * @param map the map
- * @param key the key
- * @param value the value
- * @return 0 on success, non-zero value on failure
- * @see ucx_map_put()
- */
-#define ucx_map_cstr_put(map, key, value) \
- ucx_map_put(map, ucx_key(key, strlen(key)), (void*)value)
-
-/**
- * Shorthand for putting data with an integer key into the map.
- * @param map the map
- * @param key the key
- * @param value the value
- * @return 0 on success, non-zero value on failure
- * @see ucx_map_put()
- */
-#define ucx_map_int_put(map, key, value) \
- ucx_map_put(map, ucx_key(&key, sizeof(key)), (void*)value)
-
-/**
- * Shorthand for getting data from the map with a sstr_t key.
- * @param map the map
- * @param key the key
- * @return the value
- * @see ucx_map_get()
- */
-#define ucx_map_sstr_get(map, key) \
- ucx_map_get(map, ucx_key(key.ptr, key.length))
-
-/**
- * Shorthand for getting data from the map with a C string key.
- * @param map the map
- * @param key the key
- * @return the value
- * @see ucx_map_get()
- */
-#define ucx_map_cstr_get(map, key) \
- ucx_map_get(map, ucx_key(key, strlen(key)))
-
-/**
- * Shorthand for getting data from the map with an integer key.
- * @param map the map
- * @param key the key
- * @return the value
- * @see ucx_map_get()
- */
-#define ucx_map_int_get(map, key) \
- ucx_map_get(map, ucx_key(&key, sizeof(int)))
-
-/**
- * Shorthand for removing data from the map with a sstr_t key.
- * @param map the map
- * @param key the key
- * @return the removed value
- * @see ucx_map_remove()
- */
-#define ucx_map_sstr_remove(map, key) \
- ucx_map_remove(map, ucx_key(key.ptr, key.length))
-
-/**
- * Shorthand for removing data from the map with a C string key.
- * @param map the map
- * @param key the key
- * @return the removed value
- * @see ucx_map_remove()
- */
-#define ucx_map_cstr_remove(map, key) \
- ucx_map_remove(map, ucx_key(key, strlen(key)))
-
-/**
- * Shorthand for removing data from the map with an integer key.
- * @param map the map
- * @param key the key
- * @return the removed value
- * @see ucx_map_remove()
- */
-#define ucx_map_int_remove(map, key) \
- ucx_map_remove(map, ucx_key(&key, sizeof(key)))
-
-/**
- * Creates a UcxKey based on the given data.
- *
- * This function implicitly computes the hash.
- *
- * @param data the data for the key
- * @param len the length of the data
- * @return a UcxKey with implicitly computed hash
- * @see ucx_hash()
- */
-UcxKey ucx_key(const void *data, size_t len);
-
-/**
- * Computes a murmur hash-2.
- *
- * @param data the data to hash
- * @param len the length of the data
- * @return the murmur hash-2 of the data
- */
-int ucx_hash(const char *data, size_t len);
-
-/**
- * Creates an iterator for a map.
- *
- * <b>Note:</b> A UcxMapIterator iterates over all elements in all element
- * lists successively. Therefore the order highly depends on the key hashes and
- * may vary under different map sizes. So generally you may <b>NOT</b> rely on
- * the iteration order.
- *
- * <b>Note:</b> The iterator is <b>NOT</b> initialized. You need to call
- * ucx_map_iter_next() at least once before accessing any information. However,
- * it is not recommended to access the fields of a UcxMapIterator directly.
- *
- * @param map the map to create the iterator for
- * @return an iterator initialized on the first element of the
- * first element list
- * @see ucx_map_iter_next()
- */
-UcxMapIterator ucx_map_iterator(UcxMap const *map);
-
-/**
- * Proceeds to the next element of the map (if any).
- *
- * Subsequent calls on the same iterator proceed to the next element and
- * store the key/value-pair into the memory specified as arguments of this
- * function.
- *
- * If no further elements are found, this function returns zero and leaves the
- * last found key/value-pair in memory.
- *
- * @param iterator the iterator to use
- * @param key a pointer to the memory where to store the key
- * @param value a pointer to the memory where to store the value
- * @return 1, if another element was found, 0 if all elements has been processed
- * @see ucx_map_iterator()
- */
-int ucx_map_iter_next(UcxMapIterator *iterator, UcxKey *key, void **value);
-
-/**
- * Returns the union of two maps.
- *
- * The union is a fresh map which is filled by two successive calls of
- * ucx_map_copy() on the two input maps.
- *
- * @param first the first source map
- * @param second the second source map
- * @param cpfnc a function to copy the elements
- * @param cpdata additional data for the copy function
- * @return a new map containing the union
- */
-UcxMap* ucx_map_union(const UcxMap *first, const UcxMap *second,
- copy_func cpfnc, void* cpdata);
-
-/**
- * Returns the union of two maps.
- *
- * The union is a fresh map which is filled by two successive calls of
- * ucx_map_copy() on the two input maps.
- *
- * @param allocator the allocator that shall be used by the new map
- * @param first the first source map
- * @param second the second source map
- * @param cpfnc a function to copy the elements
- * @param cpdata additional data for the copy function
- * @return a new map containing the union
- */
-UcxMap* ucx_map_union_a(UcxAllocator *allocator,
- const UcxMap *first, const UcxMap *second,
- copy_func cpfnc, void* cpdata);
-
-/**
- * Returns the intersection of two maps.
- *
- * The intersection is defined as a copy of the first map with every element
- * removed that has no valid key in the second map.
- *
- * @param first the first source map
- * @param second the second source map
- * @param cpfnc a function to copy the elements
- * @param cpdata additional data for the copy function
- * @return a new map containing the intersection
- */
-UcxMap* ucx_map_intersection(const UcxMap *first, const UcxMap *second,
- copy_func cpfnc, void* cpdata);
-
-/**
- * Returns the intersection of two maps.
- *
- * The intersection is defined as a copy of the first map with every element
- * removed that has no valid key in the second map.
- *
- * @param allocator the allocator that shall be used by the new map
- * @param first the first source map
- * @param second the second source map
- * @param cpfnc a function to copy the elements
- * @param cpdata additional data for the copy function
- * @return a new map containing the intersection
- */
-UcxMap* ucx_map_intersection_a(UcxAllocator *allocator,
- const UcxMap *first, const UcxMap *second,
- copy_func cpfnc, void* cpdata);
-
-/**
- * Returns the difference of two maps.
- *
- * The difference contains a copy of all elements of the first map
- * for which the corresponding keys cannot be found in the second map.
- *
- * @param first the first source map
- * @param second the second source map
- * @param cpfnc a function to copy the elements
- * @param cpdata additional data for the copy function
- * @return a new list containing the difference
- */
-UcxMap* ucx_map_difference(const UcxMap *first, const UcxMap *second,
- copy_func cpfnc, void* cpdata);
-
-/**
- * Returns the difference of two maps.
- *
- * The difference contains a copy of all elements of the first map
- * for which the corresponding keys cannot be found in the second map.
- *
- * @param allocator the allocator that shall be used by the new map
- * @param first the first source map
- * @param second the second source map
- * @param cpfnc a function to copy the elements
- * @param cpdata additional data for the copy function
- * @return a new list containing the difference
- */
-UcxMap* ucx_map_difference_a(UcxAllocator *allocator,
- const UcxMap *first, const UcxMap *second,
- copy_func cpfnc, void* cpdata);
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* UCX_MAP_H */
-
+++ /dev/null
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-/**
- * @file mempool.h
- *
- * Memory pool implementation.
- *
- * @author Mike Becker
- * @author Olaf Wintermann
- */
-
-#ifndef UCX_MEMPOOL_H
-#define UCX_MEMPOOL_H
-
-#include "ucx.h"
-#include "allocator.h"
-#include <stddef.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * UCX mempool structure.
- */
-typedef struct {
- /** UcxAllocator based on this pool */
- UcxAllocator *allocator;
-
- /** List of pointers to pooled memory. */
- void **data;
-
- /** Count of pooled memory items. */
- size_t ndata;
-
- /** Memory pool size. */
- size_t size;
-} UcxMempool;
-
-/** Shorthand for a new default memory pool with a capacity of 16 elements. */
-#define ucx_mempool_new_default() ucx_mempool_new(16)
-
-
-/**
- * Creates a memory pool with the specified initial size.
- *
- * As the created memory pool automatically grows in size by factor two when
- * trying to allocate memory on a full pool, it is recommended that you use
- * a power of two for the initial size.
- *
- * @param n initial pool size (should be a power of two, e.g. 16)
- * @return a pointer to the new memory pool
- * @see ucx_mempool_new_default()
- */
-UcxMempool *ucx_mempool_new(size_t n);
-
-/**
- * Resizes a memory pool.
- *
- * This function will fail if the new capacity is not sufficient for the
- * present data.
- *
- * @param pool the pool to resize
- * @param newcap the new capacity
- * @return zero on success or non-zero on failure
- */
-int ucx_mempool_chcap(UcxMempool *pool, size_t newcap);
-
-/**
- * Allocates pooled memory.
- *
- * @param pool the memory pool
- * @param n amount of memory to allocate
- * @return a pointer to the allocated memory
- * @see ucx_allocator_malloc()
- */
-void *ucx_mempool_malloc(UcxMempool *pool, size_t n);
-/**
- * Allocates a pooled memory array.
- *
- * The content of the allocated memory is set to zero.
- *
- * @param pool the memory pool
- * @param nelem amount of elements to allocate
- * @param elsize amount of memory per element
- * @return a pointer to the allocated memory
- * @see ucx_allocator_calloc()
- */
-void *ucx_mempool_calloc(UcxMempool *pool, size_t nelem, size_t elsize);
-
-/**
- * Reallocates pooled memory.
- *
- * If the memory to be reallocated is not contained by the specified pool, the
- * behavior is undefined.
- *
- * @param pool the memory pool
- * @param ptr a pointer to the memory that shall be reallocated
- * @param n the new size of the memory
- * @return a pointer to the new location of the memory
- * @see ucx_allocator_realloc()
- */
-void *ucx_mempool_realloc(UcxMempool *pool, void *ptr, size_t n);
-
-/**
- * Frees pooled memory.
- *
- * Before freeing the memory, the specified destructor function (if any)
- * is called.
- *
- * If you specify memory, that is not pooled by the specified memory pool, the
- * program will terminate with a call to <code>abort()</code>.
- *
- * @param pool the memory pool
- * @param ptr a pointer to the memory that shall be freed
- * @see ucx_mempool_set_destr()
- */
-void ucx_mempool_free(UcxMempool *pool, void *ptr);
-
-/**
- * Destroys a memory pool.
- *
- * For each element the destructor function (if any) is called and the element
- * is freed.
- *
- * Each of the registered destructor function that has no corresponding element
- * within the pool (namely those registered by ucx_mempool_reg_destr) is
- * called interleaving with the element destruction, but with guarantee to the
- * order in which they were registered (FIFO order).
- *
- *
- * @param pool the mempool to destroy
- */
-void ucx_mempool_destroy(UcxMempool *pool);
-
-/**
- * Sets a destructor function for the specified memory.
- *
- * The destructor is automatically called when the memory is freed or the
- * pool is destroyed.
- * A destructor for pooled memory <b>MUST NOT</b> free the memory itself,
- * as this is done by the pool. Use a destructor to free any resources
- * managed by the pooled object.
- *
- * The only requirement for the specified memory is, that it <b>MUST</b> be
- * pooled memory by a UcxMempool or an element-compatible mempool. The pointer
- * to the destructor function is saved in a reserved area before the actual
- * memory.
- *
- * @param ptr pooled memory
- * @param func a pointer to the destructor function
- * @see ucx_mempool_free()
- * @see ucx_mempool_destroy()
- */
-void ucx_mempool_set_destr(void *ptr, ucx_destructor func);
-
-/**
- * Registers a destructor function for the specified (non-pooled) memory.
- *
- * This is useful, if you have memory that has not been allocated by a mempool,
- * but shall be managed by a mempool.
- *
- * This function creates an entry in the specified mempool and the memory will
- * therefore (logically) convert to pooled memory.
- * <b>However, this does not cause the memory to be freed automatically!</b>.
- * If you want to use this function, make the memory pool free non-pooled
- * memory, the specified destructor function must call <code>free()</code>
- * by itself. But keep in mind, that you then MUST NOT use this destructor
- * function with pooled memory (e.g. in ucx_mempool_set_destr()), as it
- * would cause a double-free.
- *
- * @param pool the memory pool
- * @param ptr data the destructor is registered for
- * @param destr a pointer to the destructor function
- */
-void ucx_mempool_reg_destr(UcxMempool *pool, void *ptr, ucx_destructor destr);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* UCX_MEMPOOL_H */
-
+++ /dev/null
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-/**
- * @file stack.h
- *
- * Default stack memory allocation implementation.
- *
- * @author Mike Becker
- * @author Olaf Wintermann
- */
-
-#ifndef UCX_STACK_H
-#define UCX_STACK_H
-
-#include "ucx.h"
-#include "allocator.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-/**
- * UCX stack structure.
- */
-typedef struct {
- /** UcxAllocator based on this stack */
- UcxAllocator allocator;
-
- /** Stack size. */
- size_t size;
-
- /** Pointer to the bottom of the stack */
- char *space;
-
- /** Pointer to the top of the stack */
- char *top;
-} UcxStack;
-
-/**
- * Metadata for each UCX stack element.
- */
-struct ucx_stack_metadata {
- /**
- * Location of the previous element (<code>NULL</code> if this is the first)
- */
- char *prev;
-
- /** Size of this element */
- size_t size;
-};
-
-/**
- * Initializes UcxStack structure with memory.
- *
- * @param stack a pointer to an uninitialized stack structure
- * @param space the memory area that shall be managed
- * @param size size of the memory area
- * @return a new UcxStack structure
- */
-void ucx_stack_init(UcxStack *stack, char* space, size_t size);
-
-/**
- * Allocates stack memory.
- *
- * @param stack a pointer to the stack
- * @param n amount of memory to allocate
- * @return a pointer to the allocated memory or <code>NULL</code> on stack
- * overflow
- * @see ucx_allocator_malloc()
- */
-void *ucx_stack_malloc(UcxStack *stack, size_t n);
-
-/**
- * Allocates memory with #ucx_stack_malloc() and copies the specified data if
- * the allocation was successful.
- *
- * @param stack a pointer to the stack
- * @param n amount of memory to allocate
- * @param data a pointer to the data to copy
- * @return a pointer to the allocated memory
- * @see ucx_stack_malloc
- */
-void *ucx_stack_push(UcxStack *stack, size_t n, const void *data);
-
-/**
- * Allocates an array of stack memory
- *
- * The content of the allocated memory is set to zero.
- *
- * @param stack a pointer to the stack
- * @param nelem amount of elements to allocate
- * @param elsize amount of memory per element
- * @return a pointer to the allocated memory
- * @see ucx_allocator_calloc()
- */
-void *ucx_stack_calloc(UcxStack *stack, size_t nelem, size_t elsize);
-
-/**
- * Allocates memory with #ucx_stack_calloc() and copies the specified data if
- * the allocation was successful.
- *
- * @param stack a pointer to the stack
- * @param nelem amount of elements to allocate
- * @param elsize amount of memory per element
- * @param data a pointer to the data
- * @return a pointer to the allocated memory
- * @see ucx_stack_calloc
- */
-void *ucx_stack_pusharr(UcxStack *stack,
- size_t nelem, size_t elsize, const void *data);
-
-/**
- * Reallocates memory on the stack.
- *
- * Shrinking memory is always safe. Extending memory can be very expensive.
- *
- * @param stack the stack
- * @param ptr a pointer to the memory that shall be reallocated
- * @param n the new size of the memory
- * @return a pointer to the new location of the memory
- * @see ucx_allocator_realloc()
- */
-void *ucx_stack_realloc(UcxStack *stack, void *ptr, size_t n);
-
-/**
- * Frees memory on the stack.
- *
- * Freeing stack memory behaves in a special way.
- *
- * If the element, that should be freed, is the top most element of the stack,
- * it is removed from the stack. Otherwise it is marked as freed. Marked
- * elements are removed, when they become the top most elements of the stack.
- *
- * @param stack a pointer to the stack
- * @param ptr a pointer to the memory that shall be freed
- */
-void ucx_stack_free(UcxStack *stack, void *ptr);
-
-
-/**
- * Returns the size of the top most element.
- * @param stack a pointer to the stack
- * @return the size of the top most element
- */
-#define ucx_stack_topsize(stack) ((stack)->top ? ((struct ucx_stack_metadata*)\
- (stack)->top - 1)->size : 0)
-
-/**
- * Removes the top most element from the stack and copies the content to <code>
- * dest</code>, if specified.
- *
- * Use #ucx_stack_topsize()# to get the amount of memory that must be available
- * at the location of <code>dest</code>.
- *
- * @param stack a pointer to the stack
- * @param dest the location where the contents shall be written to, or <code>
- * NULL</code>, if the element shall only be removed.
- * @see ucx_stack_free
- * @see ucx_stack_popn
- */
-#define ucx_stack_pop(stack, dest) ucx_stack_popn(stack, dest, (size_t)-1)
-
-/**
- * Removes the top most element from the stack and copies the content to <code>
- * dest</code>.
- *
- * This function copies at most <code>n</code> bytes to the destination, but
- * the element is always freed as a whole.
- * If the element was larger than <code>n</code>, the remaining data is lost.
- *
- * @param stack a pointer to the stack
- * @param dest the location where the contents shall be written to
- * @param n copies at most n bytes to <code>dest</code>
- * @see ucx_stack_pop
- */
-void ucx_stack_popn(UcxStack *stack, void *dest, size_t n);
-
-/**
- * Returns the remaining available memory on the specified stack.
- *
- * @param stack a pointer to the stack
- * @return the remaining available memory
- */
-size_t ucx_stack_avail(UcxStack *stack);
-
-/**
- * Checks, if the stack is empty.
- *
- * @param stack a pointer to the stack
- * @return nonzero, if the stack is empty, zero otherwise
- */
-#define ucx_stack_empty(stack) (!(stack)->top)
-
-/**
- * Computes a recommended size for the stack memory area. Note, that
- * reallocations have not been taken into account, so you might need to reserve
- * twice as much memory to allow many reallocations.
- *
- * @param size the approximate payload
- * @param elems the approximate count of element allocations
- * @return a recommended size for the stack space based on the information
- * provided
- */
-#define ucx_stack_dim(size, elems) (size+sizeof(struct ucx_stack_metadata) * \
- (elems + 1))
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* UCX_STACK_H */
-
+++ /dev/null
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-/**
- * Bounded string implementation.
- *
- * The UCX strings (<code>sstr_t</code>) provide an alternative to C strings.
- * The main difference to C strings is, that <code>sstr_t</code> does <b>not
- * need to be <code>NULL</code>-terminated</b>. Instead the length is stored
- * within the structure.
- *
- * When using <code>sstr_t</code>, developers must be full aware of what type
- * of string (<code>NULL</code>-terminated) or not) they are using, when
- * accessing the <code>char* ptr</code> directly.
- *
- * The UCX string module provides some common string functions, known from
- * standard libc, working with <code>sstr_t</code>.
- *
- * @file string.h
- * @author Mike Becker
- * @author Olaf Wintermann
- */
-
-#ifndef UCX_STRING_H
-#define UCX_STRING_H
-
-#include "ucx.h"
-#include "allocator.h"
-#include <stddef.h>
-
-/*
- * Use this macro to disable the shortcuts if you experience macro collision.
- */
-#ifndef UCX_NO_SSTR_SHORTCUTS
-/**
- * Shortcut for a <code>sstr_t struct</code>
- * or <code>scstr_t struct</code> literal.
- */
-#define ST(s) { s, sizeof(s)-1 }
-
-/** Shortcut for the conversion of a C string to a <code>sstr_t</code>. */
-#define S(s) sstrn(s, sizeof(s)-1)
-
-/** Shortcut for the conversion of a C string to a <code>scstr_t</code>. */
-#define SC(s) scstrn(s, sizeof(s)-1)
-#endif /* UCX_NO_SSTR_SHORTCUTS */
-
-/*
- * Use this macro to disable the format macros.
- */
-#ifndef UCX_NO_SSTR_FORMAT_MACROS
-/** Expands a sstr_t or scstr_t to printf arguments. */
-#define SFMT(s) (int) (s).length, (s).ptr
-
-/** Format specifier for a sstr_t or scstr_t. */
-#define PRIsstr ".*s"
-#endif /* UCX_NO_SSTR_FORMAT_MACROS */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * The UCX string structure.
- */
-typedef struct {
- /** A pointer to the string
- * (<b>not necessarily <code>NULL</code>-terminated</b>) */
- char *ptr;
- /** The length of the string */
- size_t length;
-} sstr_t;
-
-/**
- * The UCX string structure for immutable (constant) strings.
- */
-typedef struct {
- /** A constant pointer to the immutable string
- * (<b>not necessarily <code>NULL</code>-terminated</b>) */
- const char *ptr;
- /** The length of the string */
- size_t length;
-} scstr_t;
-
-#ifdef __cplusplus
-}
-#endif
-
-
-#ifdef __cplusplus
-/**
- * One of two type adjustment functions that return an scstr_t.
- *
- * Used <b>internally</b> to convert a UCX string to an immutable UCX string.
- *
- * <b>Do not use this function manually.</b>
- *
- * @param str some sstr_t
- * @return an immutable (scstr_t) version of the provided string.
- */
-inline scstr_t s2scstr(sstr_t s) {
- scstr_t c;
- c.ptr = s.ptr;
- c.length = s.length;
- return c;
-}
-
-/**
- * One of two type adjustment functions that return an scstr_t.
- *
- * Used <b>internally</b> to convert a UCX string to an immutable UCX string.
- * This variant is used, when the string is already immutable and no operation
- * needs to be performed.
- *
- * <b>Do not use this function manually.</b>
- *
- * @param str some scstr_t
- * @return the argument itself
- */
-inline scstr_t s2scstr(scstr_t str) {
- return str;
-}
-
-/**
- * Converts a UCX string to an immutable UCX string (scstr_t).
- * @param str some UCX string
- * @return an immutable version of the provided string
- */
-#define SCSTR(s) s2scstr(s)
-#else
-
-/**
- * One of two type adjustment functions that return an scstr_t.
- *
- * Used <b>internally</b> to convert a UCX string to an immutable UCX string.
- * This variant is used, when the string is already immutable and no operation
- * needs to be performed.
- *
- * <b>Do not use this function manually.</b>
- *
- * @param str some scstr_t
- * @return the argument itself
- */
-scstr_t ucx_sc2sc(scstr_t str);
-
-/**
- * One of two type adjustment functions that return an scstr_t.
- *
- * Used <b>internally</b> to convert a UCX string to an immutable UCX string.
- *
- * <b>Do not use this function manually.</b>
- *
- * @param str some sstr_t
- * @return an immutable (scstr_t) version of the provided string.
- */
-scstr_t ucx_ss2sc(sstr_t str);
-
-#if __STDC_VERSION__ >= 201112L
-/**
- * Converts a UCX string to an immutable UCX string (scstr_t).
- * @param str some UCX string
- * @return an immutable version of the provided string
- */
-#define SCSTR(str) _Generic(str, sstr_t: ucx_ss2sc, scstr_t: ucx_sc2sc)(str)
-
-#elif defined(__GNUC__) || defined(__clang__)
-
-/**
- * Converts a UCX string to an immutable UCX string (scstr_t).
- * @param str some UCX string
- * @return an immutable version of the provided string
- */
-#define SCSTR(str) __builtin_choose_expr( \
- __builtin_types_compatible_p(typeof(str), sstr_t), \
- ucx_ss2sc, \
- ucx_sc2sc)(str)
-
-#elif defined(__sun)
-
-/**
- * Converts a UCX string to an immutable UCX string (scstr_t).
- * @param str some UCX string
- * @return the an immutable version of the provided string
- */
-#define SCSTR(str) ({typeof(str) ucx_tmp_var_str = str; \
- scstr_t ucx_tmp_var_c; \
- ucx_tmp_var_c.ptr = ucx_tmp_var_str.ptr;\
- ucx_tmp_var_c.length = ucx_tmp_var_str.length;\
- ucx_tmp_var_c; })
-#else /* no generics and no builtins */
-
-/**
- * Converts a UCX string to an immutable UCX string (scstr_t).
- *
- * This <b>internal</b> function (ab)uses the C standard an expects one single
- * argument which is then implicitly converted to scstr_t without a warning.
- *
- * <b>Do not use this function manually.</b>
- *
- * @return the an immutable version of the provided string
- */
-scstr_t ucx_ss2c_s();
-
-/**
- * Converts a UCX string to an immutable UCX string (scstr_t).
- * @param str some UCX string
- * @return the an immutable version of the provided string
- */
-#define SCSTR(str) ucx_ss2c_s(str)
-#endif /* C11 feature test */
-
-#endif /* C++ */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-/**
- * Creates a new sstr_t based on a C string.
- *
- * The length is implicitly inferred by using a call to <code>strlen()</code>.
- *
- * <b>Note:</b> the sstr_t will share the specified pointer to the C string.
- * If you do want a copy, use sstrdup() on the return value of this function.
- *
- * If you need to wrap a constant string, use scstr().
- *
- * @param cstring the C string to wrap
- * @return a new sstr_t containing the C string
- *
- * @see sstrn()
- */
-sstr_t sstr(char *cstring);
-
-/**
- * Creates a new sstr_t of the specified length based on a C string.
- *
- * <b>Note:</b> the sstr_t will share the specified pointer to the C string.
- * If you do want a copy, use sstrdup() on the return value of this function.
- *
- * If you need to wrap a constant string, use scstrn().
- *
- * @param cstring the C string to wrap
- * @param length the length of the string
- * @return a new sstr_t containing the C string
- *
- * @see sstr()
- * @see S()
- */
-sstr_t sstrn(char *cstring, size_t length);
-
-/**
- * Creates a new scstr_t based on a constant C string.
- *
- * The length is implicitly inferred by using a call to <code>strlen()</code>.
- *
- * <b>Note:</b> the scstr_t will share the specified pointer to the C string.
- * If you do want a copy, use scstrdup() on the return value of this function.
- *
- * @param cstring the C string to wrap
- * @return a new scstr_t containing the C string
- *
- * @see scstrn()
- */
-scstr_t scstr(const char *cstring);
-
-
-/**
- * Creates a new scstr_t of the specified length based on a constant C string.
- *
- * <b>Note:</b> the scstr_t will share the specified pointer to the C string.
- * If you do want a copy, use scstrdup() on the return value of this function. *
- *
- * @param cstring the C string to wrap
- * @param length the length of the string
- * @return a new scstr_t containing the C string
- *
- * @see scstr()
- */
-scstr_t scstrn(const char *cstring, size_t length);
-
-/**
- * Returns the accumulated length of all specified strings.
- *
- * <b>Attention:</b> if the count argument is larger than the count of the
- * specified strings, the behavior is undefined.
- *
- * @param count the total number of specified strings
- * @param ... all strings
- * @return the accumulated length of all strings
- */
-size_t scstrnlen(size_t count, ...);
-
-/**
- * Returns the accumulated length of all specified strings.
- *
- * <b>Attention:</b> if the count argument is larger than the count of the
- * specified strings, the behavior is undefined.
- *
- * @param count the total number of specified strings
- * @param ... all strings
- * @return the cumulated length of all strings
- */
-#define sstrnlen(count, ...) scstrnlen(count, __VA_ARGS__)
-
-/**
- * Concatenates two or more strings.
- *
- * The resulting string will be allocated by standard <code>malloc()</code>.
- * So developers <b>MUST</b> pass the sstr_t.ptr to <code>free()</code>.
- *
- * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>-
- * terminated.
- *
- * @param count the total number of strings to concatenate
- * @param s1 first string
- * @param ... all remaining strings
- * @return the concatenated string
- */
-sstr_t scstrcat(size_t count, scstr_t s1, ...);
-
-/**
- * Concatenates two or more strings.
- *
- * The resulting string will be allocated by standard <code>malloc()</code>.
- * So developers <b>MUST</b> pass the sstr_t.ptr to <code>free()</code>.
- *
- * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>-
- * terminated.
- *
- * @param count the total number of strings to concatenate
- * @param s1 first string
- * @param ... all remaining strings
- * @return the concatenated string
- */
-#define sstrcat(count, s1, ...) scstrcat(count, SCSTR(s1), __VA_ARGS__)
-
-/**
- * Concatenates two or more strings using a UcxAllocator.
- *
- * The resulting string must be freed by the allocators <code>free()</code>
- * implementation.
- *
- * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>-
- * terminated.
- *
- * @param alloc the allocator to use
- * @param count the total number of strings to concatenate
- * @param s1 first string
- * @param ... all remaining strings
- * @return the concatenated string
- *
- * @see scstrcat()
- */
-sstr_t scstrcat_a(UcxAllocator *alloc, size_t count, scstr_t s1, ...);
-
-/**
- * Concatenates two or more strings using a UcxAllocator.
- *
- * The resulting string must be freed by the allocators <code>free()</code>
- * implementation.
- *
- * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>-
- * terminated.
- *
- * @param alloc the allocator to use
- * @param count the total number of strings to concatenate
- * @param s1 first string
- * @param ... all remaining strings
- * @return the concatenated string
- *
- * @see sstrcat()
- */
-#define sstrcat_a(alloc, count, s1, ...) \
- scstrcat_a(alloc, count, SCSTR(s1), __VA_ARGS__)
-
-/**
- * Returns a substring starting at the specified location.
- *
- * <b>Attention:</b> the new string references the same memory area as the
- * input string and is <b>NOT</b> required to be <code>NULL</code>-terminated.
- * Use sstrdup() to get a copy.
- *
- * @param string input string
- * @param start start location of the substring
- * @return a substring of <code>string</code> starting at <code>start</code>
- *
- * @see sstrsubsl()
- * @see sstrchr()
- */
-sstr_t sstrsubs(sstr_t string, size_t start);
-
-/**
- * Returns a substring with the given length starting at the specified location.
- *
- * <b>Attention:</b> the new string references the same memory area as the
- * input string and is <b>NOT</b> required to be <code>NULL</code>-terminated.
- * Use sstrdup() to get a copy.
- *
- * @param string input string
- * @param start start location of the substring
- * @param length the maximum length of the substring
- * @return a substring of <code>string</code> starting at <code>start</code>
- * with a maximum length of <code>length</code>
- *
- * @see sstrsubs()
- * @see sstrchr()
- */
-sstr_t sstrsubsl(sstr_t string, size_t start, size_t length);
-
-/**
- * Returns a substring of an immutable string starting at the specified
- * location.
- *
- * <b>Attention:</b> the new string references the same memory area as the
-* input string and is <b>NOT</b> required to be <code>NULL</code>-terminated.
- * Use scstrdup() to get a copy.
- *
- * @param string input string
- * @param start start location of the substring
- * @return a substring of <code>string</code> starting at <code>start</code>
- *
- * @see scstrsubsl()
- * @see scstrchr()
- */
-scstr_t scstrsubs(scstr_t string, size_t start);
-
-/**
- * Returns a substring of an immutable string with a maximum length starting
- * at the specified location.
- *
- * <b>Attention:</b> the new string references the same memory area as the
- * input string and is <b>NOT</b> required to be <code>NULL</code>-terminated.
- * Use scstrdup() to get a copy.
- *
- * @param string input string
- * @param start start location of the substring
- * @param length the maximum length of the substring
- * @return a substring of <code>string</code> starting at <code>start</code>
- * with a maximum length of <code>length</code>
- *
- * @see scstrsubs()
- * @see scstrchr()
- */
-scstr_t scstrsubsl(scstr_t string, size_t start, size_t length);
-
-/**
- * Returns a substring starting at the location of the first occurrence of the
- * specified character.
- *
- * If the string does not contain the character, an empty string is returned.
- *
- * @param string the string where to locate the character
- * @param chr the character to locate
- * @return a substring starting at the first location of <code>chr</code>
- *
- * @see sstrsubs()
- */
-sstr_t sstrchr(sstr_t string, int chr);
-
-/**
- * Returns a substring starting at the location of the last occurrence of the
- * specified character.
- *
- * If the string does not contain the character, an empty string is returned.
- *
- * @param string the string where to locate the character
- * @param chr the character to locate
- * @return a substring starting at the last location of <code>chr</code>
- *
- * @see sstrsubs()
- */
-sstr_t sstrrchr(sstr_t string, int chr);
-
-/**
- * Returns an immutable substring starting at the location of the first
- * occurrence of the specified character.
- *
- * If the string does not contain the character, an empty string is returned.
- *
- * @param string the string where to locate the character
- * @param chr the character to locate
- * @return a substring starting at the first location of <code>chr</code>
- *
- * @see scstrsubs()
- */
-scstr_t scstrchr(scstr_t string, int chr);
-
-/**
- * Returns an immutable substring starting at the location of the last
- * occurrence of the specified character.
- *
- * If the string does not contain the character, an empty string is returned.
- *
- * @param string the string where to locate the character
- * @param chr the character to locate
- * @return a substring starting at the last location of <code>chr</code>
- *
- * @see scstrsubs()
- */
-scstr_t scstrrchr(scstr_t string, int chr);
-
-/**
- * Returns a substring starting at the location of the first occurrence of the
- * specified string.
- *
- * If the string does not contain the other string, an empty string is returned.
- *
- * If <code>match</code> is an empty string, the complete <code>string</code> is
- * returned.
- *
- * @param string the string to be scanned
- * @param match string containing the sequence of characters to match
- * @return a substring starting at the first occurrence of
- * <code>match</code>, or an empty string, if the sequence is not
- * present in <code>string</code>
- */
-sstr_t scstrsstr(sstr_t string, scstr_t match);
-
-/**
- * Returns a substring starting at the location of the first occurrence of the
- * specified string.
- *
- * If the string does not contain the other string, an empty string is returned.
- *
- * If <code>match</code> is an empty string, the complete <code>string</code> is
- * returned.
- *
- * @param string the string to be scanned
- * @param match string containing the sequence of characters to match
- * @return a substring starting at the first occurrence of
- * <code>match</code>, or an empty string, if the sequence is not
- * present in <code>string</code>
- */
-#define sstrstr(string, match) scstrsstr(string, SCSTR(match))
-
-/**
- * Returns an immutable substring starting at the location of the
- * first occurrence of the specified immutable string.
- *
- * If the string does not contain the other string, an empty string is returned.
- *
- * If <code>match</code> is an empty string, the complete <code>string</code> is
- * returned.
- *
- * @param string the string to be scanned
- * @param match string containing the sequence of characters to match
- * @return a substring starting at the first occurrence of
- * <code>match</code>, or an empty string, if the sequence is not
- * present in <code>string</code>
- */
-scstr_t scstrscstr(scstr_t string, scstr_t match);
-
-/**
- * Returns an immutable substring starting at the location of the
- * first occurrence of the specified immutable string.
- *
- * If the string does not contain the other string, an empty string is returned.
- *
- * If <code>match</code> is an empty string, the complete <code>string</code> is
- * returned.
- *
- * @param string the string to be scanned
- * @param match string containing the sequence of characters to match
- * @return a substring starting at the first occurrence of
- * <code>match</code>, or an empty string, if the sequence is not
- * present in <code>string</code>
- */
-#define sstrscstr(string, match) scstrscstr(string, SCSTR(match))
-
-/**
- * Splits a string into parts by using a delimiter string.
- *
- * This function will return <code>NULL</code>, if one of the following happens:
- * <ul>
- * <li>the string length is zero</li>
- * <li>the delimeter length is zero</li>
- * <li>the string equals the delimeter</li>
- * <li>memory allocation fails</li>
- * </ul>
- *
- * The integer referenced by <code>count</code> is used as input and determines
- * the maximum size of the resulting array, i.e. the maximum count of splits to
- * perform + 1.
- *
- * The integer referenced by <code>count</code> is also used as output and is
- * set to
- * <ul>
- * <li>-2, on memory allocation errors</li>
- * <li>-1, if either the string or the delimiter is an empty string</li>
- * <li>0, if the string equals the delimiter</li>
- * <li>1, if the string does not contain the delimiter</li>
- * <li>the count of array items, otherwise</li>
- * </ul>
- *
- * If the string starts with the delimiter, the first item of the resulting
- * array will be an empty string.
- *
- * If the string ends with the delimiter and the maximum list size is not
- * exceeded, the last array item will be an empty string.
- * In case the list size would be exceeded, the last array item will be the
- * remaining string after the last split, <i>including</i> the terminating
- * delimiter.
- *
- * <b>Attention:</b> The array pointer <b>AND</b> all sstr_t.ptr of the array
- * items must be manually passed to <code>free()</code>. Use scstrsplit_a() with
- * an allocator to managed memory, to avoid this.
- *
- * @param string the string to split
- * @param delim the delimiter string
- * @param count IN: the maximum size of the resulting array (0 = no limit),
- * OUT: the actual size of the array
- * @return a sstr_t array containing the split strings or
- * <code>NULL</code> on error
- *
- * @see scstrsplit_a()
- */
-sstr_t* scstrsplit(scstr_t string, scstr_t delim, ssize_t *count);
-
-/**
- * Splits a string into parts by using a delimiter string.
- *
- * This function will return <code>NULL</code>, if one of the following happens:
- * <ul>
- * <li>the string length is zero</li>
- * <li>the delimeter length is zero</li>
- * <li>the string equals the delimeter</li>
- * <li>memory allocation fails</li>
- * </ul>
- *
- * The integer referenced by <code>count</code> is used as input and determines
- * the maximum size of the resulting array, i.e. the maximum count of splits to
- * perform + 1.
- *
- * The integer referenced by <code>count</code> is also used as output and is
- * set to
- * <ul>
- * <li>-2, on memory allocation errors</li>
- * <li>-1, if either the string or the delimiter is an empty string</li>
- * <li>0, if the string equals the delimiter</li>
- * <li>1, if the string does not contain the delimiter</li>
- * <li>the count of array items, otherwise</li>
- * </ul>
- *
- * If the string starts with the delimiter, the first item of the resulting
- * array will be an empty string.
- *
- * If the string ends with the delimiter and the maximum list size is not
- * exceeded, the last array item will be an empty string.
- * In case the list size would be exceeded, the last array item will be the
- * remaining string after the last split, <i>including</i> the terminating
- * delimiter.
- *
- * <b>Attention:</b> The array pointer <b>AND</b> all sstr_t.ptr of the array
- * items must be manually passed to <code>free()</code>. Use sstrsplit_a() with
- * an allocator to managed memory, to avoid this.
- *
- * @param string the string to split
- * @param delim the delimiter string
- * @param count IN: the maximum size of the resulting array (0 = no limit),
- * OUT: the actual size of the array
- * @return a sstr_t array containing the split strings or
- * <code>NULL</code> on error
- *
- * @see sstrsplit_a()
- */
-#define sstrsplit(string, delim, count) \
- scstrsplit(SCSTR(string), SCSTR(delim), count)
-
-/**
- * Performing scstrsplit() using a UcxAllocator.
- *
- * <i>Read the description of scstrsplit() for details.</i>
- *
- * The memory for the sstr_t.ptr pointers of the array items and the memory for
- * the sstr_t array itself are allocated by using the UcxAllocator.malloc()
- * function.
- *
- * @param allocator the UcxAllocator used for allocating memory
- * @param string the string to split
- * @param delim the delimiter string
- * @param count IN: the maximum size of the resulting array (0 = no limit),
- * OUT: the actual size of the array
- * @return a sstr_t array containing the split strings or
- * <code>NULL</code> on error
- *
- * @see scstrsplit()
- */
-sstr_t* scstrsplit_a(UcxAllocator *allocator, scstr_t string, scstr_t delim,
- ssize_t *count);
-
-/**
- * Performing sstrsplit() using a UcxAllocator.
- *
- * <i>Read the description of sstrsplit() for details.</i>
- *
- * The memory for the sstr_t.ptr pointers of the array items and the memory for
- * the sstr_t array itself are allocated by using the UcxAllocator.malloc()
- * function.
- *
- * @param allocator the UcxAllocator used for allocating memory
- * @param string the string to split
- * @param delim the delimiter string
- * @param count IN: the maximum size of the resulting array (0 = no limit),
- * OUT: the actual size of the array
- * @return a sstr_t array containing the split strings or
- * <code>NULL</code> on error
- *
- * @see sstrsplit()
- */
-#define sstrsplit_a(allocator, string, delim, count) \
- scstrsplit_a(allocator, SCSTR(string), SCSTR(delim), count)
-
-/**
- * Compares two UCX strings with standard <code>memcmp()</code>.
- *
- * At first it compares the scstr_t.length attribute of the two strings. The
- * <code>memcmp()</code> function is called, if and only if the lengths match.
- *
- * @param s1 the first string
- * @param s2 the second string
- * @return -1, if the length of s1 is less than the length of s2 or 1, if the
- * length of s1 is greater than the length of s2 or the result of
- * <code>memcmp()</code> otherwise (i.e. 0 if the strings match)
- */
-int scstrcmp(scstr_t s1, scstr_t s2);
-
-/**
- * Compares two UCX strings with standard <code>memcmp()</code>.
- *
- * At first it compares the sstr_t.length attribute of the two strings. The
- * <code>memcmp()</code> function is called, if and only if the lengths match.
- *
- * @param s1 the first string
- * @param s2 the second string
- * @return -1, if the length of s1 is less than the length of s2 or 1, if the
- * length of s1 is greater than the length of s2 or the result of
- * <code>memcmp()</code> otherwise (i.e. 0 if the strings match)
- */
-#define sstrcmp(s1, s2) scstrcmp(SCSTR(s1), SCSTR(s2))
-
-/**
- * Compares two UCX strings ignoring the case.
- *
- * At first it compares the scstr_t.length attribute of the two strings. If and
- * only if the lengths match, both strings are compared char by char ignoring
- * the case.
- *
- * @param s1 the first string
- * @param s2 the second string
- * @return -1, if the length of s1 is less than the length of s2 or 1, if the
- * length of s1 is greater than the length of s2 or the result of the platform
- * specific string comparison function ignoring the case.
- */
-int scstrcasecmp(scstr_t s1, scstr_t s2);
-
-/**
- * Compares two UCX strings ignoring the case.
- *
- * At first it compares the sstr_t.length attribute of the two strings. If and
- * only if the lengths match, both strings are compared char by char ignoring
- * the case.
- *
- * @param s1 the first string
- * @param s2 the second string
- * @return -1, if the length of s1 is less than the length of s2 or 1, if the
- * length of s1 is greater than the length of s2 or the result of the platform
- * specific string comparison function ignoring the case.
- */
-#define sstrcasecmp(s1, s2) scstrcasecmp(SCSTR(s1), SCSTR(s2))
-
-/**
- * Creates a duplicate of the specified string.
- *
- * The new sstr_t will contain a copy allocated by standard
- * <code>malloc()</code>. So developers <b>MUST</b> pass the sstr_t.ptr to
- * <code>free()</code>.
- *
- * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>-
- * terminated and mutable, regardless of the argument.
- *
- * @param string the string to duplicate
- * @return a duplicate of the string
- * @see scstrdup_a()
- */
-sstr_t scstrdup(scstr_t string);
-
-/**
- * Creates a duplicate of the specified string.
- *
- * The new sstr_t will contain a copy allocated by standard
- * <code>malloc()</code>. So developers <b>MUST</b> pass the sstr_t.ptr to
- * <code>free()</code>.
- *
- * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>-
- * terminated, regardless of the argument.
- *
- * @param string the string to duplicate
- * @return a duplicate of the string
- * @see sstrdup_a()
- */
-#define sstrdup(string) scstrdup(SCSTR(string))
-
-/**
- * Creates a duplicate of the specified string using a UcxAllocator.
- *
- * The new sstr_t will contain a copy allocated by the allocators
- * UcxAllocator.malloc() function. So it is implementation depended, whether the
- * returned sstr_t.ptr pointer must be passed to the allocators
- * UcxAllocator.free() function manually.
- *
- * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>-
- * terminated and mutable, regardless of the argument.
- *
- * @param allocator a valid instance of a UcxAllocator
- * @param string the string to duplicate
- * @return a duplicate of the string
- * @see scstrdup()
- */
-sstr_t scstrdup_a(UcxAllocator *allocator, scstr_t string);
-
-/**
- * Creates a duplicate of the specified string using a UcxAllocator.
- *
- * The new sstr_t will contain a copy allocated by the allocators
- * UcxAllocator.malloc() function. So it is implementation depended, whether the
- * returned sstr_t.ptr pointer must be passed to the allocators
- * UcxAllocator.free() function manually.
- *
- * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>-
- * terminated, regardless of the argument.
- *
- * @param allocator a valid instance of a UcxAllocator
- * @param string the string to duplicate
- * @return a duplicate of the string
- * @see scstrdup()
- */
-#define sstrdup_a(allocator, string) scstrdup_a(allocator, SCSTR(string))
-
-
-/**
- * Omits leading and trailing spaces.
- *
- * This function returns a new sstr_t containing a trimmed version of the
- * specified string.
- *
- * <b>Note:</b> the new sstr_t references the same memory, thus you
- * <b>MUST NOT</b> pass the sstr_t.ptr of the return value to
- * <code>free()</code>. It is also highly recommended to avoid assignments like
- * <code>mystr = sstrtrim(mystr);</code> as you lose the reference to the
- * source string. Assignments of this type are only permitted, if the
- * sstr_t.ptr of the source string does not need to be freed or if another
- * reference to the source string exists.
- *
- * @param string the string that shall be trimmed
- * @return a new sstr_t containing the trimmed string
- */
-sstr_t sstrtrim(sstr_t string);
-
-/**
- * Omits leading and trailing spaces.
- *
- * This function returns a new scstr_t containing a trimmed version of the
- * specified string.
- *
- * <b>Note:</b> the new scstr_t references the same memory, thus you
- * <b>MUST NOT</b> pass the scstr_t.ptr of the return value to
- * <code>free()</code>. It is also highly recommended to avoid assignments like
- * <code>mystr = scstrtrim(mystr);</code> as you lose the reference to the
- * source string. Assignments of this type are only permitted, if the
- * scstr_t.ptr of the source string does not need to be freed or if another
- * reference to the source string exists.
- *
- * @param string the string that shall be trimmed
- * @return a new scstr_t containing the trimmed string
- */
-scstr_t scstrtrim(scstr_t string);
-
-/**
- * Checks, if a string has a specific prefix.
- *
- * @param string the string to check
- * @param prefix the prefix the string should have
- * @return 1, if and only if the string has the specified prefix, 0 otherwise
- */
-int scstrprefix(scstr_t string, scstr_t prefix);
-
-/**
- * Checks, if a string has a specific prefix.
- *
- * @param string the string to check
- * @param prefix the prefix the string should have
- * @return 1, if and only if the string has the specified prefix, 0 otherwise
- */
-#define sstrprefix(string, prefix) scstrprefix(SCSTR(string), SCSTR(prefix))
-
-/**
- * Checks, if a string has a specific suffix.
- *
- * @param string the string to check
- * @param suffix the suffix the string should have
- * @return 1, if and only if the string has the specified suffix, 0 otherwise
- */
-int scstrsuffix(scstr_t string, scstr_t suffix);
-
-/**
- * Checks, if a string has a specific suffix.
- *
- * @param string the string to check
- * @param suffix the suffix the string should have
- * @return 1, if and only if the string has the specified suffix, 0 otherwise
- */
-#define sstrsuffix(string, suffix) scstrsuffix(SCSTR(string), SCSTR(suffix))
-
-/**
- * Checks, if a string has a specific prefix, ignoring the case.
- *
- * @param string the string to check
- * @param prefix the prefix the string should have
- * @return 1, if and only if the string has the specified prefix, 0 otherwise
- */
-int scstrcaseprefix(scstr_t string, scstr_t prefix);
-
-/**
- * Checks, if a string has a specific prefix, ignoring the case.
- *
- * @param string the string to check
- * @param prefix the prefix the string should have
- * @return 1, if and only if the string has the specified prefix, 0 otherwise
- */
-#define sstrcaseprefix(string, prefix) \
- scstrcaseprefix(SCSTR(string), SCSTR(prefix))
-
-/**
- * Checks, if a string has a specific suffix, ignoring the case.
- *
- * @param string the string to check
- * @param suffix the suffix the string should have
- * @return 1, if and only if the string has the specified suffix, 0 otherwise
- */
-int scstrcasesuffix(scstr_t string, scstr_t suffix);
-
-/**
- * Checks, if a string has a specific suffix, ignoring the case.
- *
- * @param string the string to check
- * @param suffix the suffix the string should have
- * @return 1, if and only if the string has the specified suffix, 0 otherwise
- */
-#define sstrcasesuffix(string, suffix) \
- scstrcasesuffix(SCSTR(string), SCSTR(suffix))
-
-/**
- * Returns a lower case version of a string.
- *
- * This function creates a duplicate of the input string, first
- * (see scstrdup()).
- *
- * @param string the input string
- * @return the resulting lower case string
- * @see scstrdup()
- */
-sstr_t scstrlower(scstr_t string);
-
-/**
- * Returns a lower case version of a string.
- *
- * This function creates a duplicate of the input string, first
- * (see sstrdup()).
- *
- * @param string the input string
- * @return the resulting lower case string
- */
-#define sstrlower(string) scstrlower(SCSTR(string))
-
-/**
- * Returns a lower case version of a string.
- *
- * This function creates a duplicate of the input string, first
- * (see scstrdup_a()).
- *
- * @param allocator the allocator used for duplicating the string
- * @param string the input string
- * @return the resulting lower case string
- * @see scstrdup_a()
- */
-sstr_t scstrlower_a(UcxAllocator *allocator, scstr_t string);
-
-
-/**
- * Returns a lower case version of a string.
- *
- * This function creates a duplicate of the input string, first
- * (see sstrdup_a()).
- *
- * @param allocator the allocator used for duplicating the string
- * @param string the input string
- * @return the resulting lower case string
- */
-#define sstrlower_a(allocator, string) scstrlower_a(allocator, SCSTR(string))
-
-/**
- * Returns a upper case version of a string.
- *
- * This function creates a duplicate of the input string, first
- * (see scstrdup()).
- *
- * @param string the input string
- * @return the resulting upper case string
- * @see scstrdup()
- */
-sstr_t scstrupper(scstr_t string);
-
-/**
- * Returns a upper case version of a string.
- *
- * This function creates a duplicate of the input string, first
- * (see sstrdup()).
- *
- * @param string the input string
- * @return the resulting upper case string
- */
-#define sstrupper(string) scstrupper(SCSTR(string))
-
-/**
- * Returns a upper case version of a string.
- *
- * This function creates a duplicate of the input string, first
- * (see scstrdup_a()).
- *
- * @param allocator the allocator used for duplicating the string
- * @param string the input string
- * @return the resulting upper case string
- * @see scstrdup_a()
- */
-sstr_t scstrupper_a(UcxAllocator *allocator, scstr_t string);
-
-/**
- * Returns a upper case version of a string.
- *
- * This function creates a duplicate of the input string, first
- * (see sstrdup_a()).
- *
- * @param allocator the allocator used for duplicating the string
- * @param string the input string
- * @return the resulting upper case string
- */
-#define sstrupper_a(allocator, string) scstrupper_a(allocator, string)
-
-
-/**
- * Replaces a pattern in a string with another string.
- *
- * The pattern is taken literally and is no regular expression.
- * Replaces at most <code>replmax</code> occurrences.
- *
- * The resulting string is allocated by the specified allocator. I.e. it
- * depends on the used allocator, whether the sstr_t.ptr must be freed
- * manually.
- *
- * If allocation fails, the sstr_t.ptr of the return value is NULL.
- *
- * @param allocator the allocator to use
- * @param str the string where replacements should be applied
- * @param pattern the pattern to search for
- * @param replacement the replacement string
- * @param replmax maximum number of replacements
- * @return the resulting string after applying the replacements
- */
-sstr_t scstrreplacen_a(UcxAllocator *allocator, scstr_t str,
- scstr_t pattern, scstr_t replacement, size_t replmax);
-
-/**
- * Replaces a pattern in a string with another string.
- *
- * The pattern is taken literally and is no regular expression.
- * Replaces at most <code>replmax</code> occurrences.
- *
- * The sstr_t.ptr of the resulting string must be freed manually.
- *
- * If allocation fails, the sstr_t.ptr of the return value is NULL.
- *
- * @param str the string where replacements should be applied
- * @param pattern the pattern to search for
- * @param replacement the replacement string
- * @param replmax maximum number of replacements
- * @return the resulting string after applying the replacements
- */
-sstr_t scstrreplacen(scstr_t str, scstr_t pattern,
- scstr_t replacement, size_t replmax);
-
-/**
- * Replaces a pattern in a string with another string.
- *
- * The pattern is taken literally and is no regular expression.
- * Replaces at most <code>replmax</code> occurrences.
- *
- * The resulting string is allocated by the specified allocator. I.e. it
- * depends on the used allocator, whether the sstr_t.ptr must be freed
- * manually.
- *
- * @param allocator the allocator to use
- * @param str the string where replacements should be applied
- * @param pattern the pattern to search for
- * @param replacement the replacement string
- * @param replmax maximum number of replacements
- * @return the resulting string after applying the replacements
- */
-#define sstrreplacen_a(allocator, str, pattern, replacement, replmax) \
- scstrreplacen_a(allocator, SCSTR(str), SCSTR(pattern), \
- SCSTR(replacement), replmax)
-
-/**
- * Replaces a pattern in a string with another string.
- *
- * The pattern is taken literally and is no regular expression.
- * Replaces at most <code>replmax</code> occurrences.
- *
- * The sstr_t.ptr of the resulting string must be freed manually.
- *
- * If allocation fails, the sstr_t.ptr of the return value is NULL.
- *
- * @param str the string where replacements should be applied
- * @param pattern the pattern to search for
- * @param replacement the replacement string
- * @param replmax maximum number of replacements
- * @return the resulting string after applying the replacements
- */
-#define sstrreplacen(str, pattern, replacement, replmax) \
- scstrreplacen(SCSTR(str), SCSTR(pattern), SCSTR(replacement), replmax)
-
-/**
- * Replaces a pattern in a string with another string.
- *
- * The pattern is taken literally and is no regular expression.
- * Replaces at most <code>replmax</code> occurrences.
- *
- * The resulting string is allocated by the specified allocator. I.e. it
- * depends on the used allocator, whether the sstr_t.ptr must be freed
- * manually.
- *
- * If allocation fails, the sstr_t.ptr of the return value is NULL.
- *
- * @param allocator the allocator to use
- * @param str the string where replacements should be applied
- * @param pattern the pattern to search for
- * @param replacement the replacement string
- * @return the resulting string after applying the replacements
- */
-#define sstrreplace_a(allocator, str, pattern, replacement) \
- scstrreplacen_a(allocator, SCSTR(str), SCSTR(pattern), \
- SCSTR(replacement), SIZE_MAX)
-
-/**
- * Replaces a pattern in a string with another string.
- *
- * The pattern is taken literally and is no regular expression.
- * Replaces at most <code>replmax</code> occurrences.
- *
- * The sstr_t.ptr of the resulting string must be freed manually.
- *
- * If allocation fails, the sstr_t.ptr of the return value is NULL.
- *
- * @param str the string where replacements should be applied
- * @param pattern the pattern to search for
- * @param replacement the replacement string
- * @return the resulting string after applying the replacements
- */
-#define sstrreplace(str, pattern, replacement) \
- scstrreplacen(SCSTR(str), SCSTR(pattern), SCSTR(replacement), SIZE_MAX)
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* UCX_STRING_H */
+++ /dev/null
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-/**
- * @file: test.h
- *
- * UCX Test Framework.
- *
- * Usage of this test framework:
- *
- * **** IN HEADER FILE: ****
- *
- * <pre>
- * UCX_TEST(function_name);
- * UCX_TEST_SUBROUTINE(subroutine_name, paramlist); // optional
- * </pre>
- *
- * **** IN SOURCE FILE: ****
- * <pre>
- * UCX_TEST_SUBROUTINE(subroutine_name, paramlist) {
- * // tests with UCX_TEST_ASSERT()
- * }
- *
- * UCX_TEST(function_name) {
- * // memory allocation and other stuff here
- * #UCX_TEST_BEGIN
- * // tests with UCX_TEST_ASSERT() and/or
- * // calls with UCX_TEST_CALL_SUBROUTINE() here
- * #UCX_TEST_END
- * // cleanup of memory here
- * }
- * </pre>
- *
- * <b>Note:</b> if a test fails, a longjump is performed
- * back to the #UCX_TEST_BEGIN macro!
- *
- * <b>Attention:</b> Do not call own functions within a test, that use
- * UCX_TEST_ASSERT() macros and are not defined by using UCX_TEST_SUBROUTINE().
- *
- *
- * @author Mike Becker
- * @author Olaf Wintermann
- *
- */
-
-#ifndef UCX_TEST_H
-#define UCX_TEST_H
-
-#include "ucx.h"
-#include <stdio.h>
-#include <string.h>
-#include <setjmp.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef __FUNCTION__
-
-/**
- * Alias for the <code>__func__</code> preprocessor macro.
- * Some compilers use <code>__func__</code> and others use __FUNCTION__.
- * We use __FUNCTION__ so we define it for those compilers which use
- * <code>__func__</code>.
- */
-#define __FUNCTION__ __func__
-#endif
-
-/** Type for the UcxTestSuite. */
-typedef struct UcxTestSuite UcxTestSuite;
-
-/** Pointer to a test function. */
-typedef void(*UcxTest)(UcxTestSuite*,FILE*);
-
-/** Type for the internal list of test cases. */
-typedef struct UcxTestList UcxTestList;
-
-/** Structure for the internal list of test cases. */
-struct UcxTestList {
-
- /** Test case. */
- UcxTest test;
-
- /** Pointer to the next list element. */
- UcxTestList *next;
-};
-
-/**
- * A test suite containing multiple test cases.
- */
-struct UcxTestSuite {
-
- /** The number of successful tests after the suite has been run. */
- unsigned int success;
-
- /** The number of failed tests after the suite has been run. */
- unsigned int failure;
-
- /**
- * Internal list of test cases.
- * Use ucx_test_register() to add tests to this list.
- */
- UcxTestList *tests;
-};
-
-/**
- * Creates a new test suite.
- * @return a new test suite
- */
-UcxTestSuite* ucx_test_suite_new();
-
-/**
- * Destroys a test suite.
- * @param suite the test suite to destroy
- */
-void ucx_test_suite_free(UcxTestSuite* suite);
-
-/**
- * Registers a test function with the specified test suite.
- *
- * @param suite the suite, the test function shall be added to
- * @param test the test function to register
- * @return <code>EXIT_SUCCESS</code> on success or
- * <code>EXIT_FAILURE</code> on failure
- */
-int ucx_test_register(UcxTestSuite* suite, UcxTest test);
-
-/**
- * Runs a test suite and writes the test log to the specified stream.
- * @param suite the test suite to run
- * @param outstream the stream the log shall be written to
- */
-void ucx_test_run(UcxTestSuite* suite, FILE* outstream);
-
-/**
- * Macro for a #UcxTest function header.
- *
- * Use this macro to declare and/or define a #UcxTest function.
- *
- * @param name the name of the test function
- */
-#define UCX_TEST(name) void name(UcxTestSuite* _suite_,FILE *_output_)
-
-/**
- * Marks the begin of a test.
- * <b>Note:</b> Any UCX_TEST_ASSERT() calls must be performed <b>after</b>
- * #UCX_TEST_BEGIN.
- *
- * @see #UCX_TEST_END
- */
-#define UCX_TEST_BEGIN fwrite("Running ", 1, 8, _output_);\
- fwrite(__FUNCTION__, 1, strlen(__FUNCTION__), _output_);\
- fwrite("... ", 1, 4, _output_);\
- jmp_buf _env_; \
- if (!setjmp(_env_)) {
-
-/**
- * Checks a test assertion.
- * If the assertion is correct, the test carries on. If the assertion is not
- * correct, the specified message (terminated by a dot and a line break) is
- * written to the test suites output stream.
- * @param condition the condition to check
- * @param message the message that shall be printed out on failure
- */
-#define UCX_TEST_ASSERT(condition,message) if (!(condition)) { \
- fwrite(message".\n", 1, 2+strlen(message), _output_); \
- _suite_->failure++; \
- longjmp(_env_, 1);\
- }
-
-/**
- * Macro for a test subroutine function header.
- *
- * Use this to declare and/or define a subroutine that can be called by using
- * UCX_TEST_CALL_SUBROUTINE().
- *
- * @param name the name of the subroutine
- * @param ... the parameter list
- *
- * @see UCX_TEST_CALL_SUBROUTINE()
- */
-#define UCX_TEST_SUBROUTINE(name,...) void name(UcxTestSuite* _suite_,\
- FILE *_output_, jmp_buf _env_, __VA_ARGS__)
-
-/**
- * Macro for calling a test subroutine.
- *
- * Subroutines declared with UCX_TEST_SUBROUTINE() can be called by using this
- * macro.
- *
- * <b>Note:</b> You may <b>only</b> call subroutines within a #UCX_TEST_BEGIN-
- * #UCX_TEST_END-block.
- *
- * @param name the name of the subroutine
- * @param ... the argument list
- *
- * @see UCX_TEST_SUBROUTINE()
- */
-#define UCX_TEST_CALL_SUBROUTINE(name,...) \
- name(_suite_,_output_,_env_,__VA_ARGS__);
-
-/**
- * Marks the end of a test.
- * <b>Note:</b> Any UCX_TEST_ASSERT() calls must be performed <b>before</b>
- * #UCX_TEST_END.
- *
- * @see #UCX_TEST_BEGIN
- */
-#define UCX_TEST_END fwrite("success.\n", 1, 9, _output_); _suite_->success++;}
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* UCX_TEST_H */
-
+++ /dev/null
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-/**
- * Main UCX Header providing most common definitions.
- *
- * @file ucx.h
- * @author Mike Becker
- * @author Olaf Wintermann
- */
-
-#ifndef UCX_H
-#define UCX_H
-
-/** Major UCX version as integer constant. */
-#define UCX_VERSION_MAJOR 2
-
-/** Minor UCX version as integer constant. */
-#define UCX_VERSION_MINOR 1
-
-/** Version constant which ensures to increase monotonically. */
-#define UCX_VERSION (((UCX_VERSION_MAJOR)<<16)|UCX_VERSION_MINOR)
-
-#include <stdlib.h>
-#include <stdint.h>
-
-#ifdef _WIN32
-#if !(defined __ssize_t_defined || defined _SSIZE_T_)
-#include <BaseTsd.h>
-typedef SSIZE_T ssize_t;
-#define __ssize_t_defined
-#define _SSIZE_T_
-#endif /* __ssize_t_defined and _SSIZE_T */
-#else /* !_WIN32 */
-#include <sys/types.h>
-#endif /* _WIN32 */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-/**
- * A function pointer to a destructor function.
- * @see ucx_mempool_setdestr()
- * @see ucx_mempool_regdestr()
- */
-typedef void(*ucx_destructor)(void*);
-
-/**
- * Function pointer to a compare function.
- *
- * The compare function shall take three arguments: the two values that shall be
- * compared and optional additional data.
- * The function shall then return -1 if the first argument is less than the
- * second argument, 1 if the first argument is greater than the second argument
- * and 0 if both arguments are equal. If the third argument is
- * <code>NULL</code>, it shall be ignored.
- */
-typedef int(*cmp_func)(const void*,const void*,void*);
-
-/**
- * Function pointer to a distance function.
- *
- * The distance function shall take three arguments: the two values for which
- * the distance shall be computed and optional additional data.
- * The function shall then return the signed distance as integer value.
- */
-typedef intmax_t(*distance_func)(const void*,const void*,void*);
-
-/**
- * Function pointer to a copy function.
- *
- * The copy function shall create a copy of the first argument and may use
- * additional data provided by the second argument. If the second argument is
- * <code>NULL</code>, it shall be ignored.
-
- * <b>Attention:</b> if pointers returned by functions of this type may be
- * passed to <code>free()</code> depends on the implementation of the
- * respective <code>copy_func</code>.
- */
-typedef void*(*copy_func)(const void*,void*);
-
-/**
- * Function pointer to a write function.
- *
- * The signature of the write function shall be compatible to the signature
- * of standard <code>fwrite</code>, though it may use arbitrary data types for
- * source and destination.
- *
- * The arguments shall contain (in ascending order): a pointer to the source,
- * the length of one element, the element count and a pointer to the
- * destination.
- */
-typedef size_t(*write_func)(const void*, size_t, size_t, void*);
-
-/**
- * Function pointer to a read function.
- *
- * The signature of the read function shall be compatible to the signature
- * of standard <code>fread</code>, though it may use arbitrary data types for
- * source and destination.
- *
- * The arguments shall contain (in ascending order): a pointer to the
- * destination, the length of one element, the element count and a pointer to
- * the source.
- */
-typedef size_t(*read_func)(void*, size_t, size_t, void*);
-
-
-
-#if __GNUC__ >= 5 || defined(__clang__)
-#define UCX_MUL_BUILTIN
-
-#if __WORDSIZE == 32
-/**
- * Alias for <code>__builtin_umul_overflow</code>.
- *
- * Performs a multiplication of size_t values and checks for overflow.
- *
- * @param a first operand
- * @param b second operand
- * @param result a pointer to a size_t, where the result should
- * be stored
- * @return zero, if no overflow occurred and the result is correct, non-zero
- * otherwise
- */
-#define ucx_szmul(a, b, result) __builtin_umul_overflow(a, b, result)
-#else /* __WORDSIZE != 32 */
-/**
- * Alias for <code>__builtin_umull_overflow</code>.
- *
- * Performs a multiplication of size_t values and checks for overflow.
- *
- * @param a first operand
- * @param b second operand
- * @param result a pointer to a size_t, where the result should
- * be stored
- * @return zero, if no overflow occurred and the result is correct, non-zero
- * otherwise
- */
-#define ucx_szmul(a, b, result) __builtin_umull_overflow(a, b, result)
-#endif /* __WORDSIZE */
-
-#else /* no GNUC or clang bultin */
-
-/**
- * Performs a multiplication of size_t values and checks for overflow.
- *
- * @param a first operand
- * @param b second operand
- * @param result a pointer to a size_t, where the result should
- * be stored
- * @return zero, if no overflow occurred and the result is correct, non-zero
- * otherwise
- */
-#define ucx_szmul(a, b, result) ucx_szmul_impl(a, b, result)
-
-/**
- * Performs a multiplication of size_t values and checks for overflow.
- *
- * This is a custom implementation in case there is no compiler builtin
- * available.
- *
- * @param a first operand
- * @param b second operand
- * @param result a pointer to a size_t where the result should be stored
- * @return zero, if no overflow occurred and the result is correct, non-zero
- * otherwise
- */
-int ucx_szmul_impl(size_t a, size_t b, size_t *result);
-
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* UCX_H */
-
+++ /dev/null
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-/**
- * @file utils.h
- *
- * Compare, copy and printf functions.
- *
- * @author Mike Becker
- * @author Olaf Wintermann
- */
-
-#ifndef UCX_UTILS_H
-#define UCX_UTILS_H
-
-#include "ucx.h"
-#include "string.h"
-#include "allocator.h"
-#include <inttypes.h>
-#include <string.h>
-#include <stdarg.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Default buffer size for ucx_stream_copy() and ucx_stream_ncopy().
- */
-#define UCX_STREAM_COPY_BUFSIZE 4096
-
-/**
- * Copies a string.
- * @param s the string to copy
- * @param data omitted
- * @return a pointer to a copy of s1 that can be passed to free(void*)
- */
-void *ucx_strcpy(const void *s, void *data);
-
-/**
- * Copies a memory area.
- * @param m a pointer to the memory area
- * @param n a pointer to the size_t containing the size of the memory area
- * @return a pointer to a copy of the specified memory area that can
- * be passed to free(void*)
- */
-void *ucx_memcpy(const void *m, void *n);
-
-
-/**
- * Reads data from a stream and writes it to another stream.
- *
- * @param src the source stream
- * @param dest the destination stream
- * @param rfnc the read function
- * @param wfnc the write function
- * @param buf a pointer to the copy buffer or <code>NULL</code> if a buffer
- * shall be implicitly created on the heap
- * @param bufsize the size of the copy buffer - if <code>NULL</code> was
- * provided for <code>buf</code>, this is the size of the buffer that shall be
- * implicitly created
- * @param n the maximum number of bytes that shall be copied
- * @return the total number of bytes copied
- */
-size_t ucx_stream_bncopy(void *src, void *dest, read_func rfnc, write_func wfnc,
- char* buf, size_t bufsize, size_t n);
-
-/**
- * Shorthand for an unbounded ucx_stream_bncopy call using a default buffer.
- *
- * @param src the source stream
- * @param dest the destination stream
- * @param rfnc the read function
- * @param wfnc the write function
- * @return total number of bytes copied
- *
- * @see #UCX_STREAM_COPY_BUFSIZE
- */
-#define ucx_stream_copy(src,dest,rfnc,wfnc) ucx_stream_bncopy(\
- src, dest, (read_func)rfnc, (write_func)wfnc, \
- NULL, UCX_STREAM_COPY_BUFSIZE, (size_t)-1)
-
-/**
- * Shorthand for ucx_stream_bncopy using a default copy buffer.
- *
- * @param src the source stream
- * @param dest the destination stream
- * @param rfnc the read function
- * @param wfnc the write function
- * @param n maximum number of bytes that shall be copied
- * @return total number of bytes copied
- */
-#define ucx_stream_ncopy(src,dest,rfnc,wfnc, n) ucx_stream_bncopy(\
- src, dest, (read_func)rfnc, (write_func)wfnc, \
- NULL, UCX_STREAM_COPY_BUFSIZE, n)
-
-/**
- * Shorthand for an unbounded ucx_stream_bncopy call using the specified buffer.
- *
- * @param src the source stream
- * @param dest the destination stream
- * @param rfnc the read function
- * @param wfnc the write function
- * @param buf a pointer to the copy buffer or <code>NULL</code> if a buffer
- * shall be implicitly created on the heap
- * @param bufsize the size of the copy buffer - if <code>NULL</code> was
- * provided for <code>buf</code>, this is the size of the buffer that shall be
- * implicitly created
- * @return total number of bytes copied
- */
-#define ucx_stream_bcopy(src,dest,rfnc,wfnc, buf, bufsize) ucx_stream_bncopy(\
- src, dest, (read_func)rfnc, (write_func)wfnc, \
- buf, bufsize, (size_t)-1)
-
-/**
- * Wraps the strcmp function.
- * @param s1 string one
- * @param s2 string two
- * @param data omitted
- * @return the result of strcmp(s1, s2)
- */
-int ucx_cmp_str(const void *s1, const void *s2, void *data);
-
-/**
- * Wraps the strncmp function.
- * @param s1 string one
- * @param s2 string two
- * @param n a pointer to the size_t containing the third strncmp parameter
- * @return the result of strncmp(s1, s2, *n)
- */
-int ucx_cmp_strn(const void *s1, const void *s2, void *n);
-
-/**
- * Wraps the sstrcmp function.
- * @param s1 sstr one
- * @param s2 sstr two
- * @param data ignored
- * @return the result of sstrcmp(s1, s2)
- */
-int ucx_cmp_sstr(const void *s1, const void *s2, void *data);
-
-/**
- * Compares two integers of type int.
- * @param i1 pointer to integer one
- * @param i2 pointer to integer two
- * @param data omitted
- * @return -1, if *i1 is less than *i2, 0 if both are equal,
- * 1 if *i1 is greater than *i2
- */
-int ucx_cmp_int(const void *i1, const void *i2, void *data);
-
-/**
- * Compares two integers of type long int.
- * @param i1 pointer to long integer one
- * @param i2 pointer to long integer two
- * @param data omitted
- * @return -1, if *i1 is less than *i2, 0 if both are equal,
- * 1 if *i1 is greater than *i2
- */
-int ucx_cmp_longint(const void *i1, const void *i2, void *data);
-
-/**
- * Compares two integers of type long long.
- * @param i1 pointer to long long one
- * @param i2 pointer to long long two
- * @param data omitted
- * @return -1, if *i1 is less than *i2, 0 if both are equal,
- * 1 if *i1 is greater than *i2
- */
-int ucx_cmp_longlong(const void *i1, const void *i2, void *data);
-
-/**
- * Compares two integers of type int16_t.
- * @param i1 pointer to int16_t one
- * @param i2 pointer to int16_t two
- * @param data omitted
- * @return -1, if *i1 is less than *i2, 0 if both are equal,
- * 1 if *i1 is greater than *i2
- */
-int ucx_cmp_int16(const void *i1, const void *i2, void *data);
-
-/**
- * Compares two integers of type int32_t.
- * @param i1 pointer to int32_t one
- * @param i2 pointer to int32_t two
- * @param data omitted
- * @return -1, if *i1 is less than *i2, 0 if both are equal,
- * 1 if *i1 is greater than *i2
- */
-int ucx_cmp_int32(const void *i1, const void *i2, void *data);
-
-/**
- * Compares two integers of type int64_t.
- * @param i1 pointer to int64_t one
- * @param i2 pointer to int64_t two
- * @param data omitted
- * @return -1, if *i1 is less than *i2, 0 if both are equal,
- * 1 if *i1 is greater than *i2
- */
-int ucx_cmp_int64(const void *i1, const void *i2, void *data);
-
-/**
- * Compares two integers of type unsigned int.
- * @param i1 pointer to unsigned integer one
- * @param i2 pointer to unsigned integer two
- * @param data omitted
- * @return -1, if *i1 is less than *i2, 0 if both are equal,
- * 1 if *i1 is greater than *i2
- */
-int ucx_cmp_uint(const void *i1, const void *i2, void *data);
-
-/**
- * Compares two integers of type unsigned long int.
- * @param i1 pointer to unsigned long integer one
- * @param i2 pointer to unsigned long integer two
- * @param data omitted
- * @return -1, if *i1 is less than *i2, 0 if both are equal,
- * 1 if *i1 is greater than *i2
- */
-int ucx_cmp_ulongint(const void *i1, const void *i2, void *data);
-
-/**
- * Compares two integers of type unsigned long long.
- * @param i1 pointer to unsigned long long one
- * @param i2 pointer to unsigned long long two
- * @param data omitted
- * @return -1, if *i1 is less than *i2, 0 if both are equal,
- * 1 if *i1 is greater than *i2
- */
-int ucx_cmp_ulonglong(const void *i1, const void *i2, void *data);
-
-/**
- * Compares two integers of type uint16_t.
- * @param i1 pointer to uint16_t one
- * @param i2 pointer to uint16_t two
- * @param data omitted
- * @return -1, if *i1 is less than *i2, 0 if both are equal,
- * 1 if *i1 is greater than *i2
- */
-int ucx_cmp_uint16(const void *i1, const void *i2, void *data);
-
-/**
- * Compares two integers of type uint32_t.
- * @param i1 pointer to uint32_t one
- * @param i2 pointer to uint32_t two
- * @param data omitted
- * @return -1, if *i1 is less than *i2, 0 if both are equal,
- * 1 if *i1 is greater than *i2
- */
-int ucx_cmp_uint32(const void *i1, const void *i2, void *data);
-
-/**
- * Compares two integers of type uint64_t.
- * @param i1 pointer to uint64_t one
- * @param i2 pointer to uint64_t two
- * @param data omitted
- * @return -1, if *i1 is less than *i2, 0 if both are equal,
- * 1 if *i1 is greater than *i2
- */
-int ucx_cmp_uint64(const void *i1, const void *i2, void *data);
-
-/**
- * Distance function for integers of type int.
- * @param i1 pointer to integer one
- * @param i2 pointer to integer two
- * @param data omitted
- * @return i1 minus i2
- */
-intmax_t ucx_dist_int(const void *i1, const void *i2, void *data);
-
-/**
- * Distance function for integers of type long int.
- * @param i1 pointer to long integer one
- * @param i2 pointer to long integer two
- * @param data omitted
- * @return i1 minus i2
- */
-intmax_t ucx_dist_longint(const void *i1, const void *i2, void *data);
-
-/**
- * Distance function for integers of type long long.
- * @param i1 pointer to long long one
- * @param i2 pointer to long long two
- * @param data omitted
- * @return i1 minus i2
- */
-intmax_t ucx_dist_longlong(const void *i1, const void *i2, void *data);
-
-/**
- * Distance function for integers of type int16_t.
- * @param i1 pointer to int16_t one
- * @param i2 pointer to int16_t two
- * @param data omitted
- * @return i1 minus i2
- */
-intmax_t ucx_dist_int16(const void *i1, const void *i2, void *data);
-
-/**
- * Distance function for integers of type int32_t.
- * @param i1 pointer to int32_t one
- * @param i2 pointer to int32_t two
- * @param data omitted
- * @return i1 minus i2
- */
-intmax_t ucx_dist_int32(const void *i1, const void *i2, void *data);
-
-/**
- * Distance function for integers of type int64_t.
- * @param i1 pointer to int64_t one
- * @param i2 pointer to int64_t two
- * @param data omitted
- * @return i1 minus i2
- */
-intmax_t ucx_dist_int64(const void *i1, const void *i2, void *data);
-
-/**
- * Distance function for integers of type unsigned int.
- * @param i1 pointer to unsigned integer one
- * @param i2 pointer to unsigned integer two
- * @param data omitted
- * @return i1 minus i2
- */
-intmax_t ucx_dist_uint(const void *i1, const void *i2, void *data);
-
-/**
- * Distance function for integers of type unsigned long int.
- * @param i1 pointer to unsigned long integer one
- * @param i2 pointer to unsigned long integer two
- * @param data omitted
- * @return i1 minus i2
- */
-intmax_t ucx_dist_ulongint(const void *i1, const void *i2, void *data);
-
-/**
- * Distance function for integers of type unsigned long long.
- * @param i1 pointer to unsigned long long one
- * @param i2 pointer to unsigned long long two
- * @param data omitted
- * @return i1 minus i2
- */
-intmax_t ucx_dist_ulonglong(const void *i1, const void *i2, void *data);
-
-/**
- * Distance function for integers of type uint16_t.
- * @param i1 pointer to uint16_t one
- * @param i2 pointer to uint16_t two
- * @param data omitted
- * @return i1 minus i2
- */
-intmax_t ucx_dist_uint16(const void *i1, const void *i2, void *data);
-
-/**
- * Distance function for integers of type uint32_t.
- * @param i1 pointer to uint32_t one
- * @param i2 pointer to uint32_t two
- * @param data omitted
- * @return i1 minus i2
- */
-intmax_t ucx_dist_uint32(const void *i1, const void *i2, void *data);
-
-/**
- * Distance function for integers of type uint64_t.
- * @param i1 pointer to uint64_t one
- * @param i2 pointer to uint64_t two
- * @param data omitted
- * @return i1 minus i2
- */
-intmax_t ucx_dist_uint64(const void *i1, const void *i2, void *data);
-
-/**
- * Compares two real numbers of type float.
- * @param f1 pointer to float one
- * @param f2 pointer to float two
- * @param data if provided: a pointer to precision (default: 1e-6f)
- * @return -1, if *f1 is less than *f2, 0 if both are equal,
- * 1 if *f1 is greater than *f2
- */
-
-int ucx_cmp_float(const void *f1, const void *f2, void *data);
-
-/**
- * Compares two real numbers of type double.
- * @param d1 pointer to double one
- * @param d2 pointer to double two
- * @param data if provided: a pointer to precision (default: 1e-14)
- * @return -1, if *d1 is less than *d2, 0 if both are equal,
- * 1 if *d1 is greater than *d2
- */
-int ucx_cmp_double(const void *d1, const void *d2, void *data);
-
-/**
- * Compares two pointers.
- * @param ptr1 pointer one
- * @param ptr2 pointer two
- * @param data omitted
- * @return -1 if ptr1 is less than ptr2, 0 if both are equal,
- * 1 if ptr1 is greater than ptr2
- */
-int ucx_cmp_ptr(const void *ptr1, const void *ptr2, void *data);
-
-/**
- * Compares two memory areas.
- * @param ptr1 pointer one
- * @param ptr2 pointer two
- * @param n a pointer to the size_t containing the third parameter for memcmp
- * @return the result of memcmp(ptr1, ptr2, *n)
- */
-int ucx_cmp_mem(const void *ptr1, const void *ptr2, void *n);
-
-/**
- * A <code>printf()</code> like function which writes the output to a stream by
- * using a write_func().
- * @param stream the stream the data is written to
- * @param wfc the write function
- * @param fmt format string
- * @param ... additional arguments
- * @return the total number of bytes written
- */
-int ucx_fprintf(void *stream, write_func wfc, const char *fmt, ...);
-
-/**
- * <code>va_list</code> version of ucx_fprintf().
- * @param stream the stream the data is written to
- * @param wfc the write function
- * @param fmt format string
- * @param ap argument list
- * @return the total number of bytes written
- * @see ucx_fprintf()
- */
-int ucx_vfprintf(void *stream, write_func wfc, const char *fmt, va_list ap);
-
-/**
- * A <code>printf()</code> like function which allocates space for a sstr_t
- * the result is written to.
- *
- * <b>Attention</b>: The sstr_t data is allocated with the allocators
- * ucx_allocator_malloc() function. So it is implementation dependent, if
- * the returned sstr_t.ptr pointer must be passed to the allocators
- * ucx_allocator_free() function manually.
- *
- * <b>Note</b>: The sstr_t.ptr of the return value will <i>always</i> be
- * <code>NULL</code>-terminated.
- *
- * @param allocator the UcxAllocator used for allocating the result sstr_t
- * @param fmt format string
- * @param ... additional arguments
- * @return a sstr_t containing the formatted string
- */
-sstr_t ucx_asprintf(UcxAllocator *allocator, const char *fmt, ...);
-
-/**
- * <code>va_list</code> version of ucx_asprintf().
- *
- * @param allocator the UcxAllocator used for allocating the result sstr_t
- * @param fmt format string
- * @param ap argument list
- * @return a sstr_t containing the formatted string
- * @see ucx_asprintf()
- */
-sstr_t ucx_vasprintf(UcxAllocator *allocator, const char *fmt, va_list ap);
-
-/** Shortcut for ucx_asprintf() with default allocator. */
-#define ucx_sprintf(...) \
- ucx_asprintf(ucx_default_allocator(), __VA_ARGS__)
-
-/**
- * A <code>printf()</code> like function which writes the output to a
- * UcxBuffer.
- *
- * @param buffer the buffer the data is written to
- * @param ... format string and additional arguments
- * @return the total number of bytes written
- * @see ucx_fprintf()
- */
-#define ucx_bprintf(buffer, ...) ucx_fprintf((UcxBuffer*)buffer, \
- (write_func)ucx_buffer_write, __VA_ARGS__)
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* UCX_UTILS_H */
-
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* POSSIBILITY OF SUCH DAMAGE.
*/
-#include "ucx/utils.h"
-
-#include <math.h>
-#include <stdio.h>
-#include <limits.h>
-#include <errno.h>
-
-/* COPY FUCNTIONS */
-void* ucx_strcpy(const void* s, void* data) {
- const char *str = (const char*) s;
- size_t n = 1+strlen(str);
- char *cpy = (char*) malloc(n);
- memcpy(cpy, str, n);
- return cpy;
-}
-
-void* ucx_memcpy(const void* m, void* n) {
- size_t k = *((size_t*)n);
- void *cpy = malloc(k);
- memcpy(cpy, m, k);
- return cpy;
-}
-
-size_t ucx_stream_bncopy(void *src, void *dest, read_func readfnc,
- write_func writefnc, char* buf, size_t bufsize, size_t n) {
- if(n == 0 || bufsize == 0) {
+#include "cx/utils.h"
+
+#define CX_STREAM_BCOPY_BUF_SIZE 8192
+#define CX_STREAM_COPY_BUF_SIZE 1024
+
+size_t cx_stream_bncopy(
+ void *src,
+ void *dest,
+ cx_read_func rfnc,
+ cx_write_func wfnc,
+ char *buf,
+ size_t bufsize,
+ size_t n
+) {
+ if (n == 0) {
return 0;
}
-
- char *lbuf;
+
+ char *lbuf;
size_t ncp = 0;
-
- if(buf) {
+
+ if (buf) {
+ if (bufsize == 0) return 0;
lbuf = buf;
} else {
- lbuf = (char*)malloc(bufsize);
- if(lbuf == NULL) {
+ if (bufsize == 0) bufsize = CX_STREAM_BCOPY_BUF_SIZE;
+ lbuf = malloc(bufsize);
+ if (lbuf == NULL) {
return 0;
}
}
-
+
size_t r;
size_t rn = bufsize > n ? n : bufsize;
- while((r = readfnc(lbuf, 1, rn, src)) != 0) {
- r = writefnc(lbuf, 1, r, dest);
+ while ((r = rfnc(lbuf, 1, rn, src)) != 0) {
+ r = wfnc(lbuf, 1, r, dest);
ncp += r;
n -= r;
rn = bufsize > n ? n : bufsize;
- if(r == 0 || n == 0) {
+ if (r == 0 || n == 0) {
break;
}
}
-
+
if (lbuf != buf) {
free(lbuf);
}
-
- return ncp;
-}
-
-/* COMPARE FUNCTIONS */
-
-int ucx_cmp_str(const void *s1, const void *s2, void *data) {
- return strcmp((const char*)s1, (const char*)s2);
-}
-
-int ucx_cmp_strn(const void *s1, const void *s2, void *n) {
- return strncmp((const char*)s1, (const char*)s2, *((size_t*) n));
-}
-
-int ucx_cmp_sstr(const void *s1, const void *s2, void *data) {
- sstr_t a = *(const sstr_t*) s1;
- sstr_t b = *(const sstr_t*) s2;
- return sstrcmp(a, b);
-}
-
-int ucx_cmp_int(const void *i1, const void *i2, void *data) {
- int a = *((const int*) i1);
- int b = *((const int*) i2);
- if (a == b) {
- return 0;
- } else {
- return a < b ? -1 : 1;
- }
-}
-
-int ucx_cmp_longint(const void *i1, const void *i2, void *data) {
- long int a = *((const long int*) i1);
- long int b = *((const long int*) i2);
- if (a == b) {
- return 0;
- } else {
- return a < b ? -1 : 1;
- }
-}
-
-int ucx_cmp_longlong(const void *i1, const void *i2, void *data) {
- long long a = *((const long long*) i1);
- long long b = *((const long long*) i2);
- if (a == b) {
- return 0;
- } else {
- return a < b ? -1 : 1;
- }
-}
-int ucx_cmp_int16(const void *i1, const void *i2, void *data) {
- int16_t a = *((const int16_t*) i1);
- int16_t b = *((const int16_t*) i2);
- if (a == b) {
- return 0;
- } else {
- return a < b ? -1 : 1;
- }
-}
-
-int ucx_cmp_int32(const void *i1, const void *i2, void *data) {
- int32_t a = *((const int32_t*) i1);
- int32_t b = *((const int32_t*) i2);
- if (a == b) {
- return 0;
- } else {
- return a < b ? -1 : 1;
- }
-}
-
-int ucx_cmp_int64(const void *i1, const void *i2, void *data) {
- int64_t a = *((const int64_t*) i1);
- int64_t b = *((const int64_t*) i2);
- if (a == b) {
- return 0;
- } else {
- return a < b ? -1 : 1;
- }
-}
-
-int ucx_cmp_uint(const void *i1, const void *i2, void *data) {
- unsigned int a = *((const unsigned int*) i1);
- unsigned int b = *((const unsigned int*) i2);
- if (a == b) {
- return 0;
- } else {
- return a < b ? -1 : 1;
- }
-}
-
-int ucx_cmp_ulongint(const void *i1, const void *i2, void *data) {
- unsigned long int a = *((const unsigned long int*) i1);
- unsigned long int b = *((const unsigned long int*) i2);
- if (a == b) {
- return 0;
- } else {
- return a < b ? -1 : 1;
- }
-}
-
-int ucx_cmp_ulonglong(const void *i1, const void *i2, void *data) {
- unsigned long long a = *((const unsigned long long*) i1);
- unsigned long long b = *((const unsigned long long*) i2);
- if (a == b) {
- return 0;
- } else {
- return a < b ? -1 : 1;
- }
-}
-
-int ucx_cmp_uint16(const void *i1, const void *i2, void *data) {
- uint16_t a = *((const uint16_t*) i1);
- uint16_t b = *((const uint16_t*) i2);
- if (a == b) {
- return 0;
- } else {
- return a < b ? -1 : 1;
- }
-}
-
-int ucx_cmp_uint32(const void *i1, const void *i2, void *data) {
- uint32_t a = *((const uint32_t*) i1);
- uint32_t b = *((const uint32_t*) i2);
- if (a == b) {
- return 0;
- } else {
- return a < b ? -1 : 1;
- }
-}
-
-int ucx_cmp_uint64(const void *i1, const void *i2, void *data) {
- uint64_t a = *((const uint64_t*) i1);
- uint64_t b = *((const uint64_t*) i2);
- if (a == b) {
- return 0;
- } else {
- return a < b ? -1 : 1;
- }
-}
-
-intmax_t ucx_dist_int(const void *i1, const void *i2, void *data) {
- intmax_t a = *((const int*) i1);
- intmax_t b = *((const int*) i2);
- return a - b;
-}
-
-intmax_t ucx_dist_longint(const void *i1, const void *i2, void *data) {
- intmax_t a = *((const long int*) i1);
- intmax_t b = *((const long int*) i2);
- return a - b;
-}
-
-intmax_t ucx_dist_longlong(const void *i1, const void *i2, void *data) {
- intmax_t a = *((const long long*) i1);
- intmax_t b = *((const long long*) i2);
- return a - b;
-}
-
-intmax_t ucx_dist_int16(const void *i1, const void *i2, void *data) {
- intmax_t a = *((const int16_t*) i1);
- intmax_t b = *((const int16_t*) i2);
- return a - b;
-}
-
-intmax_t ucx_dist_int32(const void *i1, const void *i2, void *data) {
- intmax_t a = *((const int32_t*) i1);
- intmax_t b = *((const int32_t*) i2);
- return a - b;
-}
-
-intmax_t ucx_dist_int64(const void *i1, const void *i2, void *data) {
- intmax_t a = *((const int64_t*) i1);
- intmax_t b = *((const int64_t*) i2);
- return a - b;
-}
-
-intmax_t ucx_dist_uint(const void *i1, const void *i2, void *data) {
- uintmax_t a = *((const unsigned int*) i1);
- uintmax_t b = *((const unsigned int*) i2);
- return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a);
-}
-
-intmax_t ucx_dist_ulongint(const void *i1, const void *i2, void *data) {
- uintmax_t a = *((const unsigned long int*) i1);
- uintmax_t b = *((const unsigned long int*) i2);
- return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a);
-}
-
-intmax_t ucx_dist_ulonglong(const void *i1, const void *i2, void *data) {
- uintmax_t a = *((const unsigned long long*) i1);
- uintmax_t b = *((const unsigned long long*) i2);
- return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a);
-}
-
-intmax_t ucx_dist_uint16(const void *i1, const void *i2, void *data) {
- uintmax_t a = *((const uint16_t*) i1);
- uintmax_t b = *((const uint16_t*) i2);
- return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a);
-}
-
-intmax_t ucx_dist_uint32(const void *i1, const void *i2, void *data) {
- uintmax_t a = *((const uint32_t*) i1);
- uintmax_t b = *((const uint32_t*) i2);
- return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a);
-}
-
-intmax_t ucx_dist_uint64(const void *i1, const void *i2, void *data) {
- uintmax_t a = *((const uint64_t*) i1);
- uintmax_t b = *((const uint64_t*) i2);
- return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a);
-}
-
-int ucx_cmp_float(const void *f1, const void *f2, void *epsilon) {
- float a = *((const float*) f1);
- float b = *((const float*) f2);
- float e = !epsilon ? 1e-6f : *((float*)epsilon);
- if (fabsf(a - b) < e) {
- return 0;
- } else {
- return a < b ? -1 : 1;
- }
-}
-
-int ucx_cmp_double(const void *d1, const void *d2, void *epsilon) {
- double a = *((const double*) d1);
- double b = *((const double*) d2);
- double e = !epsilon ? 1e-14 : *((double*)epsilon);
- if (fabs(a - b) < e) {
- return 0;
- } else {
- return a < b ? -1 : 1;
- }
-}
-
-int ucx_cmp_ptr(const void *ptr1, const void *ptr2, void *data) {
- const intptr_t p1 = (const intptr_t) ptr1;
- const intptr_t p2 = (const intptr_t) ptr2;
- if (p1 == p2) {
- return 0;
- } else {
- return p1 < p2 ? -1 : 1;
- }
-}
-
-int ucx_cmp_mem(const void *ptr1, const void *ptr2, void *n) {
- return memcmp(ptr1, ptr2, *((size_t*)n));
-}
-
-/* PRINTF FUNCTIONS */
-
-#ifdef va_copy
-#define UCX_PRINTF_BUFSIZE 256
-#else
-#pragma message("WARNING: C99 va_copy macro not supported by this platform" \
- " - limiting ucx_*printf to 2 KiB")
-#define UCX_PRINTF_BUFSIZE 0x800
-#endif
-
-int ucx_fprintf(void *stream, write_func wfc, const char *fmt, ...) {
- int ret;
- va_list ap;
- va_start(ap, fmt);
- ret = ucx_vfprintf(stream, wfc, fmt, ap);
- va_end(ap);
- return ret;
-}
-
-int ucx_vfprintf(void *stream, write_func wfc, const char *fmt, va_list ap) {
- char buf[UCX_PRINTF_BUFSIZE];
-#ifdef va_copy
- va_list ap2;
- va_copy(ap2, ap);
- int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap);
- if (ret < 0) {
- return ret;
- } else if (ret < UCX_PRINTF_BUFSIZE) {
- return (int)wfc(buf, 1, ret, stream);
- } else {
- if (ret == INT_MAX) {
- errno = ENOMEM;
- return -1;
- }
-
- int len = ret + 1;
- char *newbuf = (char*)malloc(len);
- if (!newbuf) {
- return -1;
- }
-
- ret = vsnprintf(newbuf, len, fmt, ap2);
- if (ret > 0) {
- ret = (int)wfc(newbuf, 1, ret, stream);
- }
- free(newbuf);
- }
- return ret;
-#else
- int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap);
- if (ret < 0) {
- return ret;
- } else if (ret < UCX_PRINTF_BUFSIZE) {
- return (int)wfc(buf, 1, ret, stream);
- } else {
- errno = ENOMEM;
- return -1;
- }
-#endif
+ return ncp;
}
-sstr_t ucx_asprintf(UcxAllocator *allocator, const char *fmt, ...) {
- va_list ap;
- sstr_t ret;
- va_start(ap, fmt);
- ret = ucx_vasprintf(allocator, fmt, ap);
- va_end(ap);
- return ret;
+size_t cx_stream_ncopy(
+ void *src,
+ void *dest,
+ cx_read_func rfnc,
+ cx_write_func wfnc,
+ size_t n
+) {
+ char buf[CX_STREAM_COPY_BUF_SIZE];
+ return cx_stream_bncopy(src, dest, rfnc, wfnc,
+ buf, CX_STREAM_COPY_BUF_SIZE, n);
}
-sstr_t ucx_vasprintf(UcxAllocator *a, const char *fmt, va_list ap) {
- sstr_t s;
- s.ptr = NULL;
- s.length = 0;
- char buf[UCX_PRINTF_BUFSIZE];
-#ifdef va_copy
- va_list ap2;
- va_copy(ap2, ap);
- int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap);
- if (ret > 0 && ret < UCX_PRINTF_BUFSIZE) {
- s.ptr = (char*)almalloc(a, ret + 1);
- if (s.ptr) {
- s.length = (size_t)ret;
- memcpy(s.ptr, buf, ret);
- s.ptr[s.length] = '\0';
- }
- } else if (ret == INT_MAX) {
- errno = ENOMEM;
- } else {
- int len = ret + 1;
- s.ptr = (char*)almalloc(a, len);
- if (s.ptr) {
- ret = vsnprintf(s.ptr, len, fmt, ap2);
- if (ret < 0) {
- free(s.ptr);
- s.ptr = NULL;
- } else {
- s.length = (size_t)ret;
- }
- }
- }
-#else
- int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap);
- if (ret > 0 && ret < UCX_PRINTF_BUFSIZE) {
- s.ptr = (char*)almalloc(a, ret + 1);
- if (s.ptr) {
- s.length = (size_t)ret;
- memcpy(s.ptr, buf, ret);
- s.ptr[s.length] = '\0';
- }
- } else {
- errno = ENOMEM;
- }
+#ifndef CX_SZMUL_BUILTIN
+#include "szmul.c"
#endif
- return s;
-}