# HG changeset patch # User Olaf Wintermann # Date 1684765046 -7200 # Node ID 0358f1d9c5064c45ff6521ec3e35a8b76188f639 # Parent 809581724cc71dfa31b66766761e04a10b0afffe upgrade ucx diff -r 809581724cc7 -r 0358f1d9c506 application/Makefile --- a/application/Makefile Sat Apr 15 21:06:45 2023 +0200 +++ b/application/Makefile Mon May 22 16:17:26 2023 +0200 @@ -26,7 +26,7 @@ # POSSIBILITY OF SUCH DAMAGE. # -BUILD_ROOT = ../ +BUILD_ROOT = .. include ../config.mk CFLAGS += -I../ui/ -I../ucx @@ -41,5 +41,5 @@ $(LD) -o ../build/bin/mk12$(APP_EXT) $(OBJ) -L$(BUILD_ROOT)/build/lib -luitk -lucx $(LDFLAGS) $(TK_LDFLAGS) ../build/application/%.$(OBJ_EXT): %.c - $(CC) $(CFLAGS) $(TK_CFLAGS) -o $@ -c $< + $(CC) ../ucx $(CFLAGS) $(TK_CFLAGS) -o $@ -c $< diff -r 809581724cc7 -r 0358f1d9c506 application/main.c --- a/application/main.c Sat Apr 15 21:06:45 2023 +0200 +++ b/application/main.c Mon May 22 16:17:26 2023 +0200 @@ -30,8 +30,8 @@ #include #include -#include -#include +#include +#include typedef struct { UiText *text; diff -r 809581724cc7 -r 0358f1d9c506 configure --- a/configure Sat Apr 15 21:06:45 2023 +0200 +++ b/configure Mon May 22 16:17:26 2023 +0200 @@ -465,8 +465,8 @@ do cat >> $TEMP_DIR/make.mk << __EOF__ -OBJ_EXT = o -LIB_EXT = a +OBJ_EXT = .o +LIB_EXT = .a PACKAGE_SCRIPT = package_osx.sh __EOF__ @@ -488,8 +488,8 @@ do cat >> $TEMP_DIR/make.mk << __EOF__ -OBJ_EXT = o -LIB_EXT = a +OBJ_EXT = .o +LIB_EXT = .a PACKAGE_SCRIPT = package_unix.sh __EOF__ diff -r 809581724cc7 -r 0358f1d9c506 make/Makefile.mk --- a/make/Makefile.mk Sat Apr 15 21:06:45 2023 +0200 +++ b/make/Makefile.mk Mon May 22 16:17:26 2023 +0200 @@ -32,7 +32,7 @@ include config.mk BUILD_DIRS = build/bin build/lib -BUILD_DIRS += build/application +BUILD_DIRS += build/application build/ucx BUILD_DIRS += build/ui/common build/ui/$(TOOLKIT) all: $(BUILD_DIRS) ucx ui application @@ -41,9 +41,12 @@ $(BUILD_DIRS): mkdir -p $@ -ui: FORCE +ui: ucx FORCE cd ui; $(MAKE) all +ucx: FORCE + cd ucx; $(MAKE) all + application: ui FORCE cd application; $(MAKE) diff -r 809581724cc7 -r 0358f1d9c506 make/project.xml --- a/make/project.xml Sat Apr 15 21:06:45 2023 +0200 +++ b/make/project.xml Mon May 22 16:17:26 2023 +0200 @@ -46,13 +46,13 @@ - OBJ_EXT = o - LIB_EXT = a + OBJ_EXT = .o + LIB_EXT = .a PACKAGE_SCRIPT = package_osx.sh - OBJ_EXT = o - LIB_EXT = a + OBJ_EXT = .o + LIB_EXT = .a PACKAGE_SCRIPT = package_unix.sh diff -r 809581724cc7 -r 0358f1d9c506 ucx/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/Makefile Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,61 @@ +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. +# +# Copyright 2013 Olaf Wintermann. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +BUILD_ROOT = ../ +include ../config.mk + +# list of source files +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 += printf.c +SRC += string.c +SRC += utils.c + +OBJ = $(SRC:%.c=../build/ucx/%$(OBJ_EXT)) + +UCX_LIB = ../build/lib/libucx$(LIB_EXT) + +all: ../build/ucx $(UCX_LIB) + +$(UCX_LIB): $(OBJ) + $(AR) $(ARFLAGS) $(UCX_LIB) $(OBJ) + +../build/ucx: + mkdir -p ../build/ucx + +../build/ucx/%$(OBJ_EXT): %.c + $(CC) $(CFLAGS) -o $@ -c $< + diff -r 809581724cc7 -r 0358f1d9c506 ucx/allocator.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/allocator.c Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,122 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cx/allocator.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); +} + +__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 + +void *cxMalloc( + CxAllocator const *allocator, + size_t n +) { + return allocator->cl->malloc(allocator->data, n); +} + +void *cxRealloc( + CxAllocator const *allocator, + void *mem, + size_t n +) { + return allocator->cl->realloc(allocator->data, mem, n); +} + +int cxReallocate( + CxAllocator const *allocator, + void **mem, + size_t n +) { + void *nmem = allocator->cl->realloc(allocator->data, *mem, n); + if (nmem == NULL) { + return 1; + } else { + *mem = nmem; + return 0; + } +} + +void *cxCalloc( + CxAllocator const *allocator, + size_t nelem, + size_t n +) { + return allocator->cl->calloc(allocator->data, nelem, n); +} + +void cxFree( + CxAllocator const *allocator, + void *mem +) { + allocator->cl->free(allocator->data, mem); +} diff -r 809581724cc7 -r 0358f1d9c506 ucx/array_list.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/array_list.c Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,547 @@ +/* + * 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 +#include + +// 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; + size_t capacity; + 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; + + char *ptr = arl->data; + + if (list->simple_destructor) { + for (size_t i = 0; i < list->size; i++) { + cx_invoke_simple_destructor(list, ptr); + ptr += list->item_size; + } + } + if (list->advanced_destructor) { + for (size_t i = 0; i < list->size; i++) { + cx_invoke_advanced_destructor(list, ptr); + ptr += list->item_size; + } + } + + cxFree(list->allocator, arl->data); + cxFree(list->allocator, list); +} + +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->item_size; + 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, + &arl->capacity, + start_of_moved, + first_to_move, + list->item_size, + 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, + &arl->capacity, + index, + array, + list->item_size, + 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->item_size; + } + 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 + cx_invoke_destructor(list, ((char *) arl->data) + index * list->item_size); + + // 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, + &arl->capacity, + index, + ((char *) arl->data) + (index + 1) * list->item_size, + list->item_size, + 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; + + if (list->simple_destructor) { + for (size_t i = 0; i < list->size; i++) { + cx_invoke_simple_destructor(list, ptr); + ptr += list->item_size; + } + } + if (list->advanced_destructor) { + for (size_t i = 0; i < list->size; i++) { + cx_invoke_advanced_destructor(list, ptr); + ptr += list->item_size; + } + } + + memset(arl->data, 0, list->size * list->item_size); + 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->item_size, 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->item_size; + } else { + return NULL; + } +} + +static ssize_t cx_arl_find( + struct cx_list_s const *list, + void const *elem +) { + assert(list->cmpfunc != NULL); + assert(list->size < SIZE_MAX / 2); + char *cur = ((cx_array_list const *) list)->data; + + for (ssize_t i = 0; i < (ssize_t) list->size; i++) { + if (0 == list->cmpfunc(elem, cur)) { + return i; + } + cur += list->item_size; + } + + return -1; +} + +static void cx_arl_sort(struct cx_list_s *list) { + assert(list->cmpfunc != NULL); + qsort(((cx_array_list *) list)->data, + list->size, + list->item_size, + 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->item_size; + right += other->item_size; + } + 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->item_size, 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)->item_size; + } +} + +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.item_size; + } +} + +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, + cx_compare_func 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->base.cl = &cx_array_list_class; + list->base.allocator = allocator; + list->base.cmpfunc = comparator; + list->capacity = initial_capacity; + + if (item_size > 0) { + list->base.item_size = item_size; + } else { + item_size = sizeof(void *); + cxListStorePointers((CxList *) list); + } + + // allocate the array after the real item_size is known + list->data = cxCalloc(allocator, initial_capacity, item_size); + if (list->data == NULL) { + cxFree(allocator, list); + return NULL; + } + + // configure the reallocator + list->reallocator.realloc = cx_arl_realloc; + list->reallocator.ptr1 = (void *) allocator; + + return (CxList *) list; +} diff -r 809581724cc7 -r 0358f1d9c506 ucx/basic_mempool.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/basic_mempool.c Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,235 @@ +/* + * 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 + +#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; +} diff -r 809581724cc7 -r 0358f1d9c506 ucx/buffer.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/buffer.c Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,415 @@ +/* + * 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/buffer.h" +#include "cx/utils.h" + +#include +#include + +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->flags |= CX_BUFFER_FREE_CONTENTS; + } else { + buffer->bytes = space; + } + buffer->capacity = capacity; + buffer->size = 0; + buffer->pos = 0; + + buffer->flush_func = NULL; + buffer->flush_target = NULL; + buffer->flush_blkmax = 0; + buffer->flush_blksize = 4096; + buffer->flush_threshold = SIZE_MAX; + + return 0; +} + +void cxBufferDestroy(CxBuffer *buffer) { + if ((buffer->flags & CX_BUFFER_FREE_CONTENTS) == CX_BUFFER_FREE_CONTENTS) { + cxFree(buffer->allocator, buffer->bytes); + } +} + +CxBuffer *cxBufferCreate( + void *space, + size_t capacity, + CxAllocator const *allocator, + int flags +) { + CxBuffer *buf = cxMalloc(allocator, sizeof(CxBuffer)); + if (buf == NULL) return NULL; + if (0 == cxBufferInit(buf, space, capacity, allocator, flags)) { + return buf; + } else { + cxFree(allocator, buf); + return NULL; + } +} + +void cxBufferFree(CxBuffer *buffer) { + if ((buffer->flags & CX_BUFFER_FREE_CONTENTS) == CX_BUFFER_FREE_CONTENTS) { + cxFree(buffer->allocator, buffer->bytes); + } + cxFree(buffer->allocator, buffer); +} + +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; + } + + size_t opos = npos; + npos += offset; + + if ((offset > 0 && npos < opos) || (offset < 0 && npos > opos)) { + return -1; + } + + if (npos >= buffer->size) { + return -1; + } else { + buffer->pos = npos; + return 0; + } + +} + +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 cxBufferMinimumCapacity( + CxBuffer *buffer, + size_t newcap +) { + if (newcap <= buffer->capacity) { + return 0; + } + + if (cxReallocate(buffer->allocator, + (void **) &buffer->bytes, newcap) == 0) { + buffer->capacity = newcap; + return 0; + } else { + return -1; + } +} + +/** + * 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; + 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 & 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 { + 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; + } + + 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; + } + +} + +int cxBufferPut( + CxBuffer *buffer, + int c +) { + c &= 0xFF; + unsigned char const ch = c; + if (cxBufferWrite(&ch, 1, 1, buffer) == 1) { + return c; + } else { + return EOF; + } +} + +size_t cxBufferPutString( + CxBuffer *buffer, + const char *str +) { + return cxBufferWrite(str, 1, strlen(str), buffer); +} + +size_t cxBufferRead( + void *ptr, + size_t size, + size_t nitems, + CxBuffer *buffer +) { + size_t len; + if (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 (len <= 0) { + return len; + } + + memcpy(ptr, buffer->bytes + buffer->pos, len); + buffer->pos += len; + + return len / size; +} + +int cxBufferGet(CxBuffer *buffer) { + if (cxBufferEof(buffer)) { + return EOF; + } else { + int c = buffer->bytes[buffer->pos]; + buffer->pos++; + return c; + } +} + +int cxBufferShiftLeft( + CxBuffer *buffer, + size_t shift +) { + if (shift >= buffer->size) { + buffer->pos = buffer->size = 0; + } else { + memmove(buffer->bytes, buffer->bytes + shift, buffer->size - shift); + buffer->size -= shift; + + if (buffer->pos >= shift) { + buffer->pos -= shift; + } else { + buffer->pos = 0; + } + } + return 0; +} + +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 & CX_BUFFER_AUTO_EXTEND) == CX_BUFFER_AUTO_EXTEND) { + if (cxBufferMinimumCapacity(buffer, req_capacity)) { + return 1; + } + movebytes = buffer->size; + } else { + movebytes = buffer->capacity - shift; + } + } else { + movebytes = buffer->size; + } + + 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 cxBufferShift( + CxBuffer *buffer, + off_t shift +) { + if (shift < 0) { + return cxBufferShiftLeft(buffer, (size_t) (-shift)); + } else if (shift > 0) { + return cxBufferShiftRight(buffer, (size_t) shift); + } else { + return 0; + } +} diff -r 809581724cc7 -r 0358f1d9c506 ucx/compare.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/compare.c Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,201 @@ +/* + * 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 + +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; + } +} + diff -r 809581724cc7 -r 0358f1d9c506 ucx/cx/allocator.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/allocator.h Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,225 @@ +/* + * 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))); + +/** + * 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 diff -r 809581724cc7 -r 0358f1d9c506 ucx/cx/array_list.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/array_list.h Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,194 @@ +/* + * 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, + cx_compare_func 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 diff -r 809581724cc7 -r 0358f1d9c506 ucx/cx/basic_mempool.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/basic_mempool.h Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,76 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * \file basic_mempool.h + * \brief Implementation of a basic memory pool. + * \author Mike Becker + * \author Olaf Wintermann + * \version 3.0 + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_BASIC_MEMPOOL_H +#define UCX_BASIC_MEMPOOL_H + +#include "mempool.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Basic array-based memory pool. + */ +struct cx_basic_mempool_s { + /** Inherit base structure members. */ + CxMempool base; + + /** List of pointers to pooled memory. */ + void **data; + + /** Number of pooled memory items. */ + size_t ndata; + + /** Memory pool size. */ + size_t size; +}; + +/** + * Creates a basic array-based memory pool. + * + * @param capacity the initial capacity of the pool + * @return the created memory pool or \c NULL if allocation failed + */ +__attribute__((__warn_unused_result__)) +CxMempool *cxBasicMempoolCreate(size_t capacity); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // UCX_BASIC_MEMPOOL_H diff -r 809581724cc7 -r 0358f1d9c506 ucx/cx/buffer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/buffer.h Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,451 @@ +/* + * 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 +); + +/** + * Allocates and 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 space pointer to the memory area, or \c NULL to allocate + * new memory + * @param capacity the capacity of the buffer + * @param allocator the allocator to use for allocating the structure and the automatic + * memory management within the buffer. If \c NULL, the default heap allocator will be used. + * @param flags buffer features (see cx_buffer_s.flags) + * @return a pointer to the buffer on success, \c NULL if a required allocation failed + */ +CxBuffer *cxBufferCreate( + 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. + * If you want to free the memory of the entire buffer, use cxBufferFree(). + * + * @param buffer the buffer which contents shall be destroyed + * @see cxBufferInit() + */ +__attribute__((__nonnull__)) +void cxBufferDestroy(CxBuffer *buffer); + +/** + * Deallocates the buffer. + * + * If the #CX_BUFFER_FREE_CONTENTS feature is enabled, this function also destroys + * the contents. If you \em only want to destroy the contents, use cxBufferDestroy(). + * + * @param buffer the buffer to deallocate + * @see cxBufferCreate() + */ +__attribute__((__nonnull__)) +void cxBufferFree(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 + * memset(buffer->bytes, 0, shift) for a right shift or + * memset(buffer->bytes + buffer->size, 0, buffer->capacity - buffer->size) + * 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 diff -r 809581724cc7 -r 0358f1d9c506 ucx/cx/collection.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/collection.h Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,138 @@ +/* + * 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: + * + * 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 collection.h + * \brief Common definitions for various collection implementations. + * \author Mike Becker + * \author Olaf Wintermann + * \version 3.0 + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_COLLECTION_H +#define UCX_COLLECTION_H + +#include "allocator.h" +#include "iterator.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Special constant used for creating collections that are storing pointers. + */ +#define CX_STORE_POINTERS 0 + +/** + * A comparator function comparing two collection elements. + */ +typedef int(*cx_compare_func)( + void const *left, + void const *right +); + +/** + * Use this macro to declare common members for a collection structure. + */ +#define CX_COLLECTION_MEMBERS \ + /** \ + * The allocator to use. \ + */ \ + CxAllocator const *allocator; \ + /** \ + * The comparator function for the elements. \ + */ \ + cx_compare_func cmpfunc; \ + /** \ + * The size of each element. \ + */ \ + size_t item_size; \ + /** \ + * The number of currently stored elements. \ + */ \ + size_t size; \ + /** \ + * An optional simple destructor for the collection's elements. \ + * \ + * @attention Read the documentation of the particular collection 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 collection's elements. \ + * \ + * @attention Read the documentation of the particular collection implementation \ + * whether this destructor shall only destroy the contents or also free the memory. \ + */ \ + cx_destructor_func2 advanced_destructor; \ + /** \ + * The pointer to additional data that is passed to the advanced destructor. \ + */ \ + void *destructor_data; \ + /** \ + * Indicates if this instance of a collection is supposed to store pointers \ + * instead of copies of the actual objects. \ + */ \ + bool store_pointer; + +/** + * Invokes the simple destructor function for a specific element. + * + * Usually only used by collection implementations. There should be no need + * to invoke this macro manually. + * + * @param c the collection + * @param e the element + */ +#define cx_invoke_simple_destructor(c, e) \ + (c)->simple_destructor((c)->store_pointer ? (*((void **) (e))) : (e)) + +/** + * Invokes the advanced destructor function for a specific element. + * + * Usually only used by collection implementations. There should be no need + * to invoke this macro manually. + * + * @param c the collection + * @param e the element + */ +#define cx_invoke_advanced_destructor(c, e) \ + (c)->advanced_destructor((c)->destructor_data, \ + (c)->store_pointer ? (*((void **) (e))) : (e)) + + +#define cx_invoke_destructor(c, e) \ + if ((c)->simple_destructor) cx_invoke_simple_destructor(c,e); \ + if ((c)->advanced_destructor) cx_invoke_advanced_destructor(c,e) + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // UCX_COLLECTION_H diff -r 809581724cc7 -r 0358f1d9c506 ucx/cx/common.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/common.h Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,141 @@ +/* + * 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. + *

+ * Latest available source:
+ * https://sourceforge.net/projects/ucx/files/ + *

+ * + *

+ * Repositories:
+ * https://sourceforge.net/p/ucx/code + * - or - + * https://develop.uap-core.de/hg/ucx + *

+ * + *

LICENCE

+ * + * 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) + +// Common Includes + +#include +#include +#include +#include +#include + +/** + * 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 * +); + + +// Compiler specific stuff + +#ifndef __GNUC__ +/** + * Removes GNU C attributes where they are not supported. + */ +#define __attribute__(x) +#endif + +#ifdef _MSC_VER + +// fix missing ssize_t definition +#include +typedef SSIZE_T ssize_t; + +// fix missing _Thread_local support +#define _Thread_local __declspec(thread) + +#endif + +#endif // UCX_COMMON_H diff -r 809581724cc7 -r 0358f1d9c506 ucx/cx/compare.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/compare.h Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,220 @@ +/* + * 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 diff -r 809581724cc7 -r 0358f1d9c506 ucx/cx/hash_key.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/hash_key.h Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,129 @@ +/* + * 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 "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Internal structure for a key within a hash map. */ +struct cx_hash_key_s { + /** The key data. */ + void const *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 diff -r 809581724cc7 -r 0358f1d9c506 ucx/cx/hash_map.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/hash_map.h Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,134 @@ +/* + * 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 item_size 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 const *allocator, + size_t itemsize, + size_t buckets +); + +/** + * Creates a new hash map with a default number of buckets. + * + * If \p item_size 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 itemsize the size of one element + * @return a pointer to the new hash map + */ +#define cxHashMapCreateSimple(itemsize) \ + cxHashMapCreate(cxDefaultAllocator, itemsize, 0) + +/** + * 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 diff -r 809581724cc7 -r 0358f1d9c506 ucx/cx/iterator.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/iterator.h Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,263 @@ +/* + * 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. + * + * When valid returns false, the behavior of this function is undefined. + */ + __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. + * + * When valid returns false, the behavior of this function is undefined. + */ + __attribute__ ((__nonnull__)) + void (*next)(void *); + + /** + * Flag current element for removal, if possible. + * + * When valid returns false, the behavior of this function is undefined. + */ + __attribute__ ((__nonnull__)) + bool (*flag_removal)(void *); + + /** + * Indicates whether this iterator may remove elements. + */ + 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 diff -r 809581724cc7 -r 0358f1d9c506 ucx/cx/linked_list.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/linked_list.h Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,415 @@ +/* + * 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, + cx_compare_func 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 negative value if it could not be found + */ +ssize_t cx_linked_list_find( + void const *start, + ptrdiff_t loc_advance, + ptrdiff_t loc_data, + cx_compare_func 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, + cx_compare_func 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, + cx_compare_func 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 diff -r 809581724cc7 -r 0358f1d9c506 ucx/cx/list.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/list.h Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,650 @@ +/* + * 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 "collection.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * 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 { + CX_COLLECTION_MEMBERS + /** + * 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 class definition for arbitrary lists. + */ +struct cx_list_class_s { + /** + * Destructor function. + * + * Implementations SHALL invoke the content destructor functions if provided + * and SHALL deallocate the list memory, if an allocator is provided. + */ + 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. + */ + ssize_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; + +/** + * 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 true, if this list is storing pointers + * @see cxListStorePointers() + */ +__attribute__((__nonnull__)) +static inline bool cxListIsStoringPointers(CxList const *list) { + return list->store_pointer; +} + +/** + * Returns the number of elements currently stored in the list. + * + * @param list the list + * @return the number of currently stored elements + */ +__attribute__((__nonnull__)) +static inline size_t cxListSize(CxList const *list) { + return list->size; +} + +/** + * 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 a negative + * value when the element is not found + */ +__attribute__((__nonnull__)) +static inline ssize_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); + +/** + * A shared instance of an empty list. + * + * Writing to that list is undefined. + */ +extern CxList * const cxEmptyList; + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // UCX_LIST_H diff -r 809581724cc7 -r 0358f1d9c506 ucx/cx/map.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/map.h Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,1129 @@ +/* + * 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 "collection.h" +#include "string.h" +#include "hash_key.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Type for the UCX map. */ +typedef struct cx_map_s CxMap; + +/** Type for a map entry. */ +typedef struct cx_map_entry_s CxMapEntry; + +/** Type for map class definitions. */ +typedef struct cx_map_class_s cx_map_class; + +/** Structure for the UCX map. */ +struct cx_map_s { + CX_COLLECTION_MEMBERS + /** The map class definition. */ + cx_map_class *cl; +}; + +/** + * The type of iterator for a map. + */ +enum cx_map_iterator_type { + /** + * Iterates over key/value pairs. + */ + CX_MAP_ITERATOR_PAIRS, + /** + * Iterates over keys only. + */ + CX_MAP_ITERATOR_KEYS, + /** + * Iterates over values only. + */ + CX_MAP_ITERATOR_VALUES +}; + +/** + * 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, + bool destroy + ); + + /** + * Creates an iterator for this map. + */ + __attribute__((__nonnull__, __warn_unused_result__)) + CxIterator (*iterator)(CxMap const *map, enum cx_map_iterator_type type); +}; + +/** + * A map entry. + */ +struct cx_map_entry_s { + /** + * A pointer to the key. + */ + CxHashKey const *key; + /** + * A pointer to the value. + */ + void *value; +}; + +extern CxMap *const cxEmptyMap; + +/** + * 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_pointer = 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_pointer = true; + map->item_size = sizeof(void *); +} + + +/** + * Deallocates the memory of the specified map. + * + * @param map the map to be destroyed + */ +__attribute__((__nonnull__)) +static inline void cxMapDestroy(CxMap *map) { + 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); +} + + +// 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 const *map) { + return map->cl->iterator(map, CX_MAP_ITERATOR_VALUES); +} + +/** + * 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 const *map) { + return map->cl->iterator(map, CX_MAP_ITERATOR_KEYS); +} + +/** + * 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 const *map) { + return map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS); +} + + +/** + * 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__)) +CxMutIterator cxMapMutIteratorValues(CxMap *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__)) +CxMutIterator cxMapMutIteratorKeys(CxMap *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__)) +CxMutIterator cxMapMutIterator(CxMap *map); + +#ifdef __cplusplus +} // end the extern "C" block here, because we want to start overloading + +/** + * 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 const &key, + void *value +) { + return map->cl->put(map, key, value); +} + + +/** + * 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, + cxstring const &key, + void *value +) { + return map->cl->put(map, cx_hash_key_cxstr(key), value); +} + +/** + * 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, + cxmutstr const &key, + void *value +) { + return map->cl->put(map, cx_hash_key_cxstr(key), value); +} + +/** + * 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, + char const *key, + void *value +) { + return map->cl->put(map, cx_hash_key_str(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 const &key +) { + return map->cl->get(map, key); +} + +/** + * 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, + cxstring const &key +) { + return map->cl->get(map, cx_hash_key_cxstr(key)); +} + +/** + * 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, + cxmutstr const &key +) { + return map->cl->get(map, cx_hash_key_cxstr(key)); +} + +/** + * 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, + char const *key +) { + return map->cl->get(map, cx_hash_key_str(key)); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * Always invokes the destructor function, if any, on the removed element. + * If this map is storing pointers and you just want to retrieve the pointer + * without invoking the destructor, use cxMapRemoveAndGet(). + * If you just want to detach the element from the map without invoking the + * destructor or returning the element, use cxMapDetach(). + * + * @param map the map + * @param key the key + * @see cxMapRemoveAndGet() + * @see cxMapDetach() + */ +__attribute__((__nonnull__)) +static inline void cxMapRemove( + CxMap *map, + CxHashKey const &key +) { + (void) map->cl->remove(map, key, true); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * Always invokes the destructor function, if any, on the removed element. + * If this map is storing pointers and you just want to retrieve the pointer + * without invoking the destructor, use cxMapRemoveAndGet(). + * If you just want to detach the element from the map without invoking the + * destructor or returning the element, use cxMapDetach(). + * + * @param map the map + * @param key the key + * @see cxMapRemoveAndGet() + * @see cxMapDetach() + */ +__attribute__((__nonnull__)) +static inline void cxMapRemove( + CxMap *map, + cxstring const &key +) { + (void) map->cl->remove(map, cx_hash_key_cxstr(key), true); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * Always invokes the destructor function, if any, on the removed element. + * If this map is storing pointers and you just want to retrieve the pointer + * without invoking the destructor, use cxMapRemoveAndGet(). + * If you just want to detach the element from the map without invoking the + * destructor or returning the element, use cxMapDetach(). + * + * @param map the map + * @param key the key + * @see cxMapRemoveAndGet() + * @see cxMapDetach() + */ +__attribute__((__nonnull__)) +static inline void cxMapRemove( + CxMap *map, + cxmutstr const &key +) { + (void) map->cl->remove(map, cx_hash_key_cxstr(key), true); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * Always invokes the destructor function, if any, on the removed element. + * If this map is storing pointers and you just want to retrieve the pointer + * without invoking the destructor, use cxMapRemoveAndGet(). + * If you just want to detach the element from the map without invoking the + * destructor or returning the element, use cxMapDetach(). + * + * @param map the map + * @param key the key + * @see cxMapRemoveAndGet() + * @see cxMapDetach() + */ +__attribute__((__nonnull__)) +static inline void cxMapRemove( + CxMap *map, + char const *key +) { + (void) map->cl->remove(map, cx_hash_key_str(key), true); +} + +/** + * Detaches a key/value-pair from the map by using the key + * without invoking the destructor. + * + * In general, you should only use this function if the map does not own + * the data and there is a valid reference to the data somewhere else + * in the program. In all other cases it is preferable to use + * cxMapRemove() or cxMapRemoveAndGet(). + * + * @param map the map + * @param key the key + * @see cxMapRemove() + * @see cxMapRemoveAndGet() + */ +__attribute__((__nonnull__)) +static inline void cxMapDetach( + CxMap *map, + CxHashKey const &key +) { + (void) map->cl->remove(map, key, false); +} + +/** + * Detaches a key/value-pair from the map by using the key + * without invoking the destructor. + * + * In general, you should only use this function if the map does not own + * the data and there is a valid reference to the data somewhere else + * in the program. In all other cases it is preferable to use + * cxMapRemove() or cxMapRemoveAndGet(). + * + * @param map the map + * @param key the key + * @see cxMapRemove() + * @see cxMapRemoveAndGet() + */ +__attribute__((__nonnull__)) +static inline void cxMapDetach( + CxMap *map, + cxstring const &key +) { + (void) map->cl->remove(map, cx_hash_key_cxstr(key), false); +} + +/** + * Detaches a key/value-pair from the map by using the key + * without invoking the destructor. + * + * In general, you should only use this function if the map does not own + * the data and there is a valid reference to the data somewhere else + * in the program. In all other cases it is preferable to use + * cxMapRemove() or cxMapRemoveAndGet(). + * + * @param map the map + * @param key the key + * @see cxMapRemove() + * @see cxMapRemoveAndGet() + */ +__attribute__((__nonnull__)) +static inline void cxMapDetach( + CxMap *map, + cxmutstr const &key +) { + (void) map->cl->remove(map, cx_hash_key_cxstr(key), false); +} + +/** + * Detaches a key/value-pair from the map by using the key + * without invoking the destructor. + * + * In general, you should only use this function if the map does not own + * the data and there is a valid reference to the data somewhere else + * in the program. In all other cases it is preferable to use + * cxMapRemove() or cxMapRemoveAndGet(). + * + * @param map the map + * @param key the key + * @see cxMapRemove() + * @see cxMapRemoveAndGet() + */ +__attribute__((__nonnull__)) +static inline void cxMapDetach( + CxMap *map, + char const *key +) { + (void) map->cl->remove(map, cx_hash_key_str(key), false); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * This function can be used when the map is storing pointers, + * in order to retrieve the pointer from the map without invoking + * any destructor function. Sometimes you do not want the pointer + * to be returned - in that case (instead of suppressing the "unused + * result" warning) you can use cxMapDetach(). + * + * If this map is not storing pointers, this function behaves like + * cxMapRemove() and returns \c NULL. + * + * @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() + * @see cxMapDetach() + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cxMapRemoveAndGet( + CxMap *map, + CxHashKey key +) { + return map->cl->remove(map, key, !map->store_pointer); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * This function can be used when the map is storing pointers, + * in order to retrieve the pointer from the map without invoking + * any destructor function. Sometimes you do not want the pointer + * to be returned - in that case (instead of suppressing the "unused + * result" warning) you can use cxMapDetach(). + * + * If this map is not storing pointers, this function behaves like + * cxMapRemove() and returns \c NULL. + * + * @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() + * @see cxMapDetach() + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cxMapRemoveAndGet( + CxMap *map, + cxstring key +) { + return map->cl->remove(map, cx_hash_key_cxstr(key), !map->store_pointer); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * This function can be used when the map is storing pointers, + * in order to retrieve the pointer from the map without invoking + * any destructor function. Sometimes you do not want the pointer + * to be returned - in that case (instead of suppressing the "unused + * result" warning) you can use cxMapDetach(). + * + * If this map is not storing pointers, this function behaves like + * cxMapRemove() and returns \c NULL. + * + * @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() + * @see cxMapDetach() + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cxMapRemoveAndGet( + CxMap *map, + cxmutstr key +) { + return map->cl->remove(map, cx_hash_key_cxstr(key), !map->store_pointer); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * This function can be used when the map is storing pointers, + * in order to retrieve the pointer from the map without invoking + * any destructor function. Sometimes you do not want the pointer + * to be returned - in that case (instead of suppressing the "unused + * result" warning) you can use cxMapDetach(). + * + * If this map is not storing pointers, this function behaves like + * cxMapRemove() and returns \c NULL. + * + * @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() + * @see cxMapDetach() + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cxMapRemoveAndGet( + CxMap *map, + char const *key +) { + return map->cl->remove(map, cx_hash_key_str(key), !map->store_pointer); +} + +#else // __cplusplus + +/** + * 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 cx_map_put( + CxMap *map, + CxHashKey key, + void *value +) { + return map->cl->put(map, key, value); +} + +/** + * 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 cx_map_put_cxstr( + CxMap *map, + cxstring key, + void *value +) { + return map->cl->put(map, cx_hash_key_cxstr(key), value); +} + +/** + * 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 cx_map_put_mustr( + CxMap *map, + cxmutstr key, + void *value +) { + return map->cl->put(map, cx_hash_key_cxstr(key), value); +} + +/** + * 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 cx_map_put_str( + CxMap *map, + char const *key, + void *value +) { + return map->cl->put(map, cx_hash_key_str(key), value); +} + +/** + * 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 + */ +#define cxMapPut(map, key, value) _Generic((key), \ + CxHashKey: cx_map_put, \ + cxstring: cx_map_put_cxstr, \ + cxmutstr: cx_map_put_mustr, \ + char*: cx_map_put_str, \ + char const*: cx_map_put_str) \ + (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 *cx_map_get( + CxMap const *map, + CxHashKey key +) { + return map->cl->get(map, key); +} + +/** + * 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 *cx_map_get_cxstr( + CxMap const *map, + cxstring key +) { + return map->cl->get(map, cx_hash_key_cxstr(key)); +} + +/** + * 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 *cx_map_get_mustr( + CxMap const *map, + cxmutstr key +) { + return map->cl->get(map, cx_hash_key_cxstr(key)); +} + +/** + * 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 *cx_map_get_str( + CxMap const *map, + char const *key +) { + return map->cl->get(map, cx_hash_key_str(key)); +} + +/** + * Retrieves a value by using a key. + * + * @param map the map + * @param key the key + * @return the value + */ +#define cxMapGet(map, key) _Generic((key), \ + CxHashKey: cx_map_get, \ + cxstring: cx_map_get_cxstr, \ + cxmutstr: cx_map_get_mustr, \ + char*: cx_map_get_str, \ + char const*: cx_map_get_str) \ + (map, key) + +/** + * Removes a key/value-pair from the map by using the key. + * + * @param map the map + * @param key the key + */ +__attribute__((__nonnull__)) +static inline void cx_map_remove( + CxMap *map, + CxHashKey key +) { + (void) map->cl->remove(map, key, true); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * @param map the map + * @param key the key + */ +__attribute__((__nonnull__)) +static inline void cx_map_remove_cxstr( + CxMap *map, + cxstring key +) { + (void) map->cl->remove(map, cx_hash_key_cxstr(key), true); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * @param map the map + * @param key the key + */ +__attribute__((__nonnull__)) +static inline void cx_map_remove_mustr( + CxMap *map, + cxmutstr key +) { + (void) map->cl->remove(map, cx_hash_key_cxstr(key), true); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * @param map the map + * @param key the key + */ +__attribute__((__nonnull__)) +static inline void cx_map_remove_str( + CxMap *map, + char const *key +) { + (void) map->cl->remove(map, cx_hash_key_str(key), true); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * Always invokes the destructor function, if any, on the removed element. + * If this map is storing pointers and you just want to retrieve the pointer + * without invoking the destructor, use cxMapRemoveAndGet(). + * If you just want to detach the element from the map without invoking the + * destructor or returning the element, use cxMapDetach(). + * + * @param map the map + * @param key the key + * @see cxMapRemoveAndGet() + * @see cxMapDetach() + */ +#define cxMapRemove(map, key) _Generic((key), \ + CxHashKey: cx_map_remove, \ + cxstring: cx_map_remove_cxstr, \ + cxmutstr: cx_map_remove_mustr, \ + char*: cx_map_remove_str, \ + char const*: cx_map_remove_str) \ + (map, key) + +/** + * Detaches a key/value-pair from the map by using the key + * without invoking the destructor. + * + * @param map the map + * @param key the key + */ +__attribute__((__nonnull__)) +static inline void cx_map_detach( + CxMap *map, + CxHashKey key +) { + (void) map->cl->remove(map, key, false); +} + +/** + * Detaches a key/value-pair from the map by using the key + * without invoking the destructor. + * + * @param map the map + * @param key the key + */ +__attribute__((__nonnull__)) +static inline void cx_map_detach_cxstr( + CxMap *map, + cxstring key +) { + (void) map->cl->remove(map, cx_hash_key_cxstr(key), false); +} + +/** + * Detaches a key/value-pair from the map by using the key + * without invoking the destructor. + * + * @param map the map + * @param key the key + */ +__attribute__((__nonnull__)) +static inline void cx_map_detach_mustr( + CxMap *map, + cxmutstr key +) { + (void) map->cl->remove(map, cx_hash_key_cxstr(key), false); +} + +/** + * Detaches a key/value-pair from the map by using the key + * without invoking the destructor. + * + * @param map the map + * @param key the key + */ +__attribute__((__nonnull__)) +static inline void cx_map_detach_str( + CxMap *map, + char const *key +) { + (void) map->cl->remove(map, cx_hash_key_str(key), false); +} + +/** + * Detaches a key/value-pair from the map by using the key + * without invoking the destructor. + * + * In general, you should only use this function if the map does not own + * the data and there is a valid reference to the data somewhere else + * in the program. In all other cases it is preferable to use + * cxMapRemove() or cxMapRemoveAndGet(). + * + * @param map the map + * @param key the key + * @see cxMapRemove() + * @see cxMapRemoveAndGet() + */ +#define cxMapDetach(map, key) _Generic((key), \ + CxHashKey: cx_map_detach, \ + cxstring: cx_map_detach_cxstr, \ + cxmutstr: cx_map_detach_mustr, \ + char*: cx_map_detach_str, \ + char const*: cx_map_detach_str) \ + (map, key) + +/** + * Removes a key/value-pair from the map by using the key. + * + * @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 + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cx_map_remove_and_get( + CxMap *map, + CxHashKey key +) { + return map->cl->remove(map, key, !map->store_pointer); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * @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 + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cx_map_remove_and_get_cxstr( + CxMap *map, + cxstring key +) { + return map->cl->remove(map, cx_hash_key_cxstr(key), !map->store_pointer); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * @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 + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cx_map_remove_and_get_mustr( + CxMap *map, + cxmutstr key +) { + return map->cl->remove(map, cx_hash_key_cxstr(key), !map->store_pointer); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * @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 + */ +__attribute__((__nonnull__, __warn_unused_result__)) +static inline void *cx_map_remove_and_get_str( + CxMap *map, + char const *key +) { + return map->cl->remove(map, cx_hash_key_str(key), !map->store_pointer); +} + +/** + * Removes a key/value-pair from the map by using the key. + * + * This function can be used when the map is storing pointers, + * in order to retrieve the pointer from the map without invoking + * any destructor function. Sometimes you do not want the pointer + * to be returned - in that case (instead of suppressing the "unused + * result" warning) you can use cxMapDetach(). + * + * If this map is not storing pointers, this function behaves like + * cxMapRemove() and returns \c NULL. + * + * @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() + * @see cxMapDetach() + */ +#define cxMapRemoveAndGet(map, key) _Generic((key), \ + CxHashKey: cx_map_remove_and_get, \ + cxstring: cx_map_remove_and_get_cxstr, \ + cxmutstr: cx_map_remove_and_get_mustr, \ + char*: cx_map_remove_and_get_str, \ + char const*: cx_map_remove_and_get_str) \ + (map, key) + +#endif // __cplusplus + +#endif // UCX_MAP_H diff -r 809581724cc7 -r 0358f1d9c506 ucx/cx/mempool.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/mempool.h Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,122 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * \file 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 "common.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 diff -r 809581724cc7 -r 0358f1d9c506 ucx/cx/printf.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/printf.h Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,166 @@ +/* + * 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 + +#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 const *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 const *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 diff -r 809581724cc7 -r 0358f1d9c506 ucx/cx/string.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/string.h Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,1078 @@ +/* + * 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; + +#ifdef __cplusplus +extern "C" { + +/** + * A literal initializer for an UCX string structure. + * + * @param literal the string literal + */ +#define CX_STR(literal) cxstring{literal, sizeof(literal) - 1} + +#else // __cplusplus + +/** + * 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) (cxstring){literal, sizeof(literal) - 1} + +#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 const *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 strings. + * + * The resulting string will be allocated by the specified allocator. + * So developers \em must pass the return value to cx_strfree_a() eventually. + * + * If \p str already contains a string, the memory will be reallocated and + * the other strings are appended. Otherwise, new memory is allocated. + * + * \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 str the string the other strings shall be concatenated to + * @param count the number of the other following strings to concatenate + * @param ... all other strings + * @return the concatenated string + */ +__attribute__((__warn_unused_result__, __nonnull__)) +cxmutstr cx_strcat_ma( + CxAllocator const *alloc, + cxmutstr str, + size_t count, + ... +); + +/** + * Concatenates strings and returns a new string. + * + * The resulting string will be allocated by the specified allocator. + * So developers \em must pass the return value to cx_strfree_a() 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 number of the other following strings to concatenate + * @param ... all other strings + * @return the concatenated string + */ +#define cx_strcat_a(alloc, count, ...) \ +cx_strcat_ma(alloc, cx_mutstrn(NULL, 0), count, __VA_ARGS__) + +/** + * Concatenates strings and returns a new string. + * + * 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 number of the other following strings to concatenate + * @param ... all other strings + * @return the concatenated string + */ +#define cx_strcat(count, ...) \ +cx_strcat_ma(cxDefaultAllocator, cx_mutstrn(NULL, 0), count, __VA_ARGS__) + +/** + * Concatenates strings. + * + * The resulting string will be allocated by standard \c malloc(). + * So developers \em must pass the return value to cx_strfree() eventually. + * + * If \p str already contains a string, the memory will be reallocated and + * the other strings are appended. Otherwise, new memory is allocated. + * + * \note It is guaranteed that there is only one allocation. + * It is also guaranteed that the returned string is zero-terminated. + * + * @param str the string the other strings shall be concatenated to + * @param count the number of the other following strings to concatenate + * @param ... all other strings + * @return the concatenated string + */ +#define cx_strcat_m(str, count, ...) \ +cx_strcat_ma(cxDefaultAllocator, str, 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 const *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 const *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 cx_compare_func. + * + * @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 cx_compare_func. + * + * @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 const *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) + + +/** + * 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_m() + */ +#define cx_strdup_ma(allocator, string) cx_strdup_a(allocator, cx_strcast(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_ma() + */ +#define cx_strdup_m(string) cx_strdup_a(cxDefaultAllocator, cx_strcast(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 const *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 diff -r 809581724cc7 -r 0358f1d9c506 ucx/cx/tree.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/tree.h Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,136 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * \file tree.h + * \brief Interface for tree implementations. + * \author Olaf Wintermann + * \author Mike Becker + * \version 3.0 + * \copyright 2-Clause BSD License + */ + +#ifndef UCX_TREE_H +#define UCX_TREE_H + +#include "common.h" +#include "allocator.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Adds a sibling to the current tree node. + * + * In case your struct does not have a \p prev or a \p parent pointer, + * specify a negative location. The location of the \p next pointer is + * mandatory. + * + * \attention Do not use this function to add siblings in a tree where the + * nodes store a pointer to the last sibling because that would not be modified by this function. + * + * \remark If yo do not provide a location to the parent pointer, a call to this function is + * effectively the same as a call to cx_linked_list_add(). + * + * @param node a pointer to the node + * @param loc_prev the location of a \c prev pointer within your node struct + * @param loc_next the location of a \c next pointer within your node struct + * @param loc_parent the location of a \c parent pointer within your node struct + * @param new_node the new node that shall be added as a sibling + */ +void cx_tree_add_sibling(void *node, + ptrdiff_t loc_prev, ptrdiff_t loc_next, + ptrdiff_t loc_parent, + void *new_node) +__attribute__((__nonnull__)); + +/** + * Adds a node to the list of children. + * + * \par Example with a full structure + * A full tree node structure may look like this: + * \code + * typedef struct MyTreeNode MyTreeNode; + * struct MyTreeNode { + * MyTreeNode* parent; + * MyTreeNode* first_child; + * MyTreeNode* last_child; + * MyTreeNode* prev_sibling; + * MyTreeNode* next_sibling; + * // ...contents... + * } + * \endcode + * Adding a new child to a node with the above structure can be performed with the following call: + * \code + * MyTreeNode *node, *child; // given + * cx_tree_add_child(&node->first_child, &node->last_child, + * offsetof(MyTreeNode, prev_sibling), offsetof(MyTreeNode, next_sibling), + * child, offsetof(MyTreeNode, parent), node); + * \endcode + * + * \par Example with a reduced structure + * The minimal reasonable structure with parent pointer looks like this: + * \code + * typedef struct MyTreeNode MyTreeNode; + * struct MyTreeNode { + * MyTreeNode* parent; + * MyTreeNode* children; + * MyTreeNode* next_sibling; + * // ...contents... + * } + * \endcode + * This simplifies the function call to: + * \code + * MyTreeNode *node, *child; // given + * cx_tree_add_child(&node->children, NULL, -1, offsetof(MyTreeNode, next_sibling), + * child, offsetof(MyTreeNode, parent), node); + * \endcode + * + * \remark If your tree structure does not possess a parent pointer, a call to this function is + * effectively the same as a call to cx_linked_list_add(). + * + * @param children_begin a pointer to the begin node pointer (if your list has one) + * @param children_end a pointer to the end node pointer (if your list has one) + * @param loc_prev the location of a \c prev pointer within your node struct + * @param loc_next the location of a \c next pointer within your node struct + * @param new_node a pointer to the node that shall be appended + * @param loc_parent the location of a \c parent pointer within your node struct + * @param parent the parent node + */ +void cx_tree_add_child(void **children_begin, void **children_end, + ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node, + ptrdiff_t loc_parent, void *parent) +__attribute__((__nonnull__ (5))); + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // UCX_TREE_H + diff -r 809581724cc7 -r 0358f1d9c506 ucx/cx/utils.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/cx/utils.h Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,196 @@ +/* + * 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 diff -r 809581724cc7 -r 0358f1d9c506 ucx/hash_key.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/hash_key.c Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,112 @@ +/* + * 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 + +void cx_hash_murmur(CxHashKey *key) { + unsigned char const *data = key->data; + 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 = 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 = bytes; + key.len = len; + cx_hash_murmur(&key); + return key; +} + +CxHashKey cx_hash_key( + void const *obj, + size_t len +) { + CxHashKey key; + key.data = obj; + key.len = len; + cx_hash_murmur(&key); + return key; +} diff -r 809581724cc7 -r 0358f1d9c506 ucx/hash_map.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/hash_map.c Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,489 @@ +/* + * 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_map.h" +#include "cx/utils.h" + +#include +#include + +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; + // invoke the destructor + cx_invoke_destructor(map, elem->data); + // free the key data + cxFree(map->allocator, (void *) elem->key.data); + // 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 const *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, key.data, key.len) == 0) { + // overwrite existing element + if (map->store_pointer) { + memcpy(elm->data, &value, sizeof(void *)); + } else { + memcpy(elm->data, value, map->item_size); + } + } else { + // allocate new element + struct cx_hash_map_element_s *e = cxMalloc( + allocator, + sizeof(struct cx_hash_map_element_s) + map->item_size + ); + if (e == NULL) { + return -1; + } + + // write the value + if (map->store_pointer) { + memcpy(e->data, &value, sizeof(void *)); + } else { + memcpy(e->data, value, map->item_size); + } + + // copy the key + void *kd = cxMalloc(allocator, key.len); + if (kd == NULL) { + return -1; + } + memcpy(kd, key.data, key.len); + e->key.data = 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, (void *) elm->key.data); + 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 + * @param destroy flag indicating whether the destructor shall be invoked + * @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, + bool destroy +) { + 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, key.data, key.len) == 0) { + void *data = NULL; + if (destroy) { + cx_invoke_destructor(map, elm->data); + } else { + if (map->store_pointer) { + data = *(void **) elm->data; + } else { + 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 the map stays untouched + return cx_hash_map_get_remove((CxMap *) map, key, false, false); +} + +static void *cx_hash_map_remove( + CxMap *map, + CxHashKey key, + bool destroy +) { + return cx_hash_map_get_remove(map, key, true, destroy); +} + +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_pointer) { + 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; + } + } + + // destroy + cx_invoke_destructor((struct cx_map_s *) map, elm->data); + + // 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_pointer) { + 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, + enum cx_map_iterator_type type +) { + CxIterator iter; + + iter.src_handle = map; + iter.base.valid = cx_hash_map_iter_valid; + iter.base.next = cx_hash_map_iter_next; + + switch (type) { + case CX_MAP_ITERATOR_PAIRS: + iter.base.current = cx_hash_map_iter_current_entry; + break; + case CX_MAP_ITERATOR_KEYS: + iter.base.current = cx_hash_map_iter_current_key; + break; + case CX_MAP_ITERATOR_VALUES: + iter.base.current = cx_hash_map_iter_current_value; + break; + default: + assert(false); + } + + 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_pointer) { + 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 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, +}; + +CxMap *cxHashMapCreate( + CxAllocator const *allocator, + size_t itemsize, + size_t buckets +) { + if (buckets == 0) { + // implementation defined default + buckets = 16; + } + + struct cx_hash_map_s *map = cxCalloc(allocator, 1, + 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; + + if (itemsize > 0) { + map->base.store_pointer = false; + map->base.item_size = itemsize; + } else { + map->base.store_pointer = true; + map->base.item_size = 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; +} diff -r 809581724cc7 -r 0358f1d9c506 ucx/linked_list.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/linked_list.c Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,927 @@ +/* + * 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 +#include + +// 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; +} + +ssize_t cx_linked_list_find( + void const *start, + ptrdiff_t loc_advance, + ptrdiff_t loc_data, + cx_compare_func cmp_func, + void const *elem +) { + assert(start != NULL); + assert(loc_advance >= 0); + assert(loc_data >= 0); + assert(cmp_func); + + void const *node = start; + ssize_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 -1; +} + +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, + cx_compare_func cmp_func, + void **begin, + void **end +) { + 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; + + *begin = sorted[0]; + *end = sorted[length-1]; + if (sorted != sbo) { + free(sorted); + } +} + +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, + cx_compare_func 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; + + // early exit when this list is empty + if (ls == NULL) return; + + // 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_begin, *sorted_end; + cx_linked_list_sort_merge(loc_prev, loc_next, loc_data, + ln + rn, ls, le, re, cmp_func, + &sorted_begin, &sorted_end); + + // 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 + cx_linked_list_sort_merge(loc_prev, loc_next, loc_data, + ln + rn + remainder_length, + sorted_begin, remainder, NULL, cmp_func, + &sorted_begin, &sorted_end); + } + *begin = sorted_begin; + if (end) *end = sorted_end; + } +} + +int cx_linked_list_compare( + void const *begin_left, + void const *begin_right, + ptrdiff_t loc_advance, + ptrdiff_t loc_data, + cx_compare_func 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->item_size); + + // 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->item_size); + + // 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->item_size; + 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 + cx_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; + while (node != NULL) { + cx_invoke_destructor(list, node->payload); + cx_linked_list_node *next = node->next; + cxFree(list->allocator, node); + node = next; + } + 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->item_size > 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->item_size); + memcpy(nleft->payload, nright->payload, list->item_size); + memcpy(nright->payload, buf, list->item_size); + } + + 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 ssize_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; + cx_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--; + cx_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) { + cx_invoke_destructor(list, node->payload); + void *next = node->next; + cxFree(list->allocator, node); + node = next; + } + + cxFree(list->allocator, list); +} + +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, + cx_compare_func 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; + + if (item_size > 0) { + list->base.item_size = item_size; + } else { + cxListStorePointers((CxList *) list); + } + + return (CxList *) list; +} diff -r 809581724cc7 -r 0358f1d9c506 ucx/list.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/list.c Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,342 @@ +/* + * 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/list.h" + +#include + +// + +static _Thread_local cx_compare_func cx_pl_cmpfunc_impl; + +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); +} + +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; +} + +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; +} + +static void cx_pl_destructor(struct cx_list_s *list) { + list->climpl->destructor(list); +} + +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); +} + +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); +} + +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); +} + +static int cx_pl_remove( + struct cx_list_s *list, + size_t index +) { + return list->climpl->remove(list, index); +} + +static void cx_pl_clear(struct cx_list_s *list) { + list->climpl->clear(list); +} + +static int cx_pl_swap( + struct cx_list_s *list, + size_t i, + size_t j +) { + return list->climpl->swap(list, i, j); +} + +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; +} + +static ssize_t cx_pl_find( + struct cx_list_s const *list, + void const *elem +) { + cx_pl_hack_cmpfunc(list); + ssize_t ret = list->climpl->find(list, &elem); + cx_pl_unhack_cmpfunc(list); + return ret; +} + +static void cx_pl_sort(struct cx_list_s *list) { + cx_pl_hack_cmpfunc(list); + list->climpl->sort(list); + cx_pl_unhack_cmpfunc(list); +} + +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 void cx_pl_reverse(struct cx_list_s *list) { + list->climpl->reverse(list); +} + +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; +} + +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; +} + +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) { + list->store_pointer = false; + if (list->climpl != NULL) { + list->cl = list->climpl; + list->climpl = NULL; + } +} + +void cxListStorePointers(CxList *list) { + list->item_size = sizeof(void *); + list->store_pointer = true; + list->climpl = list->cl; + list->cl = &cx_pointer_list_class; +} + +// + +// + +static void cx_emptyl_noop(__attribute__((__unused__)) CxList *list) { + // this is a noop, but MUST be implemented +} + +static void *cx_emptyl_at( + __attribute__((__unused__)) struct cx_list_s const *list, + __attribute__((__unused__)) size_t index +) { + return NULL; +} + +static ssize_t cx_emptyl_find( + __attribute__((__unused__)) struct cx_list_s const *list, + __attribute__((__unused__)) void const *elem +) { + return -1; +} + +static int cx_emptyl_compare( + __attribute__((__unused__)) struct cx_list_s const *list, + struct cx_list_s const *other +) { + if (other->size == 0) return 0; + return -1; +} + +static bool cx_emptyl_iter_valid(__attribute__((__unused__)) void const *iter) { + return false; +} + +static CxIterator cx_emptyl_iterator( + struct cx_list_s const *list, + size_t index, + __attribute__((__unused__)) bool backwards +) { + CxIterator iter = {0}; + iter.src_handle = list; + iter.index = index; + iter.base.valid = cx_emptyl_iter_valid; + return iter; +} + +static cx_list_class cx_empty_list_class = { + cx_emptyl_noop, + NULL, + NULL, + NULL, + NULL, + cx_emptyl_noop, + NULL, + cx_emptyl_at, + cx_emptyl_find, + cx_emptyl_noop, + cx_emptyl_compare, + cx_emptyl_noop, + cx_emptyl_iterator, +}; + +CxList cx_empty_list = { + NULL, + NULL, + 0, + 0, + NULL, + NULL, + NULL, + false, + &cx_empty_list_class, + NULL +}; + +CxList *const cxEmptyList = &cx_empty_list; + +// + +void cxListDestroy(CxList *list) { + list->cl->destructor(list); +} + +int cxListCompare( + CxList const *list, + CxList const *other +) { + if ( + // if one is storing pointers but the other is not + (list->store_pointer ^ other->store_pointer) || + + // if one class is wrapped but the other is not + ((list->climpl == NULL) ^ (other->climpl == NULL)) || + + // if the resolved compare functions are not the same + ((list->climpl != NULL ? list->climpl->compare : list->cl->compare) != + (other->climpl != NULL ? other->climpl->compare : other->cl->compare)) + ) { + // lists are definitely different - cannot use internal compare function + 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; + } + } else { + // lists are compatible + return list->cl->compare(list, other); + } +} + +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; +} + +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; +} diff -r 809581724cc7 -r 0358f1d9c506 ucx/map.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/map.c Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,112 @@ +/* + * 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: + * + * 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/map.h" +#include + +// + +static void cx_empty_map_noop(__attribute__((__unused__)) CxMap *map) { + // this is a noop, but MUST be implemented +} + +static void *cx_empty_map_get( + __attribute__((__unused__)) CxMap const *map, + __attribute__((__unused__)) CxHashKey key +) { + return NULL; +} + +static bool cx_empty_map_iter_valid(__attribute__((__unused__)) void const *iter) { + return false; +} + +static CxIterator cx_empty_map_iterator( + struct cx_map_s const *map, + __attribute__((__unused__)) enum cx_map_iterator_type type +) { + CxIterator iter = {0}; + iter.src_handle = map; + iter.base.valid = cx_empty_map_iter_valid; + return iter; +} + +static struct cx_map_class_s cx_empty_map_class = { + cx_empty_map_noop, + cx_empty_map_noop, + NULL, + cx_empty_map_get, + NULL, + cx_empty_map_iterator +}; + +CxMap cx_empty_map = { + NULL, + NULL, + 0, + 0, + NULL, + NULL, + NULL, + false, + &cx_empty_map_class +}; + +CxMap *const cxEmptyMap = &cx_empty_map; + +// + +CxMutIterator cxMapMutIteratorValues(CxMap *map) { + CxIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_VALUES); + it.base.mutating = true; + + // we know the iterators share the same memory layout + CxMutIterator iter; + memcpy(&iter, &it, sizeof(CxMutIterator)); + return iter; +} + +CxMutIterator cxMapMutIteratorKeys(CxMap *map) { + CxIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_KEYS); + it.base.mutating = true; + + // we know the iterators share the same memory layout + CxMutIterator iter; + memcpy(&iter, &it, sizeof(CxMutIterator)); + return iter; +} + +CxMutIterator cxMapMutIterator(CxMap *map) { + CxIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS); + it.base.mutating = true; + + // we know the iterators share the same memory layout + CxMutIterator iter; + memcpy(&iter, &it, sizeof(CxMutIterator)); + return iter; +} diff -r 809581724cc7 -r 0358f1d9c506 ucx/printf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/printf.c Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,129 @@ +/* + * 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 +#include + +#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 const *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 const *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; +} + diff -r 809581724cc7 -r 0358f1d9c506 ucx/string.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/string.c Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,785 @@ +/* + * 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/string.h" +#include "cx/utils.h" + +#include +#include +#include + +#ifndef _WIN32 + +#include // 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}; +} + +cxstring cx_str(const char *cstring) { + return (cxstring) {cstring, strlen(cstring)}; +} + +cxstring cx_strn( + const char *cstring, + size_t length +) { + return (cxstring) {cstring, length}; +} + +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 const *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; + + va_list ap; + va_start(ap, count); + size_t size = 0; + cx_for_n(i, count) { + cxstring str = va_arg(ap, cxstring); + size += str.length; + } + va_end(ap); + + return size; +} + +cxmutstr cx_strcat_ma( + CxAllocator const *alloc, + cxmutstr str, + size_t count, + ... +) { + if (count == 0) return str; + + cxstring *strings = calloc(count, sizeof(cxstring)); + if (!strings) abort(); + + va_list ap; + va_start(ap, count); + + // get all args and overall length + size_t slen = str.length; + cx_for_n(i, count) { + cxstring s = va_arg (ap, cxstring); + strings[i] = s; + slen += s.length; + } + va_end(ap); + + // reallocate or create new string + if (str.ptr == NULL) { + str.ptr = cxMalloc(alloc, slen + 1); + } else { + str.ptr = cxRealloc(alloc, str.ptr, slen + 1); + } + if (str.ptr == NULL) abort(); + + // concatenate strings + size_t pos = str.length; + str.length = slen; + cx_for_n(i, count) { + cxstring s = strings[i]; + memcpy(str.ptr + pos, s.ptr, s.length); + pos += s.length; + } + + // terminate string + str.ptr[str.length] = '\0'; + + // free temporary array + free(strings); + + return str; +} + +cxstring cx_strsubs( + cxstring string, + size_t start +) { + return cx_strsubsl(string, start, string.length - start); +} + +cxmutstr cx_strsubs_m( + cxmutstr string, + size_t start +) { + return cx_strsubsl_m(string, start, string.length - start); +} + +cxstring cx_strsubsl( + cxstring string, + size_t start, + size_t length +) { + if (start > string.length) { + return (cxstring) {NULL, 0}; + } + + size_t rem_len = string.length - start; + if (length > rem_len) { + length = rem_len; + } + + return (cxstring) {string.ptr + start, length}; +} + +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 (cxstring) {NULL, 0}; +} + +cxmutstr cx_strchr_m( + cxmutstr string, + int chr +) { + cxstring result = cx_strchr(cx_strcast(string), chr); + return (cxmutstr) {(char *) result.ptr, result.length}; +} + +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 (cxstring) {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}; +} + +#ifndef CX_STRSTR_SBO_SIZE +#define CX_STRSTR_SBO_SIZE 512 +#endif + +cxstring cx_strstr( + cxstring haystack, + cxstring needle +) { + if (needle.length == 0) { + return haystack; + } + + // optimize for single-char needles + if (needle.length == 1) { + return cx_strchr(haystack, *needle.ptr); + } + + /* + * 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. + */ + + // 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[i] = j; + while (i < needle.length) { + while (j >= 1 && needle.ptr[j - 1] != needle.ptr[i]) { + j = ptable[j - 1]; + } + 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 (ptable != s_prefix_table) { + free(ptable); + } + + 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}; +} + +size_t cx_strsplit( + cxstring string, + cxstring delim, + size_t limit, + cxstring *output +) { + // special case: output limit is zero + if (limit == 0) return 0; + + // special case: delimiter is empty + if (delim.length == 0) { + output[0] = string; + return 1; + } + + // special cases: delimiter is at least as large as the string + if (delim.length >= string.length) { + // exact match + if (cx_strcmp(string, delim) == 0) { + output[0] = cx_strn(string.ptr, 0); + output[1] = cx_strn(string.ptr + string.length, 0); + return 2; + } else { + // no match possible + output[0] = string; + return 1; + } + } + + size_t n = 0; + cxstring curpos = string; + while (1) { + ++n; + cxstring match = cx_strstr(curpos, delim); + if (match.length > 0) { + // is the limit reached? + if (n < limit) { + // copy the current string to the array + cxstring item = cx_strn(curpos.ptr, match.ptr - curpos.ptr); + output[n - 1] = item; + size_t processed = item.length + delim.length; + curpos.ptr += processed; + curpos.length -= processed; + } else { + // limit reached, copy the _full_ remaining string + output[n - 1] = curpos; + break; + } + } else { + // no more matches, copy last string + output[n - 1] = curpos; + break; + } + } + + return n; +} + +size_t cx_strsplit_a( + CxAllocator const *allocator, + cxstring string, + cxstring delim, + size_t limit, + cxstring **output +) { + // find out how many splits we're going to make and allocate memory + size_t n = 0; + cxstring curpos = string; + while (1) { + ++n; + cxstring match = cx_strstr(curpos, delim); + if (match.length > 0) { + // is the limit reached? + if (n < limit) { + size_t processed = match.ptr - curpos.ptr + delim.length; + curpos.ptr += processed; + curpos.length -= processed; + } else { + // limit reached + break; + } + } else { + // no more matches + break; + } + } + *output = cxCalloc(allocator, n, sizeof(cxstring)); + return cx_strsplit(string, delim, n, *output); +} + +size_t cx_strsplit_m( + cxmutstr string, + cxstring delim, + size_t limit, + cxmutstr *output +) { + return cx_strsplit(cx_strcast(string), + delim, limit, (cxstring *) output); +} + +size_t cx_strsplit_ma( + CxAllocator const *allocator, + cxmutstr string, + cxstring delim, + size_t limit, + cxmutstr **output +) { + return cx_strsplit_a(allocator, cx_strcast(string), + delim, limit, (cxstring **) output); +} + +int cx_strcmp( + cxstring s1, + cxstring s2 +) { + if (s1.length == s2.length) { + return memcmp(s1.ptr, s2.ptr, s1.length); + } else if (s1.length > s2.length) { + return 1; + } else { + return -1; + } +} + +int cx_strcasecmp( + cxstring s1, + cxstring s2 +) { + if (s1.length == s2.length) { +#ifdef _WIN32 + return _strnicmp(s1.ptr, s2.ptr, s1.length); +#else + return strncasecmp(s1.ptr, s2.ptr, s1.length); +#endif + } else if (s1.length > s2.length) { + return 1; + } else { + return -1; + } +} + +int cx_strcmp_p( + void const *s1, + void const *s2 +) { + cxstring const *left = s1; + cxstring const *right = s2; + return cx_strcmp(*left, *right); +} + +int cx_strcasecmp_p( + void const *s1, + void const *s2 +) { + cxstring const *left = s1; + cxstring const *right = s2; + return cx_strcasecmp(*left, *right); +} + +cxmutstr cx_strdup_a( + CxAllocator const *allocator, + cxstring string +) { + cxmutstr result = { + cxMalloc(allocator, string.length + 1), + string.length + }; + if (result.ptr == NULL) { + result.length = 0; + return result; + } + memcpy(result.ptr, string.ptr, string.length); + result.ptr[string.length] = '\0'; + return result; +} + +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--; + } + while (result.length > 0 && isspace(result.ptr[result.length - 1])) { + result.length--; + } + return result; +} + +cxmutstr cx_strtrim_m(cxmutstr string) { + cxstring result = cx_strtrim(cx_strcast(string)); + return (cxmutstr) {(char *) result.ptr, result.length}; +} + +bool cx_strprefix( + cxstring string, + cxstring prefix +) { + if (string.length < prefix.length) return false; + return memcmp(string.ptr, prefix.ptr, prefix.length) == 0; +} + +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; +} + +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 +} + +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 +} + +void cx_strlower(cxmutstr string) { + cx_for_n(i, string.length) { + string.ptr[i] = (char) tolower(string.ptr[i]); + } +} + +void cx_strupper(cxmutstr string) { + cx_for_n(i, string.length) { + string.ptr[i] = (char) toupper(string.ptr[i]); + } +} + +#ifndef CX_STRREPLACE_INDEX_BUFFER_SIZE +#define CX_STRREPLACE_INDEX_BUFFER_SIZE 64 +#endif + +struct cx_strreplace_ibuf { + size_t *buf; + struct cx_strreplace_ibuf *next; + unsigned int len; +}; + +static void cx_strrepl_free_ibuf(struct cx_strreplace_ibuf *buf) { + while (buf) { + struct cx_strreplace_ibuf *next = buf->next; + free(buf->buf); + free(buf); + buf = next; + } +} + +cxmutstr cx_strreplacen_a( + CxAllocator const *allocator, + cxstring str, + cxstring pattern, + cxstring replacement, + size_t replmax +) { + + if (pattern.length == 0 || pattern.length > str.length || replmax == 0) + return cx_strdup_a(allocator, str); + + // Compute expected buffer length + size_t ibufmax = str.length / pattern.length; + size_t ibuflen = replmax < ibufmax ? replmax : ibufmax; + if (ibuflen > CX_STRREPLACE_INDEX_BUFFER_SIZE) { + ibuflen = CX_STRREPLACE_INDEX_BUFFER_SIZE; + } + + // 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 cx_mutstrn(NULL, 0); + } + + // Search occurrences + cxstring searchstr = str; + size_t found = 0; + do { + cxstring match = cx_strstr(searchstr, pattern); + if (match.length > 0) { + // Allocate next buffer in chain, if required + if (curbuf->len == ibuflen) { + struct cx_strreplace_ibuf *nextbuf = + calloc(1, sizeof(struct cx_strreplace_ibuf)); + if (!nextbuf) { + cx_strrepl_free_ibuf(firstbuf); + return cx_mutstrn(NULL, 0); + } + nextbuf->buf = calloc(ibuflen, sizeof(size_t)); + if (!nextbuf->buf) { + free(nextbuf); + cx_strrepl_free_ibuf(firstbuf); + return cx_mutstrn(NULL, 0); + } + curbuf->next = nextbuf; + curbuf = nextbuf; + } + + // Record match index + found++; + size_t idx = match.ptr - str.ptr; + curbuf->buf[curbuf->len++] = idx; + searchstr.ptr = match.ptr + pattern.length; + searchstr.length = str.length - idx - pattern.length; + } else { + break; + } + } while (searchstr.length > 0 && found < replmax); + + // Allocate result string + cxmutstr result; + { + ssize_t adjlen = (ssize_t) replacement.length - (ssize_t) pattern.length; + size_t rcount = 0; + curbuf = firstbuf; + do { + rcount += curbuf->len; + curbuf = curbuf->next; + } while (curbuf); + result.length = str.length + rcount * adjlen; + result.ptr = cxMalloc(allocator, result.length + 1); + if (!result.ptr) { + cx_strrepl_free_ibuf(firstbuf); + return cx_mutstrn(NULL, 0); + } + } + + // Build result string + curbuf = firstbuf; + size_t srcidx = 0; + char *destptr = result.ptr; + do { + for (size_t i = 0; i < curbuf->len; i++) { + // Copy source part up to next match + size_t idx = curbuf->buf[i]; + size_t srclen = idx - srcidx; + if (srclen > 0) { + memcpy(destptr, str.ptr + srcidx, srclen); + destptr += srclen; + srcidx += srclen; + } + + // Copy the replacement and skip the source pattern + srcidx += pattern.length; + memcpy(destptr, replacement.ptr, replacement.length); + destptr += replacement.length; + } + curbuf = curbuf->next; + } while (curbuf); + memcpy(destptr, str.ptr + srcidx, str.length - srcidx); + + // Result is guaranteed to be zero-terminated + result.ptr[result.length] = '\0'; + + // Free index buffer + cx_strrepl_free_ibuf(firstbuf); + + return result; +} + +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; + + return true; +} + +bool cx_strtok_next_m( + CxStrtokCtx *ctx, + cxmutstr *token +) { + return cx_strtok_next(ctx, (cxstring *) token); +} + +void cx_strtok_delim( + CxStrtokCtx *ctx, + cxstring const *delim, + size_t count +) { + ctx->delim_more = delim; + ctx->delim_more_count = count; +} diff -r 809581724cc7 -r 0358f1d9c506 ucx/szmul.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/szmul.c Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,46 @@ +/* + * 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: + * + * 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. + */ + +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) { + *result = r; + return 0; + } else { + *result = 0; + return 1; + } +} \ No newline at end of file diff -r 809581724cc7 -r 0358f1d9c506 ucx/tree.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/tree.c Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,52 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "cx/tree.h" +#include "cx/linked_list.h" + +#define CX_TR_PTR(cur, off) *((void**)(((char*)(cur))+(off))) + +void cx_tree_add_sibling(void *node, ptrdiff_t loc_prev, ptrdiff_t loc_next, ptrdiff_t loc_parent, void *new_node) { + cx_linked_list_add(&node, NULL, loc_prev, loc_next, new_node); + + // optional parent link + if (loc_parent >= 0) { + CX_TR_PTR(new_node, loc_parent) = CX_TR_PTR(node, loc_parent); + } +} + +void cx_tree_add_child(void **children_begin, void **children_end, + ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node, + ptrdiff_t loc_parent, void *parent) { + cx_linked_list_add(children_begin, children_end, loc_prev, loc_next, new_node); + + // optional parent link + if (loc_parent >= 0) { + CX_TR_PTR(new_node, loc_parent) = parent; + } +} diff -r 809581724cc7 -r 0358f1d9c506 ucx/utils.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/utils.c Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,94 @@ +/* + * 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/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; + size_t ncp = 0; + + if (buf) { + if (bufsize == 0) return 0; + lbuf = buf; + } else { + 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 = 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) { + break; + } + } + + if (lbuf != buf) { + free(lbuf); + } + + return ncp; +} + +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); +} + +#ifndef CX_SZMUL_BUILTIN +#include "szmul.c" +#endif diff -r 809581724cc7 -r 0358f1d9c506 ui/Makefile --- a/ui/Makefile Sat Apr 15 21:06:45 2023 +0200 +++ b/ui/Makefile Mon May 22 16:17:26 2023 +0200 @@ -33,7 +33,7 @@ include common/objs.mk -UI_LIB = ../build/lib/libuitk.$(LIB_EXT) +UI_LIB = ../build/lib/libuitk$(LIB_EXT) include $(TOOLKIT)/objs.mk OBJ = $(TOOLKITOBJS) $(COMMONOBJS) @@ -43,5 +43,5 @@ include $(TOOLKIT)/Makefile $(COMMON_OBJPRE)%.o: common/%.c - $(CC) -o $@ -c $(CFLAGS) $(TK_CFLAGS) $< + $(CC) -o $@ -c -I../ucx/ $(CFLAGS) $(TK_CFLAGS) $< diff -r 809581724cc7 -r 0358f1d9c506 ui/common/context.c --- a/ui/common/context.c Sat Apr 15 21:06:45 2023 +0200 +++ b/ui/common/context.c Mon May 22 16:17:26 2023 +0200 @@ -32,6 +32,10 @@ #include #include +#include +#include +#include + #include "context.h" #include "../ui/window.h" #include "document.h" @@ -40,20 +44,24 @@ static UiContext* global_context; void uic_init_global_context(void) { - UcxMempool *mp = ucx_mempool_new(32); - global_context = uic_context(NULL, mp); + CxMempool *mp = cxBasicMempoolCreate(32); + global_context = uic_context(NULL, mp->allocator); } UiContext* ui_global_context(void) { return global_context; } -UiContext* uic_context(UiObject *toplevel, UcxMempool *mp) { - UiContext *ctx = ucx_mempool_malloc(mp, sizeof(UiContext)); +UiContext* uic_context(UiObject *toplevel, const CxAllocator *a) { + UiContext *ctx = cxMalloc(a, sizeof(UiContext)); memset(ctx, 0, sizeof(UiContext)); - ctx->mempool = mp; + ctx->allocator = a; ctx->obj = toplevel; - ctx->vars = ucx_map_new_a(mp->allocator, 16); + ctx->vars = cxHashMapCreate(a, CX_STORE_POINTERS, 16); + + ctx->documents = cxLinkedListCreate(a, cx_cmp_intptr, CX_STORE_POINTERS); + ctx->group_widgets = cxLinkedListCreate(a, NULL, sizeof(UiGroupWidget)); + ctx->groups = cxArrayListCreate(a, cx_cmp_int, sizeof(int), 32); ctx->attach_document = uic_context_attach_document; ctx->detach_document2 = uic_context_detach_document2; @@ -73,8 +81,8 @@ } void uic_context_attach_document(UiContext *ctx, void *document) { - ctx->documents = ucx_list_append_a(ctx->mempool->allocator, ctx->documents, document); - ctx->document = ctx->documents->data; + cxListAdd(ctx->documents, document); + ctx->document = document; UiContext *doc_ctx = ui_document_context(document); @@ -82,25 +90,17 @@ // as any document variable UiContext *var_ctx = ctx; while(var_ctx) { - if(var_ctx->vars_unbound && var_ctx->vars_unbound->count > 0) { - UcxMapIterator i = ucx_map_iterator(var_ctx->vars_unbound); - UiVar *var; - // rmkeys holds all keys, that shall be removed from vars_unbound - UcxKey *rmkeys = calloc(var_ctx->vars_unbound->count, sizeof(UcxKey)); - size_t numkeys = 0; - UCX_MAP_FOREACH(key, var, i) { - UiVar *docvar = ucx_map_get(doc_ctx->vars, key); + if(var_ctx->vars_unbound && var_ctx->vars_unbound->size > 0) { + CxIterator i = cxMapIterator(var_ctx->vars_unbound); + cx_foreach(CxMapEntry*, entry, i) { + UiVar *var = entry->value; + UiVar *docvar = cxMapGet(doc_ctx->vars, *entry->key); if(docvar) { // bind var to document var uic_copy_binding(var, docvar, TRUE); - rmkeys[numkeys++] = key; // save the key for removal + cxIteratorFlagRemoval(i); } } - // now that we may have bound some vars to the document, - // we can remove them from the unbound map - for(size_t k=0;kvars_unbound, rmkeys[k]); - } } var_ctx = ctx->parent; @@ -108,56 +108,63 @@ } static void uic_context_unbind_vars(UiContext *ctx) { - UcxMapIterator i = ucx_map_iterator(ctx->vars); - UiVar *var; - UCX_MAP_FOREACH(key, var, i) { + CxIterator i = cxMapIterator(ctx->vars); + cx_foreach(CxMapEntry*, entry, i) { + UiVar *var = entry->value; if(var->from && var->from_ctx) { uic_save_var2(var); uic_copy_binding(var, var->from, FALSE); - ucx_map_put(var->from_ctx->vars_unbound, key, var->from); + cxMapPut(var->from_ctx->vars_unbound, *entry->key, var->from); var->from_ctx = ctx; } } - UCX_FOREACH(elm, ctx->documents) { - UiContext *subctx = ui_document_context(elm->data); - uic_context_unbind_vars(subctx); + if(ctx->documents) { + i = cxListIterator(ctx->documents); + cx_foreach(void *, doc, i) { + UiContext *subctx = ui_document_context(doc); + uic_context_unbind_vars(subctx); + } } } void uic_context_detach_document2(UiContext *ctx, void *document) { // find the document in the documents list - UcxList *doc = NULL; - UCX_FOREACH(elm, ctx->documents) { - if(elm->data == document) { - doc = elm; - break; - } - } - if(!doc) { - return; // document is not a subdocument of this context + ssize_t docIndex = cxListFind(ctx->documents, document); + if(docIndex < 0) { + return; } - ctx->documents = ucx_list_remove_a(ctx->mempool->allocator, ctx->documents, doc); - ctx->document = ctx->documents ? ctx->documents->data : NULL; + cxListRemove(ctx->documents, docIndex); + ctx->document = cxListAt(ctx->documents, 0); UiContext *docctx = ui_document_context(document); uic_context_unbind_vars(docctx); // unbind all doc/subdoc vars from the parent } void uic_context_detach_all(UiContext *ctx) { - UcxList *ls = ucx_list_clone(ctx->documents, NULL, NULL); - UCX_FOREACH(elm, ls) { - ctx->detach_document2(ctx, elm->data); + // copy list + CxList *ls = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS); + CxIterator i = cxListIterator(ctx->documents); + cx_foreach(void *, doc, i) { + cxListAdd(ls, doc); } - ucx_list_free(ls); + + // detach documents + i = cxListIterator(ls); + cx_foreach(void *, doc, i) { + ctx->detach_document2(ctx, doc); + } + + cxListDestroy(ls); } -static UiVar* ctx_getvar(UiContext *ctx, UcxKey key) { - UiVar *var = ucx_map_get(ctx->vars, key); +static UiVar* ctx_getvar(UiContext *ctx, CxHashKey key) { + UiVar *var = cxMapGet(ctx->vars, key); if(!var) { - UCX_FOREACH(elm, ctx->documents) { - UiContext *subctx = ui_document_context(elm->data); + CxIterator i = cxListIterator(ctx->documents); + cx_foreach(void *, doc, i) { + UiContext *subctx = ui_document_context(doc); var = ctx_getvar(subctx, key); if(var) { break; @@ -168,7 +175,7 @@ } UiVar* uic_get_var(UiContext *ctx, const char *name) { - UcxKey key = ucx_key(name, strlen(name)); + CxHashKey key = cx_hash_key(name, strlen(name)); return ctx_getvar(ctx, key); } @@ -189,9 +196,9 @@ var->from_ctx = ctx; if(!ctx->vars_unbound) { - ctx->vars_unbound = ucx_map_new_a(ctx->mempool->allocator, 16); + ctx->vars_unbound = cxHashMapCreate(ctx->allocator, CX_STORE_POINTERS, 16); } - ucx_map_cstr_put(ctx->vars_unbound, name, var); + cxMapPut(ctx->vars_unbound, name, var); return var; } @@ -358,9 +365,9 @@ var->value = value; var->from = NULL; var->from_ctx = ctx; - size_t oldcount = ctx->vars->count; - ucx_map_cstr_put(ctx->vars, name, var); - if(ctx->vars->count != oldcount + 1) { + size_t oldcount = ctx->vars->size; + cxMapPut(ctx->vars, name, var); + if(ctx->vars->size != oldcount + 1) { fprintf(stderr, "UiError: var '%s' already exists\n", name); } @@ -375,8 +382,7 @@ } void uic_remove_bound_var(UiContext *ctx, UiVar *var) { - // TODO: implement - printf("TODO: implement uic_remove_bound_var\n"); + // TODO } @@ -397,8 +403,8 @@ void ui_set_group(UiContext *ctx, int group) { - if(ucx_list_find(ctx->groups, (void*)(intptr_t)group, NULL, NULL) == -1) { - ctx->groups = ucx_list_append_a(ctx->mempool->allocator, ctx->groups, (void*)(intptr_t)group); + if(cxListFind(ctx->groups, &group) == -1) { + cxListAdd(ctx->groups, &group); } // enable/disable group widgets @@ -406,10 +412,9 @@ } void ui_unset_group(UiContext *ctx, int group) { - int i = ucx_list_find(ctx->groups, (void*)(intptr_t)group, NULL, NULL); + int i = cxListFind(ctx->groups, &group); if(i != -1) { - UcxList *elm = ucx_list_get(ctx->groups, i); - ctx->groups = ucx_list_remove_a(ctx->mempool->allocator, ctx->groups, elm); + cxListRemove(ctx->groups, i); } // enable/disable group widgets @@ -417,28 +422,16 @@ } int* ui_active_groups(UiContext *ctx, int *ngroups) { - if(!ctx->groups) { - return NULL; - } - - int nelm = ucx_list_size(ctx->groups); - int *groups = calloc(sizeof(int), nelm); - - int i = 0; - UCX_FOREACH(elm, ctx->groups) { - groups[i++] = (intptr_t)elm->data; - } - - *ngroups = nelm; - return groups; + *ngroups = ctx->groups->size; + return cxListAt(ctx->groups, 0); } void uic_check_group_widgets(UiContext *ctx) { int ngroups = 0; int *groups = ui_active_groups(ctx, &ngroups); - UCX_FOREACH(elm, ctx->group_widgets) { - UiGroupWidget *gw = elm->data; + CxIterator i = cxListIterator(ctx->group_widgets); + cx_foreach(UiGroupWidget *, gw, i) { char *check = calloc(1, gw->numgroups); for(int i=0;ienable(gw->widget, enable); } - - if(groups) { - free(groups); - } } void ui_widget_set_groups(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, ...) { // get groups - UcxList *groups = NULL; + CxList *groups = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), 16); va_list ap; va_start(ap, enable); int group; while((group = va_arg(ap, int)) != -1) { - groups = ucx_list_append(groups, (void*)(intptr_t)group); + cxListAdd(groups, &group); } va_end(ap); uic_add_group_widget(ctx, widget, enable, groups); - ucx_list_free(groups); + cxListDestroy(groups); } -void uic_add_group_widget(UiContext *ctx, void *widget, ui_enablefunc enable, UcxList *groups) { - UcxMempool *mp = ctx->mempool; - UiGroupWidget *gw = ucx_mempool_malloc(mp, sizeof(UiGroupWidget)); +void uic_add_group_widget(UiContext *ctx, void *widget, ui_enablefunc enable, CxList *groups) { + const CxAllocator *a = ctx->allocator; + UiGroupWidget gw; - gw->widget = widget; - gw->enable = enable; - gw->numgroups = ucx_list_size(groups); - gw->groups = ucx_mempool_calloc(mp, gw->numgroups, sizeof(int)); - int i = 0; - UCX_FOREACH(elm, groups) { - gw->groups[i++] = (intptr_t)elm->data; + gw.widget = widget; + gw.enable = enable; + gw.numgroups = groups->size; + gw.groups = cxCalloc(a, gw.numgroups, sizeof(int)); + + // copy groups + int *intgroups = cxListAt(groups, 0); + if(intgroups) { + memcpy(gw.groups, intgroups, gw.numgroups * sizeof(int)); } - ctx->group_widgets = ucx_list_append_a( - mp->allocator, - ctx->group_widgets, - gw); + cxListAdd(ctx->group_widgets, &gw); } void* ui_malloc(UiContext *ctx, size_t size) { - return ctx ? ucx_mempool_malloc(ctx->mempool, size) : NULL; + return ctx ? cxMalloc(ctx->allocator, size) : NULL; } void* ui_calloc(UiContext *ctx, size_t nelem, size_t elsize) { - return ctx ? ucx_mempool_calloc(ctx->mempool, nelem, elsize) : NULL; + return ctx ? cxCalloc(ctx->allocator, nelem, elsize) : NULL; } void ui_free(UiContext *ctx, void *ptr) { if(ctx) { - ucx_mempool_free(ctx->mempool, ptr); + cxFree(ctx->allocator, ptr); } } void* ui_realloc(UiContext *ctx, void *ptr, size_t size) { - return ctx ? ucx_mempool_realloc(ctx->mempool, ptr, size) : NULL; + return ctx ? cxRealloc(ctx->allocator, ptr, size) : NULL; } diff -r 809581724cc7 -r 0358f1d9c506 ui/common/context.h --- a/ui/common/context.h Sat Apr 15 21:06:45 2023 +0200 +++ b/ui/common/context.h Mon May 22 16:17:26 2023 +0200 @@ -30,9 +30,11 @@ #define UIC_CONTEXT_H #include "../ui/toolkit.h" -#include -#include -#include +#include +#include +#include +#include +#include #ifdef __cplusplus extern "C" { @@ -58,16 +60,16 @@ struct UiContext { UiContext *parent; UiObject *obj; - UcxMempool *mempool; + const CxAllocator *allocator; void *document; - UcxList *documents; + CxList *documents; - UcxMap *vars; // manually created context vars - UcxMap *vars_unbound; // unbound vars created by widgets + CxMap *vars; // manually created context vars + CxMap *vars_unbound; // unbound vars created by widgets - UcxList *groups; // int list - UcxList *group_widgets; // UiGroupWidget* list + CxList *groups; // int list + CxList *group_widgets; // UiGroupWidget list void (*attach_document)(UiContext *ctx, void *document); void (*detach_document2)(UiContext *ctx, void *document); @@ -100,7 +102,7 @@ void uic_init_global_context(void); -UiContext* uic_context(UiObject *toplevel, UcxMempool *mp); +UiContext* uic_context(UiObject *toplevel, const CxAllocator *a); UiContext* uic_root_context(UiContext *ctx); void uic_context_set_document(UiContext *ctx, void *document); // deprecated void uic_context_detach_document(UiContext *ctx); // deprecated @@ -122,7 +124,7 @@ void uic_remove_bound_var(UiContext *ctx, UiVar *var); void uic_check_group_widgets(UiContext *ctx); -void uic_add_group_widget(UiContext *ctx, void *widget, ui_enablefunc enable, UcxList *groups); +void uic_add_group_widget(UiContext *ctx, void *widget, ui_enablefunc enable, CxList *groups); #ifdef __cplusplus diff -r 809581724cc7 -r 0358f1d9c506 ui/common/document.c --- a/ui/common/document.c Sat Apr 15 21:06:45 2023 +0200 +++ b/ui/common/document.c Mon May 22 16:17:26 2023 +0200 @@ -32,10 +32,13 @@ #include "document.h" -static UcxMap *documents; +#include +#include + +static CxMap *documents; void uic_docmgr_init() { - documents = ucx_map_new(32); + documents = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 32); } void ui_set_document(UiObject *obj, void *document) { @@ -77,15 +80,16 @@ } void* ui_document_new(size_t size) { - UcxMempool *mp = ucx_mempool_new(256); - UiContext *ctx = ucx_mempool_calloc(mp, 1, sizeof(UiContext)); + CxMempool *mp = cxBasicMempoolCreate(256); + const CxAllocator *a = mp->allocator; + UiContext *ctx = cxCalloc(a, 1, sizeof(UiContext)); ctx->attach_document = uic_context_attach_document; ctx->detach_document2 = uic_context_detach_document2; - ctx->mempool = mp; - ctx->vars = ucx_map_new_a(mp->allocator, 16); + ctx->allocator = a; + ctx->vars = cxHashMapCreate(a, CX_STORE_POINTERS, 16); - void *document = ucx_mempool_calloc(mp, 1, size); - ucx_map_put(documents, ucx_key(&document, sizeof(void*)), ctx); + void *document = cxCalloc(a, 1, size); + cxMapPut(documents, cx_hash_key(&document, sizeof(void*)), ctx); return document; } @@ -95,7 +99,7 @@ UiContext* ui_document_context(void *doc) { if(doc) { - return ucx_map_get(documents, ucx_key(&doc, sizeof(void*))); + return cxMapGet(documents, cx_hash_key(&doc, sizeof(void*))); } else { return NULL; } diff -r 809581724cc7 -r 0358f1d9c506 ui/common/object.c --- a/ui/common/object.c Sat Apr 15 21:06:45 2023 +0200 +++ b/ui/common/object.c Mon May 22 16:17:26 2023 +0200 @@ -53,7 +53,7 @@ UiObject* uic_object_new(UiObject *toplevel, UIWIDGET widget) { UiContext *ctx = toplevel->ctx; - UiObject *newobj = ucx_mempool_calloc(ctx->mempool, 1, sizeof(UiObject)); + UiObject *newobj = cxCalloc(ctx->allocator, 1, sizeof(UiObject)); newobj->ctx = ctx; newobj->widget = widget; diff -r 809581724cc7 -r 0358f1d9c506 ui/common/objs.mk --- a/ui/common/objs.mk Sat Apr 15 21:06:45 2023 +0200 +++ b/ui/common/objs.mk Mon May 22 16:17:26 2023 +0200 @@ -34,6 +34,7 @@ COMMON_OBJ += object.o COMMON_OBJ += types.o COMMON_OBJ += properties.o +COMMON_OBJ += ucx_properties.o TOOLKITOBJS += $(COMMON_OBJ:%=$(COMMON_OBJPRE)%) TOOLKITSOURCE += $(COMMON_OBJ:%.o=common/%.c) diff -r 809581724cc7 -r 0358f1d9c506 ui/common/properties.c --- a/ui/common/properties.c Sat Apr 15 21:06:45 2023 +0200 +++ b/ui/common/properties.c Mon May 22 16:17:26 2023 +0200 @@ -33,12 +33,13 @@ #include #include "properties.h" -#include -#include -#include +#include +#include -static UiProperties *application_properties; -static UiProperties *language; +#include "ucx_properties.h" + +static CxMap *application_properties; +static CxMap *language; #ifndef UI_COCOA @@ -61,19 +62,20 @@ return NULL; } - UcxBuffer *buf = ucx_buffer_new(NULL, 128, UCX_BUFFER_AUTOEXTEND); + CxBuffer buf; + cxBufferInit(&buf, NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); // add base dir char *homeenv = getenv("HOME"); if(homeenv == NULL) { - ucx_buffer_free(buf); + cxBufferDestroy(&buf); return NULL; } - sstr_t home = sstr(homeenv); + cxstring home = cx_str(homeenv); - ucx_buffer_write(home.ptr, 1, home.length, buf); + cxBufferWrite(home.ptr, 1, home.length, &buf); if(home.ptr[home.length-1] != '/') { - ucx_buffer_putc(buf, '/'); + cxBufferPut(&buf, '/'); } #ifdef UI_COCOA @@ -81,19 +83,19 @@ ucx_buffer_puts(buf, "Library/Application Support/"); #else // app dir is $HOME/.$APPNAME/ - ucx_buffer_putc(buf, '.'); + cxBufferPut(&buf, '.'); #endif - ucx_buffer_puts(buf, appname); - ucx_buffer_putc(buf, '/'); + cxBufferPutString(&buf, appname); + cxBufferPut(&buf, '/'); // add file name if(name) { - ucx_buffer_puts(buf, name); + cxBufferPutString(&buf, name); } - char *path = buf->space; - free(buf); - return path; + cxBufferPut(&buf, 0); + + return buf.space; } static int ui_mkdir(char *path) { @@ -105,7 +107,7 @@ } void uic_load_app_properties() { - application_properties = ucx_map_new(128); + application_properties = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 128); if(!ui_appname()) { // applications without name cannot load app properties @@ -167,17 +169,17 @@ char* ui_get_property(char *name) { - return ucx_map_cstr_get(application_properties, name); + return cxMapGet(application_properties, name); } void ui_set_property(char *name, char *value) { - ucx_map_cstr_put(application_properties, name, value); + cxMapPut(application_properties, name, value); } void ui_set_default_property(char *name, char *value) { - char *v = ucx_map_cstr_get(application_properties, name); + char *v = cxMapGet(application_properties, name); if(!v) { - ucx_map_cstr_put(application_properties, name, value); + cxMapPut(application_properties, name, value); } } @@ -186,16 +188,16 @@ static char* uic_concat_path(const char *base, const char *p, const char *ext) { size_t baselen = strlen(base); - UcxBuffer *buf = ucx_buffer_new(NULL, 32, UCX_BUFFER_AUTOEXTEND); + CxBuffer *buf = cxBufferCreate(NULL, 32, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); if(baselen > 0) { - ucx_buffer_write(base, 1, baselen, buf); + cxBufferWrite(base, 1, baselen, buf); if(base[baselen - 1] != '/') { - ucx_buffer_putc(buf, '/'); + cxBufferPut(buf, '/'); } } - ucx_buffer_write(p, 1, strlen(p), buf); + cxBufferWrite(p, 1, strlen(p), buf); if(ext) { - ucx_buffer_write(ext, 1, strlen(ext), buf); + cxBufferWrite(ext, 1, strlen(ext), buf); } char *str = buf->space; @@ -263,7 +265,7 @@ #endif int uic_load_language_file(const char *path) { - UcxMap *lang = ucx_map_new(256); + CxMap *lang = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 256); FILE *file = fopen(path, "r"); if(!file) { @@ -276,7 +278,7 @@ fclose(file); - ucx_map_rehash(lang); + cxMapRehash(lang); language = lang; @@ -292,6 +294,6 @@ if(!language) { return NULL; } - return ucx_map_cstr_get(language, name); + return cxMapGet(language, name); } diff -r 809581724cc7 -r 0358f1d9c506 ui/common/properties.h --- a/ui/common/properties.h Sat Apr 15 21:06:45 2023 +0200 +++ b/ui/common/properties.h Mon May 22 16:17:26 2023 +0200 @@ -30,8 +30,8 @@ #define UIC_PROPERTIES_H #include "../ui/properties.h" -#include -#include +#include +#include #ifdef __cplusplus extern "C" { diff -r 809581724cc7 -r 0358f1d9c506 ui/common/types.c --- a/ui/common/types.c Sat Apr 15 21:06:45 2023 +0200 +++ b/ui/common/types.c Mon May 22 16:17:26 2023 +0200 @@ -31,7 +31,8 @@ #include #include -#include +#include +#include #include "../ui/tree.h" #include "types.h" #include "context.h" @@ -99,7 +100,7 @@ list->count = ui_list_count; list->observers = NULL; - list->data = NULL; + list->data = cxArrayListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS, 32); list->iter = NULL; list->update = NULL; @@ -113,54 +114,43 @@ } void ui_list_free(UiList *list) { - ucx_list_free(list->data); + cxListDestroy(list->data); free(list); } void* ui_list_first(UiList *list) { - UcxList *elm = list->data; - list->iter = elm; - return elm ? elm->data : NULL; + list->iter = (void*)(intptr_t)0; + return cxListAt(list->data, 0); } void* ui_list_next(UiList *list) { - UcxList *elm = list->iter; + intptr_t iter = (intptr_t)list->iter; + iter++; + void *elm = cxListAt(list->data, iter); if(elm) { - elm = elm->next; - if(elm) { - list->iter = elm; - return elm->data; - } + list->iter = (void*)iter; } - return NULL; + return elm; } void* ui_list_get(UiList *list, int i) { - UcxList *elm = ucx_list_get(list->data, i); - if(elm) { - list->iter = elm; - return elm->data; - } else { - return NULL; - } + return cxListAt(list->data, i); } int ui_list_count(UiList *list) { - UcxList *elm = list->data; - return (int)ucx_list_size(elm); + return ((CxList*)list->data)->size; } void ui_list_append(UiList *list, void *data) { - list->data = ucx_list_append(list->data, data); + cxListAdd(list->data, data); } void ui_list_prepend(UiList *list, void *data) { - list->data = ucx_list_prepend(list->data, data); + cxListInsert(list->data, 0, data); } void ui_list_clear(UiList *list) { - ucx_list_free(list->data); - list->data = NULL; + cxListClear(list->data); } void ui_list_addobsv(UiList *list, ui_callback f, void *data) { @@ -183,42 +173,41 @@ va_list ap; va_start(ap, ctx); - UcxList *cols = NULL; + CxList *cols = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(UiColumn), 32); int type; while((type = va_arg(ap, int)) != -1) { char *name = va_arg(ap, char*); - UiColumn *column = malloc(sizeof(UiColumn)); - column->type = type; - column->name = name; + UiColumn column; + column.type = type; + column.name = name; - cols = ucx_list_append(cols, column); + cxListAdd(cols, &column); } va_end(ap); - size_t len = ucx_list_size(cols); + size_t len = cols->size; info->columns = len; info->types = ui_calloc(ctx, len, sizeof(UiModelType)); info->titles = ui_calloc(ctx, len, sizeof(char*)); int i = 0; - UCX_FOREACH(elm, cols) { - UiColumn *c = elm->data; + CxIterator iter = cxListIterator(cols); + cx_foreach(UiColumn*, c, iter) { info->types[i] = c->type; info->titles[i] = c->name; - free(c); i++; } - ucx_list_free(cols); + cxListDestroy(cols); return info; } void ui_model_free(UiContext *ctx, UiModel *mi) { - ucx_mempool_free(ctx->mempool, mi->types); - ucx_mempool_free(ctx->mempool, mi->titles); - ucx_mempool_free(ctx->mempool, mi); + cxFree(ctx->allocator, mi->types); + cxFree(ctx->allocator, mi->titles); + cxFree(ctx->allocator, mi); } // types diff -r 809581724cc7 -r 0358f1d9c506 ui/common/ucx_properties.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/common/ucx_properties.c Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,263 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "ucx_properties.h" + +#include +#include +#include + +UcxProperties *ucx_properties_new() { + UcxProperties *parser = (UcxProperties*)malloc( + sizeof(UcxProperties)); + if(!parser) { + return NULL; + } + + parser->buffer = NULL; + parser->buflen = 0; + parser->pos = 0; + parser->tmp = NULL; + parser->tmplen = 0; + parser->tmpcap = 0; + parser->error = 0; + parser->delimiter = '='; + parser->comment1 = '#'; + parser->comment2 = 0; + parser->comment3 = 0; + + return parser; +} + +void ucx_properties_free(UcxProperties *parser) { + if(parser->tmp) { + free(parser->tmp); + } + free(parser); +} + +void ucx_properties_fill(UcxProperties *parser, char *buf, size_t len) { + parser->buffer = buf; + parser->buflen = len; + parser->pos = 0; +} + +static void parser_tmp_append(UcxProperties *parser, char *buf, size_t len) { + if(parser->tmpcap - parser->tmplen < len) { + size_t newcap = parser->tmpcap + len + 64; + parser->tmp = (char*)realloc(parser->tmp, newcap); + parser->tmpcap = newcap; + } + memcpy(parser->tmp + parser->tmplen, buf, len); + parser->tmplen += len; +} + +int ucx_properties_next(UcxProperties *parser, cxstring *name, cxstring *value) { + if(parser->tmplen > 0) { + char *buf = parser->buffer + parser->pos; + size_t len = parser->buflen - parser->pos; + 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); + // the tmp buffer contains exactly one line now + + char *orig_buf = parser->buffer; + size_t orig_len = parser->buflen; + + parser->buffer = parser->tmp; + parser->buflen = parser->tmplen; + parser->pos = 0; + parser->tmp = NULL; + parser->tmpcap = 0; + parser->tmplen = 0; + // run ucx_properties_next with the tmp buffer as main buffer + int ret = ucx_properties_next(parser, name, value); + + // restore original buffer + parser->tmp = parser->buffer; + parser->buffer = orig_buf; + parser->buflen = orig_len; + parser->pos = newlen; + + /* + * if ret == 0 the tmp buffer contained just space or a comment + * we parse again with the original buffer to get a name/value + * or a new tmp buffer + */ + return ret ? ret : ucx_properties_next(parser, name, value); + } else { + parser_tmp_append(parser, buf, len); + return 0; + } + } else if(parser->tmp) { + free(parser->tmp); + parser->tmp = NULL; + } + + char comment1 = parser->comment1; + char comment2 = parser->comment2; + char comment3 = parser->comment3; + char delimiter = parser->delimiter; + + // get one line and parse it + while(parser->pos < parser->buflen) { + char *buf = parser->buffer + parser->pos; + size_t len = parser->buflen - parser->pos; + + /* + * First we check if we have at least one line. We also get indices of + * delimiter and comment chars + */ + size_t delimiter_index = 0; + size_t comment_index = 0; + int has_comment = 0; + + size_t i = 0; + char c = 0; + for(;itmpcap = len + 128; + parser->tmp = (char*)malloc(parser->tmpcap); + parser->tmplen = len; + memcpy(parser->tmp, buf, len); + return 0; + } + + cxstring line = has_comment ? cx_strn(buf, comment_index) : cx_strn(buf, i); + // check line + if(delimiter_index == 0) { + line = cx_strtrim(line); + if(line.length != 0) { + parser->error = 1; + } + } else { + cxstring n = cx_strn(buf, delimiter_index); + cxstring v = cx_strn( + buf + delimiter_index + 1, + line.length - delimiter_index - 1); + n = cx_strtrim(n); + v = cx_strtrim(v); + if(n.length != 0 || v.length != 0) { + *name = n; + *value = v; + parser->pos += i + 1; + return 1; + } else { + parser->error = 1; + } + } + + parser->pos += i + 1; + } + + return 0; +} + +int ucx_properties2map(UcxProperties *parser, CxMap *map) { + cxstring name; + cxstring value; + while(ucx_properties_next(parser, &name, &value)) { + cxmutstr mutvalue = cx_strdup_a(map->allocator, value); + if(!mutvalue.ptr) { + return 1; + } + if(cxMapPut(map, cx_hash_key_cxstr(name), mutvalue.ptr)) { + cxFree(map->allocator, mutvalue.ptr); + return 1; + } + } + if (parser->error) { + return parser->error; + } else { + return 0; + } +} + +// buffer size is documented - change doc, when you change bufsize! +#define UCX_PROPLOAD_BUFSIZE 1024 +int ucx_properties_load(CxMap *map, FILE *file) { + UcxProperties *parser = ucx_properties_new(); + if(!(parser && map && file)) { + return 1; + } + + int error = 0; + size_t r; + char buf[UCX_PROPLOAD_BUFSIZE]; + while((r = fread(buf, 1, UCX_PROPLOAD_BUFSIZE, file)) != 0) { + ucx_properties_fill(parser, buf, r); + error = ucx_properties2map(parser, map); + if (error) { + break; + } + } + ucx_properties_free(parser); + return error; +} + +int ucx_properties_store(CxMap *map, FILE *file) { + CxIterator iter = cxMapIterator(map); + cxstring value; + size_t written; + + cx_foreach(CxMapEntry *, v, iter) { + value = cx_str(v->value); + + written = 0; + written += fwrite(v->key->data, 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 != v->key->len + value.length + 4) { + return 1; + } + } + + return 0; +} + diff -r 809581724cc7 -r 0358f1d9c506 ui/common/ucx_properties.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/common/ucx_properties.h Mon May 22 16:17:26 2023 +0200 @@ -0,0 +1,223 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * @file properties.h + * + * Load / store utilities for properties files. + * + * @author Mike Becker + * @author Olaf Wintermann + */ + +#ifndef UCX_PROPERTIES_H +#define UCX_PROPERTIES_H + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * UcxProperties object for parsing properties data. + * Most of the fields are for internal use only. You may configure the + * properties parser, e.g. by changing the used delimiter or specifying + * up to three different characters that shall introduce comments. + */ +typedef struct { + /** + * Input buffer (don't set manually). + * Automatically set by calls to ucx_properties_fill(). + */ + char *buffer; + + /** + * Length of the input buffer (don't set manually). + * Automatically set by calls to ucx_properties_fill(). + */ + size_t buflen; + + /** + * Current buffer position (don't set manually). + * Used by ucx_properties_next(). + */ + size_t pos; + + /** + * Internal temporary buffer (don't set manually). + * Used by ucx_properties_next(). + */ + char *tmp; + + /** + * Internal temporary buffer length (don't set manually). + * Used by ucx_properties_next(). + */ + size_t tmplen; + + /** + * Internal temporary buffer capacity (don't set manually). + * Used by ucx_properties_next(). + */ + size_t tmpcap; + + /** + * Parser error code. + * This is always 0 on success and a nonzero value on syntax errors. + * The value is set by ucx_properties_next(). + */ + int error; + + /** + * The delimiter that shall be used. + * This is '=' by default. + */ + char delimiter; + + /** + * The first comment character. + * This is '#' by default. + */ + char comment1; + + /** + * The second comment character. + * This is not set by default. + */ + char comment2; + + /** + * The third comment character. + * This is not set by default. + */ + char comment3; +} UcxProperties; + + +/** + * Constructs a new UcxProperties object. + * @return a pointer to the new UcxProperties object + */ +UcxProperties *ucx_properties_new(); + +/** + * Destroys a UcxProperties object. + * @param prop the UcxProperties object to destroy + */ +void ucx_properties_free(UcxProperties *prop); + +/** + * Sets the input buffer for the properties parser. + * + * After calling this function, you may parse the data by calling + * ucx_properties_next() until it returns 0. The function ucx_properties2map() + * is a convenience function that reads as much data as possible by using this + * function. + * + * + * @param prop the UcxProperties object + * @param buf a pointer to the new buffer + * @param len the payload length of the buffer + * @see ucx_properties_next() + * @see ucx_properties2map() + */ +void ucx_properties_fill(UcxProperties *prop, char *buf, size_t len); + +/** + * Retrieves the next key/value-pair. + * + * This function returns a nonzero value as long as there are key/value-pairs + * found. If no more key/value-pairs are found, you may refill the input buffer + * with ucx_properties_fill(). + * + * Attention: 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 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, cxstring *name, cxstring *value); + +/** + * Retrieves all available key/value-pairs and puts them into a UcxMap. + * + * This is done by successive calls to ucx_properties_next() until no more + * key/value-pairs can be retrieved. + * + * The memory for the map values is allocated by the map's own allocator. + * + * @param prop the UcxProperties object + * @param map the target map + * @return The UcxProperties.error code (i.e. 0 on success). + * @see ucx_properties_fill() + * @see UcxMap.allocator + */ +int ucx_properties2map(UcxProperties *prop, CxMap *map); + +/** + * Loads a properties file to a UcxMap. + * + * This is a convenience function that reads data from an input + * stream until the end of the stream is reached. + * + * @param map the map object to write the key/value-pairs to + * @param file the FILE* stream to read from + * @return 0 on success, or a non-zero value on error + * + * @see ucx_properties_fill() + * @see ucx_properties2map() + */ +int ucx_properties_load(CxMap *map, FILE *file); + +/** + * Stores a UcxMap to a file. + * + * The key/value-pairs are written by using the following format: + * + * [key] = [value]\\n + * + * @param map the map to store + * @param file the FILE* stream to write to + * @return 0 on success, or a non-zero value on error + */ +int ucx_properties_store(CxMap *map, FILE *file); + +#ifdef __cplusplus +} +#endif + +#endif /* UCX_PROPERTIES_H */ + diff -r 809581724cc7 -r 0358f1d9c506 ui/gtk/button.c --- a/ui/gtk/button.c Sat Apr 15 21:06:45 2023 +0200 +++ b/ui/gtk/button.c Mon May 22 16:17:26 2023 +0200 @@ -31,7 +31,7 @@ #include "button.h" #include "container.h" -#include +#include #include "../common/context.h" #include "../common/object.h" diff -r 809581724cc7 -r 0358f1d9c506 ui/gtk/container.c --- a/ui/gtk/container.c Sat Apr 15 21:06:45 2023 +0200 +++ b/ui/gtk/container.c Mon May 22 16:17:26 2023 +0200 @@ -69,8 +69,8 @@ /* -------------------- Frame Container (deprecated) -------------------- */ UiContainer* ui_frame_container(UiObject *obj, GtkWidget *frame) { - UiContainer *ct = ucx_mempool_calloc( - obj->ctx->mempool, + UiContainer *ct = cxCalloc( + obj->ctx->allocator, 1, sizeof(UiContainer)); ct->widget = frame; @@ -87,8 +87,8 @@ /* -------------------- Box Container -------------------- */ UiContainer* ui_box_container(UiObject *obj, GtkWidget *box) { - UiBoxContainer *ct = ucx_mempool_calloc( - obj->ctx->mempool, + UiBoxContainer *ct = cxCalloc( + obj->ctx->allocator, 1, sizeof(UiBoxContainer)); ct->container.widget = box; @@ -118,8 +118,8 @@ } UiContainer* ui_grid_container(UiObject *obj, GtkWidget *grid) { - UiGridContainer *ct = ucx_mempool_calloc( - obj->ctx->mempool, + UiGridContainer *ct = cxCalloc( + obj->ctx->allocator, 1, sizeof(UiGridContainer)); ct->container.widget = grid; @@ -202,8 +202,8 @@ #endif UiContainer* ui_scrolledwindow_container(UiObject *obj, GtkWidget *scrolledwindow) { - UiContainer *ct = ucx_mempool_calloc( - obj->ctx->mempool, + UiContainer *ct = cxCalloc( + obj->ctx->allocator, 1, sizeof(UiContainer)); ct->widget = scrolledwindow; @@ -223,8 +223,8 @@ } UiContainer* ui_tabview_container(UiObject *obj, GtkWidget *tabview) { - UiTabViewContainer *ct = ucx_mempool_calloc( - obj->ctx->mempool, + UiTabViewContainer *ct = cxCalloc( + obj->ctx->allocator, 1, sizeof(UiTabViewContainer)); ct->container.widget = tabview; @@ -408,8 +408,8 @@ if(max <= 0) max = INT_MAX; - UiPanedContainer *pctn = ucx_mempool_calloc( - obj->ctx->mempool, + UiPanedContainer *pctn = cxCalloc( + obj->ctx->allocator, 1, sizeof(UiPanedContainer)); pctn->container.widget = paned; @@ -482,8 +482,9 @@ left->container = ct1; UiObject *right = uic_object_new(obj, sidebar); - UiContainer *ct2 = ucx_mempool_malloc( - obj->ctx->mempool, + UiContainer *ct2 = cxCalloc( + obj->ctx->allocator, + 1, sizeof(UiContainer)); ct2->widget = paned; ct2->add = ui_split_container_add2; @@ -557,7 +558,7 @@ UiObject *tab = ui_malloc(view->ctx, sizeof(UiObject)); tab->widget = NULL; // initialization for uic_context() - tab->ctx = uic_context(tab, view->ctx->mempool); + tab->ctx = uic_context(tab, view->ctx->allocator); tab->ctx->parent = view->ctx; tab->ctx->attach_document = uic_context_attach_document; tab->ctx->detach_document2 = uic_context_detach_document2; diff -r 809581724cc7 -r 0358f1d9c506 ui/gtk/display.c --- a/ui/gtk/display.c Sat Apr 15 21:06:45 2023 +0200 +++ b/ui/gtk/display.c Mon May 22 16:17:26 2023 +0200 @@ -31,7 +31,6 @@ #include "display.h" #include "container.h" -#include #include "../common/context.h" #include "../common/object.h" diff -r 809581724cc7 -r 0358f1d9c506 ui/gtk/dnd.c --- a/ui/gtk/dnd.c Sat Apr 15 21:06:45 2023 +0200 +++ b/ui/gtk/dnd.c Mon May 22 16:17:26 2023 +0200 @@ -31,20 +31,20 @@ #include #include "dnd.h" -#include +#include #ifdef UI_GTK2LEGACY static gboolean selection_data_set_uris(GtkSelectionData *selection_data, char **uris) { - UcxBuffer *buf = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND); + CxBuffer *buf = cxBufferCreate(NULL, 1024, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); char *uri; int i = 0; while((uri = uris[i]) != NULL) { - ucx_buffer_puts(buf, uri); - ucx_buffer_puts(buf, "\r\n"); + cxBufferPutString(buf, uri); + cxBufferPutString(buf, "\r\n"); } GdkAtom type = gdk_atom_intern("text/uri-list", FALSE); gtk_selection_data_set(selection_data, type, 8, (guchar*)buf->space, buf->pos); - ucx_buffer_free(buf); + cxBufferFree(buf); return TRUE; } static char** selection_data_get_uris(GtkSelectionData *selection_data) { diff -r 809581724cc7 -r 0358f1d9c506 ui/gtk/entry.c --- a/ui/gtk/entry.c Sat Apr 15 21:06:45 2023 +0200 +++ b/ui/gtk/entry.c Mon May 22 16:17:26 2023 +0200 @@ -34,7 +34,6 @@ #include "container.h" #include "entry.h" -#include UIWIDGET ui_spinner(UiObject *obj, int step, UiInteger *i) { UiVar *var = malloc(sizeof(UiVar)); diff -r 809581724cc7 -r 0358f1d9c506 ui/gtk/image.c --- a/ui/gtk/image.c Sat Apr 15 21:06:45 2023 +0200 +++ b/ui/gtk/image.c Mon May 22 16:17:26 2023 +0200 @@ -29,18 +29,18 @@ #include #include #include -#include +#include #include "toolkit.h" #include "image.h" #include "../common/properties.h" -static UcxMap *image_map; +static CxMap *image_map; static GtkIconTheme *icon_theme; void ui_image_init(void) { - image_map = ucx_map_new(8); + image_map = cxHashMapCreateSimple(CX_STORE_POINTERS); icon_theme = gtk_icon_theme_get_default(); } @@ -48,7 +48,7 @@ // **** deprecated functions **** GdkPixbuf* ui_get_image(const char *name) { - UiImage *img = ucx_map_cstr_get(image_map, name); + UiImage *img = cxMapGet(image_map, name); if(img) { return img->pixbuf; } else { @@ -125,7 +125,7 @@ UiImage *img = malloc(sizeof(UiImage)); img->pixbuf = pixbuf; if(name) { - ucx_map_cstr_put(image_map, name, img); + cxMapPut(image_map, name, img); } return img; } diff -r 809581724cc7 -r 0358f1d9c506 ui/gtk/menu.c --- a/ui/gtk/menu.c Sat Apr 15 21:06:45 2023 +0200 +++ b/ui/gtk/menu.c Mon May 22 16:17:26 2023 +0200 @@ -38,48 +38,78 @@ #include "../ui/window.h" #include "container.h" -static UcxList *menus; -static UcxList *current; +#include +#include + +static UiMenu *menus_begin; +static UiMenu *menus_end; +static CxList *current; + +static void add_menu(UiMenu *menu) { + cx_linked_list_add( + (void**)&menus_begin, + (void**)&menus_end, + offsetof(UiMenu, item.prev), + offsetof(UiMenu, item.next), + menu); +} + +static void add_item(UiMenuItemI *item) { + UiMenu *menu = cxListAt(current, 0); + cx_linked_list_add( + (void**)&menu->items_begin, + (void**)&menu->items_end, + offsetof(UiMenu, item.prev), + offsetof(UiMenu, item.next), + item); +} void ui_menu(char *label) { - // free current menu hierarchy - ucx_list_free(current); + if(!current) { + current = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS); + } else { + // free current menu hierarchy + cxListClear(current); + } // create menu UiMenu *menu = malloc(sizeof(UiMenu)); + menu->item.prev = NULL; + menu->item.next = NULL; menu->item.add_to = (ui_menu_add_f)add_menu_widget; - menu->label = label; - menu->items = NULL; - menu->parent = NULL; + menu->label = label; + menu->items_begin = NULL; + menu->items_end = NULL; + menu->parent = NULL; - current = ucx_list_prepend(NULL, menu); - menus = ucx_list_append(menus, menu); - + add_menu(menu); + cxListAdd(current, menu); } void ui_submenu(char *label) { UiMenu *menu = malloc(sizeof(UiMenu)); + menu->item.prev = NULL; + menu->item.next = NULL; menu->item.add_to = (ui_menu_add_f)add_menu_widget; - menu->label = label; - menu->items = NULL; - menu->parent = NULL; + menu->label = label; + menu->items_begin = NULL; + menu->items_end = NULL; + menu->parent = NULL; // add submenu to current menu - UiMenu *cm = current->data; - cm->items = ucx_list_append(cm->items, menu); + add_item((UiMenuItemI*)menu); // set the submenu to current menu - current = ucx_list_prepend(current, menu); + cxListInsert(current, 0, menu); } void ui_submenu_end() { - if(ucx_list_size(current) < 2) { + if(current->size < 2) { return; } - current = ucx_list_remove(current, current); - //UcxList *c = current; + cxListRemove(current, 0); } void ui_menuitem(char *label, ui_callback f, void *userdata) { @@ -96,6 +126,8 @@ } UiMenuItem *item = malloc(sizeof(UiMenuItem)); + item->item.prev = NULL; + item->item.next = NULL; item->item.add_to = (ui_menu_add_f)add_menuitem_widget; item->label = label; @@ -108,12 +140,14 @@ va_start(ap, userdata); int group; while((group = va_arg(ap, int)) != -1) { - item->groups = ucx_list_append(item->groups, (void*)(intptr_t)group); + if(!item->groups) { + item->groups = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), 8); + } + cxListAdd(item->groups, &group); } va_end(ap); - UiMenu *cm = current->data; - cm->items = ucx_list_append(cm->items, item); + add_item((UiMenuItemI*)item); } void ui_menuitem_stgr(char *stockid, ui_callback f, void *userdata, ...) { @@ -122,6 +156,8 @@ } UiStMenuItem *item = malloc(sizeof(UiStMenuItem)); + item->item.prev = NULL; + item->item.next = NULL; item->item.add_to = (ui_menu_add_f)add_menuitem_st_widget; item->stockid = stockid; @@ -134,12 +170,14 @@ va_start(ap, userdata); int group; while((group = va_arg(ap, int)) != -1) { - item->groups = ucx_list_append(item->groups, (void*)(intptr_t)group); + if(!item->groups) { + item->groups = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), 8); + } + cxListAdd(item->groups, &group); } va_end(ap); - UiMenu *cm = current->data; - cm->items = ucx_list_append(cm->items, item); + add_item((UiMenuItemI*)item); } void ui_menuseparator() { @@ -148,10 +186,11 @@ } UiMenuItemI *item = malloc(sizeof(UiMenuItemI)); + item->prev = NULL; + item->next = NULL; item->add_to = (ui_menu_add_f)add_menuseparator_widget; - UiMenu *cm = current->data; - cm->items = ucx_list_append(cm->items, item); + add_item((UiMenuItemI*)item); } void ui_checkitem(char *label, ui_callback f, void *userdata) { @@ -160,13 +199,14 @@ } UiCheckItem *item = malloc(sizeof(UiCheckItem)); + item->item.prev = NULL; + item->item.next = NULL; item->item.add_to = (ui_menu_add_f)add_checkitem_widget; item->label = label; item->callback = f; item->userdata = userdata; - UiMenu *cm = current->data; - cm->items = ucx_list_append(cm->items, item); + add_item((UiMenuItemI*)item); } void ui_checkitem_nv(char *label, char *vname) { @@ -175,12 +215,13 @@ } UiCheckItemNV *item = malloc(sizeof(UiCheckItemNV)); + item->item.prev = NULL; + item->item.next = NULL; item->item.add_to = (ui_menu_add_f)add_checkitemnv_widget; item->varname = vname; item->label = label; - UiMenu *cm = current->data; - cm->items = ucx_list_append(cm->items, item); + add_item((UiMenuItemI*)item); } void ui_menuitem_list(UiList *items, ui_callback f, void *userdata) { @@ -189,29 +230,30 @@ } UiMenuItemList *item = malloc(sizeof(UiMenuItemList)); + item->item.prev = NULL; + item->item.next = NULL; item->item.add_to = (ui_menu_add_f)add_menuitem_list_widget; item->callback = f; item->userdata = userdata; item->list = items; - UiMenu *cm = current->data; - cm->items = ucx_list_append(cm->items, item); + add_item((UiMenuItemI*)item); } // private menu functions GtkWidget *ui_create_menubar(UiObject *obj) { - if(menus == NULL) { + if(menus_begin == NULL) { return NULL; } GtkWidget *mb = gtk_menu_bar_new(); - UcxList *ls = menus; + UiMenu *ls = menus_begin; while(ls) { - UiMenu *menu = ls->data; + UiMenu *menu = ls; menu->item.add_to(mb, 0, &menu->item, obj); - ls = ls->next; + ls = (UiMenu*)ls->item.next; } return mb; @@ -224,13 +266,12 @@ GtkWidget *menu_item = gtk_menu_item_new_with_mnemonic(menu->label); gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), menu_widget); - UcxList *ls = menu->items; + UiMenuItemI *it = menu->items_begin; int index = 0; - while(ls) { - UiMenuItemI *i = ls->data; - i->add_to(menu_widget, index, i, obj); + while(it) { + it->add_to(menu_widget, index, it, obj); - ls = ls->next; + it = it->next; index++; } @@ -360,10 +401,10 @@ void add_menuitem_list_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject *obj) { UiMenuItemList *il = (UiMenuItemList*)item; - UcxMempool *mp = obj->ctx->mempool; + const CxAllocator *a = obj->ctx->allocator; - UiActiveMenuItemList *ls = ucx_mempool_malloc( - mp, + UiActiveMenuItemList *ls = cxMalloc( + a, sizeof(UiActiveMenuItemList)); ls->object = obj; @@ -519,12 +560,15 @@ } // add groups - UcxList *groups = NULL; + CxList *groups = NULL; va_list ap; va_start(ap, userdata); int group; while((group = va_arg(ap, int)) != -1) { - ucx_list_append(groups, (void*)(intptr_t)group); + if(!groups) { + groups = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), 16); + } + cxListAdd(groups, &group); } va_end(ap); @@ -555,6 +599,7 @@ if(groups) { uic_add_group_widget(obj->ctx, widget, (ui_enablefunc)ui_set_enabled, groups); + cxListDestroy(groups); } } @@ -569,12 +614,15 @@ } // add groups - UcxList *groups = NULL; + CxList *groups = NULL; va_list ap; va_start(ap, userdata); int group; while((group = va_arg(ap, int)) != -1) { - ucx_list_append(groups, (void*)(intptr_t)group); + if(!groups) { + groups = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), 16); + } + cxListAdd(groups, &group); } va_end(ap); @@ -605,5 +653,6 @@ if(groups) { uic_add_group_widget(obj->ctx, widget, (ui_enablefunc)ui_set_enabled, groups); + cxListDestroy(groups); } } diff -r 809581724cc7 -r 0358f1d9c506 ui/gtk/menu.h --- a/ui/gtk/menu.h Sat Apr 15 21:06:45 2023 +0200 +++ b/ui/gtk/menu.h Mon May 22 16:17:26 2023 +0200 @@ -30,7 +30,7 @@ #define MENU_H #include "../ui/menu.h" -#include +#include #include "toolkit.h" #ifdef __cplusplus @@ -50,13 +50,16 @@ typedef GtkWidget*(*ui_menu_add_f)(GtkWidget *, int, UiMenuItemI*, UiObject*); struct UiMenuItemI { + UiMenuItemI *prev; + UiMenuItemI *next; ui_menu_add_f add_to; }; struct UiMenu { UiMenuItemI item; char *label; - UcxList *items; + UiMenuItemI *items_begin; + UiMenuItemI *items_end; UiMenu *parent; }; @@ -65,7 +68,7 @@ ui_callback callback; char *label; void *userdata; - UcxList *groups; + CxList *groups; }; struct UiStMenuItem { @@ -73,7 +76,7 @@ ui_callback callback; char *stockid; void *userdata; - UcxList *groups; + CxList *groups; }; struct UiCheckItem { diff -r 809581724cc7 -r 0358f1d9c506 ui/gtk/range.c --- a/ui/gtk/range.c Sat Apr 15 21:06:45 2023 +0200 +++ b/ui/gtk/range.c Mon May 22 16:17:26 2023 +0200 @@ -31,7 +31,6 @@ #include "range.h" #include "container.h" -#include #include "../common/context.h" #include "../common/object.h" diff -r 809581724cc7 -r 0358f1d9c506 ui/gtk/text.c --- a/ui/gtk/text.c Sat Apr 15 21:06:45 2023 +0200 +++ b/ui/gtk/text.c Mon May 22 16:17:26 2023 +0200 @@ -34,6 +34,8 @@ #include "container.h" +#include "../common/types.h" + static void selection_handler( GtkTextBuffer *buf, GtkTextIter *location, @@ -304,19 +306,19 @@ } if(mgr->cur) { - UcxList *elm = mgr->cur->next; + UiTextBufOp *elm = mgr->cur->next; if(elm) { mgr->cur->next = NULL; + mgr->end = mgr->cur; while(elm) { elm->prev = NULL; - UcxList *next = elm->next; - ui_free_textbuf_op(elm->data); - free(elm); + UiTextBufOp *next = elm->next; + ui_free_textbuf_op(elm); elm = next; } } - UiTextBufOp *last_op = mgr->cur->data; + UiTextBufOp *last_op = mgr->cur; if( last_op->type == UI_TEXTBUF_INSERT && ui_check_insertstr(last_op->text, last_op->len, text, length) == 0) @@ -341,15 +343,22 @@ dpstr[length] = 0; UiTextBufOp *op = malloc(sizeof(UiTextBufOp)); + op->prev = NULL; + op->next = NULL; op->type = UI_TEXTBUF_INSERT; op->start = gtk_text_iter_get_offset(location); op->end = op->start+length; op->len = length; op->text = dpstr; - UcxList *elm = ucx_list_append(NULL, op); - mgr->cur = elm; - mgr->begin = ucx_list_concat(mgr->begin, elm); + cx_linked_list_add( + (void**)&mgr->begin, + (void**)&mgr->end, + offsetof(UiTextBufOp, prev), + offsetof(UiTextBufOp, next), + op); + + mgr->cur = op; } void ui_textbuf_delete( @@ -369,14 +378,14 @@ } if(mgr->cur) { - UcxList *elm = mgr->cur->next; + UiTextBufOp *elm = mgr->cur->next; if(elm) { mgr->cur->next = NULL; + mgr->end = mgr->cur; while(elm) { elm->prev = NULL; - UcxList *next = elm->next; - ui_free_textbuf_op(elm->data); - free(elm); + UiTextBufOp *next = elm->next; + ui_free_textbuf_op(elm); elm = next; } } @@ -385,6 +394,8 @@ char *text = gtk_text_buffer_get_text(value->obj, start, end, FALSE); UiTextBufOp *op = malloc(sizeof(UiTextBufOp)); + op->prev = NULL; + op->next = NULL; op->type = UI_TEXTBUF_DELETE; op->start = gtk_text_iter_get_offset(start); op->end = gtk_text_iter_get_offset(end); @@ -395,20 +406,39 @@ dpstr[op->len] = 0; op->text = dpstr; - UcxList *elm = ucx_list_append(NULL, op); - mgr->cur = elm; - mgr->begin = ucx_list_concat(mgr->begin, elm); + cx_linked_list_add( + (void**)&mgr->begin, + (void**)&mgr->end, + offsetof(UiTextBufOp, prev), + offsetof(UiTextBufOp, next), + op); + + mgr->cur = op; } UiUndoMgr* ui_create_undomgr() { UiUndoMgr *mgr = malloc(sizeof(UiUndoMgr)); mgr->begin = NULL; + mgr->end = NULL; mgr->cur = NULL; mgr->length = 0; mgr->event = 1; return mgr; } +void ui_destroy_undomgr(UiUndoMgr *mgr) { + UiTextBufOp *op = mgr->begin; + while(op) { + UiTextBufOp *nextOp = op->next; + if(op->text) { + free(op->text); + } + free(op); + op = nextOp; + } + free(mgr); +} + void ui_free_textbuf_op(UiTextBufOp *op) { if(op->text) { free(op->text); @@ -440,7 +470,7 @@ UiUndoMgr *mgr = value->undomgr; if(mgr->cur) { - UiTextBufOp *op = mgr->cur->data; + UiTextBufOp *op = mgr->cur; mgr->event = 0; switch(op->type) { case UI_TEXTBUF_INSERT: { @@ -468,7 +498,7 @@ void ui_text_redo(UiText *value) { UiUndoMgr *mgr = value->undomgr; - UcxList *elm = NULL; + UiTextBufOp *elm = NULL; if(mgr->cur) { if(mgr->cur->next) { elm = mgr->cur->next; @@ -478,7 +508,7 @@ } if(elm) { - UiTextBufOp *op = elm->data; + UiTextBufOp *op = elm; mgr->event = 0; switch(op->type) { case UI_TEXTBUF_INSERT: { @@ -580,6 +610,10 @@ void ui_textfield_destroy(GtkWidget *object, UiTextField *textfield) { if(textfield->var) { + UiText *text = textfield->var->value; + if(text->undomgr) { + ui_destroy_undomgr(text->undomgr); + } ui_destroy_boundvar(textfield->ctx, textfield->var); } free(textfield); diff -r 809581724cc7 -r 0358f1d9c506 ui/gtk/text.h --- a/ui/gtk/text.h Sat Apr 15 21:06:45 2023 +0200 +++ b/ui/gtk/text.h Mon May 22 16:17:26 2023 +0200 @@ -31,7 +31,7 @@ #include "../ui/text.h" #include "toolkit.h" -#include +#include #include "../common/context.h" #ifdef __cplusplus @@ -40,19 +40,24 @@ #define UI_TEXTBUF_INSERT 0 #define UI_TEXTBUF_DELETE 1 -typedef struct UiTextBufOp { + +typedef struct UiTextBufOp UiTextBufOp; +struct UiTextBufOp { + UiTextBufOp *prev; + UiTextBufOp *next; int type; // UI_TEXTBUF_INSERT, UI_TEXTBUF_DELETE int start; int end; int len; char *text; -} UiTextBufOp; +}; typedef struct UiUndoMgr { - UcxList *begin; - UcxList *cur; - int length; - int event; + UiTextBufOp *begin; + UiTextBufOp *end; + UiTextBufOp *cur; + int length; + int event; } UiUndoMgr; typedef struct UiTextArea { @@ -95,6 +100,7 @@ GtkTextIter *end, void *data); UiUndoMgr* ui_create_undomgr(); +void ui_destroy_undomgr(UiUndoMgr *mgr); void ui_free_textbuf_op(UiTextBufOp *op); int ui_check_insertstr(char *oldstr, int oldlen, char *newstr, int newlen); diff -r 809581724cc7 -r 0358f1d9c506 ui/gtk/toolbar.c --- a/ui/gtk/toolbar.c Sat Apr 15 21:06:45 2023 +0200 +++ b/ui/gtk/toolbar.c Mon May 22 16:17:26 2023 +0200 @@ -34,14 +34,18 @@ #include "button.h" #include "image.h" #include "tree.h" -#include +#include +#include +#include +#include #include "../common/context.h" -static UcxMap *toolbar_items; -static UcxList *defaults; +static CxMap *toolbar_items; +static CxList *defaults; void ui_toolbar_init() { - toolbar_items = ucx_map_new(16); + toolbar_items = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); + defaults = cxLinkedListCreateSimple(CX_STORE_POINTERS); } void ui_toolitem(char *name, char *label, ui_callback f, void *udata) { @@ -80,7 +84,7 @@ item->isimportant = 0; item->groups = NULL; - ucx_map_cstr_put(toolbar_items, name, item); + cxMapPut(toolbar_items, name, item); } void ui_toolitem_vstgr( @@ -102,10 +106,13 @@ // add groups int group; while((group = va_arg(ap, int)) != -1) { - item->groups = ucx_list_append(item->groups, (void*)(intptr_t)group); + if(!item->groups) { + item->groups = cxArrayListCreateSimple(sizeof(int), 16); + } + cxListAdd(item->groups, &group); } - ucx_map_cstr_put(toolbar_items, name, item); + cxMapPut(toolbar_items, name, item); } void ui_toolitem_toggle(const char *name, const char *label, const char *img, UiInteger *i) { @@ -119,7 +126,7 @@ item->value = i; item->var = NULL; - ucx_map_cstr_put(toolbar_items, name, item); + cxMapPut(toolbar_items, name, item); } void ui_toolitem_toggle_st(const char *name, const char *stockid, UiInteger *i) { @@ -133,7 +140,7 @@ item->value = i; item->var = NULL; - ucx_map_cstr_put(toolbar_items, name, item); + cxMapPut(toolbar_items, name, item); } void ui_toolitem_toggle_nv(const char *name, const char *label, const char *img, const char *intvar) { @@ -147,7 +154,7 @@ item->value = NULL; item->var = intvar; - ucx_map_cstr_put(toolbar_items, name, item); + cxMapPut(toolbar_items, name, item); } void ui_toolitem_toggle_stnv(const char *name, const char *stockid, const char *intvar) { @@ -161,7 +168,7 @@ item->value = NULL; item->var = intvar; - ucx_map_cstr_put(toolbar_items, name, item); + cxMapPut(toolbar_items, name, item); } @@ -184,7 +191,7 @@ cb->callback = f; cb->userdata = udata; - ucx_map_cstr_put(toolbar_items, name, cb); + cxMapPut(toolbar_items, name, cb); } void ui_toolbar_combobox_str( @@ -210,13 +217,13 @@ cb->callback = f; cb->userdata = udata; - ucx_map_cstr_put(toolbar_items, name, cb); + cxMapPut(toolbar_items, name, cb); } void ui_toolbar_add_default(char *name) { char *s = strdup(name); - defaults = ucx_list_append(defaults, s); + cxListAdd(defaults, s); } GtkWidget* ui_create_toolbar(UiObject *obj) { @@ -232,14 +239,15 @@ #endif GtkToolbar *tb = GTK_TOOLBAR(toolbar); - UCX_FOREACH(elm, defaults) { - UiToolItemI *item = ucx_map_cstr_get(toolbar_items, elm->data); + CxIterator i = cxListIterator(defaults); + cx_foreach(char *, def, i) { + UiToolItemI *item = cxMapGet(toolbar_items, def); if(item) { item->add_to(tb, item, obj); - } else if(!strcmp(elm->data, "@separator")) { + } else if(!strcmp(def, "@separator")) { gtk_toolbar_insert(tb, gtk_separator_tool_item_new(), -1); } else { - fprintf(stderr, "UI Error: Unknown toolbar item: %s\n", elm->data); + fprintf(stderr, "UI Error: Unknown toolbar item: %s\n", def); } } @@ -258,8 +266,8 @@ } if(item->callback) { - UiEventData *event = ucx_mempool_malloc( - obj->ctx->mempool, + UiEventData *event = cxMalloc( + obj->ctx->allocator, sizeof(UiEventData)); event->obj = obj; event->userdata = item->userdata; @@ -287,8 +295,8 @@ } if(item->callback) { - UiEventData *event = ucx_mempool_malloc( - obj->ctx->mempool, + UiEventData *event = cxMalloc( + obj->ctx->allocator, sizeof(UiEventData)); event->obj = obj; event->userdata = item->userdata; @@ -349,8 +357,8 @@ // register event // the event func will call the UiInteger observer callbacks - UiEventData *event = ucx_mempool_malloc( - obj->ctx->mempool, + UiEventData *event = cxMalloc( + obj->ctx->allocator, sizeof(UiEventData)); event->obj = obj; event->userdata = var; diff -r 809581724cc7 -r 0358f1d9c506 ui/gtk/toolbar.h --- a/ui/gtk/toolbar.h Sat Apr 15 21:06:45 2023 +0200 +++ b/ui/gtk/toolbar.h Mon May 22 16:17:26 2023 +0200 @@ -30,8 +30,8 @@ #define TOOLBAR_H #include "../ui/toolbar.h" -#include -#include +#include +#include #include "model.h" #include "tree.h" @@ -61,7 +61,7 @@ ui_callback callback; void *userdata; const char *varname; - UcxList *groups; + CxList *groups; int isimportant; }; @@ -71,7 +71,7 @@ ui_callback callback; void *userdata; const char *varname; - UcxList *groups; + CxList *groups; int isimportant; }; @@ -82,7 +82,7 @@ const char *stockid; UiInteger *value; const char *var; - UcxList *groups; + CxList *groups; int isimportant; }; diff -r 809581724cc7 -r 0358f1d9c506 ui/gtk/toolkit.c --- a/ui/gtk/toolkit.c Sat Apr 15 21:06:45 2023 +0200 +++ b/ui/gtk/toolkit.c Mon May 22 16:17:26 2023 +0200 @@ -38,7 +38,9 @@ #include "../common/document.h" #include "../common/properties.h" -#include +#include +#include +#include #include @@ -118,7 +120,7 @@ void ui_main() { #ifndef UI_GTK2 - sstr_t appid = ucx_sprintf( + cxmutstr appid = cx_asprintf( "ui.%s", application_name ? application_name : "application1"); @@ -246,10 +248,14 @@ } void ui_destroy_boundvar(UiContext *ctx, UiVar *var) { + uic_unbind_var(var); + if(var->type == UI_VAR_SPECIAL) { free(var); } else { - uic_remove_bound_var(ctx, var); + ui_free(var->from_ctx, var); + // TODO: free or unbound + //uic_remove_bound_var(ctx, var); } } diff -r 809581724cc7 -r 0358f1d9c506 ui/gtk/window.c --- a/ui/gtk/window.c Sat Apr 15 21:06:45 2023 +0200 +++ b/ui/gtk/window.c Mon May 22 16:17:26 2023 +0200 @@ -34,6 +34,8 @@ #include "../ui/properties.h" #include "../common/context.h" +#include + #include "menu.h" #include "toolbar.h" #include "container.h" @@ -66,8 +68,8 @@ } static UiObject* create_window(char *title, void *window_data, UiBool simple) { - UcxMempool *mp = ucx_mempool_new(256); - UiObject *obj = ucx_mempool_calloc(mp, 1, sizeof(UiObject)); + CxMempool *mp = cxBasicMempoolCreate(256); + UiObject *obj = cxCalloc(mp->allocator, 1, sizeof(UiObject)); #ifndef UI_GTK2 obj->widget = gtk_application_window_new(ui_get_application()); @@ -76,7 +78,7 @@ #endif - obj->ctx = uic_context(obj, mp); + obj->ctx = uic_context(obj, mp->allocator); obj->window = window_data; if(title != NULL) { diff -r 809581724cc7 -r 0358f1d9c506 ui/ui/properties.h --- a/ui/ui/properties.h Sat Apr 15 21:06:45 2023 +0200 +++ b/ui/ui/properties.h Mon May 22 16:17:26 2023 +0200 @@ -35,7 +35,7 @@ extern "C" { #endif -typedef struct UcxMap UiProperties; +typedef struct CxMap UiProperties; char* ui_getappdir(); char* ui_configfile(char *name);