commit 062056f66005d4be404a58b6960b5bcce9f92678 Author: Alex Denes Date: Tue Aug 10 18:04:03 2021 +0000 Initial commit after repository separation diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..45cd163 --- /dev/null +++ b/Makefile @@ -0,0 +1,60 @@ +include config.mk + +ifeq (${CC},clang) + +CFLAGS :=\ + -std=c99\ + -Weverything\ + -Wno-padded\ + -Wno-disabled-macro-expansion\ + -pedantic\ + ${CFLAGS} + +else ifeq (${CC},gcc) + +CFLAGS :=\ + -std=c99\ + -Wall\ + -Wextra\ + -Wformat-overflow=2\ + -Wformat-security\ + -Winit-self\ + -Wstrict-overflow=2\ + -Wstringop-overflow=2\ + -Walloc-zero\ + -Wduplicated-branches\ + -Wduplicated-cond\ + -Wtrampolines\ + -Wfloat-equal\ + -Wshadow\ + -Wunsafe-loop-optimizations\ + -Wparentheses\ + -pedantic\ + ${CFLAGS} + +endif + +SRC = dynarray.c +HDR = dynarray.h +LIB = dynarray.a +OBJ = ${SRC:.c=.o} + +all: ${LIB} + +.c.o: + ${CC} -c ${CFLAGS} $< + +${LIB}: ${OBJ} + ${AR} rc $@ $? + ${RANLIB} $@ + +clean: + rm -f ${LIB} ${OBJ} + +install: all + mkdir -p "${DESTDIR}${PREFIX}/lib/corelibs" + cp -f ${LIB} "${DESTDIR}${PREFIX}/lib/corelibs" + mkdir -p "${DESTDIR}${PREFIX}/include/corelibs" + cp -f ${HDR} "${DESTDIR}${PREFIX}/include/corelibs" + +.PHONY: all clean install diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..2b512d9 --- /dev/null +++ b/config.mk @@ -0,0 +1,7 @@ +VERSION = 1.1 + +PREFIX = /usr/local + +CC = clang +AR = llvm-ar +RANLIB = llvm-ranlib diff --git a/dynarray.c b/dynarray.c new file mode 100644 index 0000000..d9ab70b --- /dev/null +++ b/dynarray.c @@ -0,0 +1,526 @@ +/* + * This file is part of corelibs. (https://git.redxen.eu/caskd/corelibs) + * Copyright (c) 2021 Alex-David Denes + * + * corelibs is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * corelibs is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with corelibs. If not, see . + */ + +#include "dynarray.h" + +#include // bool +#include // malloc() free() +#include // memcpy() + +// Exporter +static dynarray_err corelibs_dynarray_export_slice(const dynarray_t*, uintmax_t, uintmax_t, void*); + +// Lifetime +static dynarray_err corelibs_dynarray_make_new(size_t, dynarray_t**); +static dynarray_err corelibs_dynarray_make_slice(const dynarray_t*, uintmax_t, uintmax_t, dynarray_t**); +static dynarray_err corelibs_dynarray_free(dynarray_t*); + +// Array storage +static dynarray_err corelibs_dynarray_mod_arr_cap(dynarray_t*, uintmax_t); +static dynarray_err corelibs_dynarray_mod_arr_lock(dynarray_t*, bool); + +// Array contents +static dynarray_err corelibs_dynarray_mod_ct_app(dynarray_t*, uintmax_t, const void*); +static dynarray_err corelibs_dynarray_mod_ct_ins(dynarray_t*, uintmax_t, uintmax_t, const void*); +static dynarray_err corelibs_dynarray_mod_ct_rep(dynarray_t*, uintmax_t, uintmax_t, const void*); +static dynarray_err corelibs_dynarray_mod_ct_rm(dynarray_t*, uintmax_t, uintmax_t); + +// Get array properties +static dynarray_err corelibs_dynarray_get_len(const dynarray_t*, uintmax_t*); +static dynarray_err corelibs_dynarray_get_size(const dynarray_t*, size_t*); +static dynarray_err corelibs_dynarray_get_cap_len(const dynarray_t*, uintmax_t*); +static dynarray_err corelibs_dynarray_get_cap_lock(const dynarray_t*, bool*); + +// Compare arrays +static dynarray_err corelibs_dynarray_cmp_data(const dynarray_t* a, const dynarray_t* b, bool* eq); + +// Private functions +static dynarray_err corelibs_dynarray_bcheck(const dynarray_t*, uintmax_t, uintmax_t); + +struct dynarray_t { + void* addr; // Location of element + uintmax_t len; // Element count + struct { + bool lock; // Is the capacity frozen? + uintmax_t len; // Capacity + } cap; + size_t es; // Size of element +}; + +enum { // Avoid collision in error numbers + CORELIBS_DYNARRAY_ERR_NCOMPAT = -7, + CORELIBS_DYNARRAY_ERR_INVAL, + CORELIBS_DYNARRAY_ERR_IMMUT, + CORELIBS_DYNARRAY_ERR_UNDEF, + CORELIBS_DYNARRAY_ERR_OOB, + CORELIBS_DYNARRAY_ERR_NULL, + CORELIBS_DYNARRAY_ERR_ALLOC, + CORELIBS_DYNARRAY_ERR_UNK = 0, + CORELIBS_DYNARRAY_ERR_OK = 1, +}; + +// Present a unified structure that may receive changes, deprecations and renames without hassle for external users +const struct corelibs_dynarray_interface dynarray = { + .make = { + .new = corelibs_dynarray_make_new, + .slice = corelibs_dynarray_make_slice, + }, + .free = corelibs_dynarray_free, + .mod = { + .arr = { + .cap = corelibs_dynarray_mod_arr_cap, + .lock = corelibs_dynarray_mod_arr_lock, + }, + .ct = { + .app = corelibs_dynarray_mod_ct_app, + .ins = corelibs_dynarray_mod_ct_ins, + .rep = corelibs_dynarray_mod_ct_rep, + .rm = corelibs_dynarray_mod_ct_rm, + }, + }, + .cmp = { + .data = corelibs_dynarray_cmp_data, + }, + .export = { + .slice = corelibs_dynarray_export_slice, + }, + .get = { + .len = corelibs_dynarray_get_len, + .size = corelibs_dynarray_get_size, + .cap = { + .len = corelibs_dynarray_get_cap_len, + .lock = corelibs_dynarray_get_cap_lock, + }, + }, + .err = { + .ok = CORELIBS_DYNARRAY_ERR_OK, + .unknown = CORELIBS_DYNARRAY_ERR_UNK, + .mem = { + .alloc = CORELIBS_DYNARRAY_ERR_ALLOC, + .null = CORELIBS_DYNARRAY_ERR_NULL, + .oob = CORELIBS_DYNARRAY_ERR_OOB, + }, + .var = { + .undef = CORELIBS_DYNARRAY_ERR_UNDEF, + .immut = CORELIBS_DYNARRAY_ERR_IMMUT, + .inval = CORELIBS_DYNARRAY_ERR_INVAL, + .ncompat = CORELIBS_DYNARRAY_ERR_NCOMPAT, + }, + }, +}; + +static dynarray_err +corelibs_dynarray_make_new(size_t sbyte, dynarray_t** ptr) +{ + dynarray_err err = CORELIBS_DYNARRAY_ERR_OK; + + if (ptr == NULL) { + err = CORELIBS_DYNARRAY_ERR_NULL; + goto ret; + } + + dynarray_t* new = malloc(sizeof(*new)); + if (new == NULL) { + err = CORELIBS_DYNARRAY_ERR_ALLOC; + goto ret; + } + + new->addr = NULL; + new->es = sbyte; + new->len = 0; + new->cap.len = 0; + new->cap.lock = false; + + *ptr = new; +ret: + return err; +} + +static dynarray_err +corelibs_dynarray_make_slice(const dynarray_t* arr, uintmax_t pos, uintmax_t cnt, dynarray_t** save) +{ + dynarray_err err = CORELIBS_DYNARRAY_ERR_OK; + + if (save == NULL || arr == NULL) { + err = CORELIBS_DYNARRAY_ERR_NULL; + goto ret; + } + + if (cnt == 0) { + // Don't copy anything, just make new array + err = corelibs_dynarray_make_new(arr->es, save); + goto ret; // Return even if no error is met, nothing more to do + } + + // Can we slice these elements? + if ((err = corelibs_dynarray_bcheck(arr, pos, cnt)) != CORELIBS_DYNARRAY_ERR_OK) goto ret; + + dynarray_t* new; + // Create new array + if ((err = corelibs_dynarray_make_new(arr->es, &new)) != CORELIBS_DYNARRAY_ERR_OK) goto ret; + + // Resize array to fit contents + if ((err = corelibs_dynarray_mod_arr_cap(new, cnt)) != CORELIBS_DYNARRAY_ERR_OK) { + corelibs_dynarray_free(new); + goto ret; + } + + // Copy contents from old array to new one + if ((err = corelibs_dynarray_export_slice(arr, pos, cnt, new->addr)) != CORELIBS_DYNARRAY_ERR_OK) { + corelibs_dynarray_free(new); + goto ret; + } + new->len = cnt; + new->cap.lock = arr->cap.lock; // Inherit capacity lock + + *save = new; +ret: + return err; +} + +static dynarray_err +corelibs_dynarray_free(dynarray_t* arr) +{ + dynarray_err err = CORELIBS_DYNARRAY_ERR_OK; + + if (arr == NULL) { + err = CORELIBS_DYNARRAY_ERR_NULL; + goto ret; + } + + if ((err = corelibs_dynarray_mod_arr_cap(arr, 0)) != CORELIBS_DYNARRAY_ERR_OK) goto ret; + + free(arr); +ret: + return err; +} + +static dynarray_err +corelibs_dynarray_mod_arr_cap(dynarray_t* arr, uintmax_t len) +{ + dynarray_err err = CORELIBS_DYNARRAY_ERR_OK; + + if (arr == NULL) { + err = CORELIBS_DYNARRAY_ERR_NULL; + goto ret; + } + + if (len == 0) { + free(arr->addr); + arr->cap.len = 0; + goto ret; // Return success (default) + } + + if (arr->cap.lock) { + err = CORELIBS_DYNARRAY_ERR_IMMUT; + goto ret; + } + + void* reg = NULL; + const size_t nl = len * arr->es; + + if (arr->addr == NULL) { + if ((reg = malloc(nl)) == NULL) { + err = CORELIBS_DYNARRAY_ERR_ALLOC; + goto ret; + } + } else if (arr->cap.len != len) { + if ((reg = realloc(arr->addr, nl)) == NULL) { + err = CORELIBS_DYNARRAY_ERR_ALLOC; + goto ret; + } + } + + arr->cap.len = len; + arr->addr = reg; + if (arr->cap.len < arr->len) arr->len = arr->cap.len; // Cut out discarded elements if resized to shorter size +ret: + return err; +} + +static dynarray_err +corelibs_dynarray_mod_arr_lock(dynarray_t* arr, bool lock) +{ + dynarray_err err = CORELIBS_DYNARRAY_ERR_OK; + + if (arr == NULL) { + err = CORELIBS_DYNARRAY_ERR_NULL; + goto ret; + } + + arr->cap.lock = lock; +ret: + return err; +} + +static dynarray_err +corelibs_dynarray_mod_ct_app(dynarray_t* arr, uintmax_t cnt, const void* elem) +{ + return corelibs_dynarray_mod_ct_ins(arr, arr->len, cnt, elem); +} + +static dynarray_err +corelibs_dynarray_mod_ct_ins(dynarray_t* arr, uintmax_t pos, uintmax_t cnt, const void* elem) +{ + dynarray_err err = CORELIBS_DYNARRAY_ERR_OK; + + if (arr == NULL || elem == NULL) { + err = CORELIBS_DYNARRAY_ERR_NULL; + goto ret; + } + + // Resize array to fit inserted objects + if ((err = corelibs_dynarray_mod_arr_cap(arr, ((arr->cap.len < pos) ? pos : arr->cap.len) + cnt)) != CORELIBS_DYNARRAY_ERR_OK) goto ret; + + // CHECKPOINT: From here it is safe to commit changes as required conditions are met + // and no errors should be possible (unless you have some special hardware) + + uintmax_t clen = arr->len; // Lenght pre-resize + + if (arr->len < pos) arr->len = pos; + arr->len += cnt; + + uintptr_t src, // Source of copy address without pointer aritmethic + dest; // Destination to copy to + size_t bcnt; // Amount of bytes to copy + + // If we are inserting and not appending + if (pos < clen) { + src = (uintptr_t) arr->addr + (pos * arr->es); + dest = src + (cnt * arr->es); + bcnt = (clen - pos) * arr->es; + memmove((void*) dest, (void*) src, bcnt); // Shift bytes to after insertion region + } + + src = (uintptr_t) elem; + dest = (uintptr_t) arr->addr + (pos * arr->es); + bcnt = cnt * arr->es; + memcpy((void*) dest, (void*) src, bcnt); // No overlaps possible +ret: + return err; +} + +static dynarray_err +corelibs_dynarray_mod_ct_rep(dynarray_t* arr, uintmax_t pos, uintmax_t cnt, const void* elem) +{ + dynarray_err err = CORELIBS_DYNARRAY_ERR_OK; + + if (arr == NULL || elem == NULL) { + err = CORELIBS_DYNARRAY_ERR_NULL; + goto ret; + } + + if (corelibs_dynarray_bcheck(arr, pos, cnt) == dynarray.err.mem.oob) { + // Resize array to fit new elements + if ((err = corelibs_dynarray_mod_arr_cap(arr, pos + cnt)) != CORELIBS_DYNARRAY_ERR_OK) goto ret; + } + + // CHECKPOINT: From here it is safe to commit changes as required conditions are met + // and no errors should be possible (unless you have some special hardware) + + uintptr_t src, // Source of copy address without pointer aritmethic + dest; // Destination to copy to + size_t bcnt; // Amount of bytes to copy + + src = (uintptr_t) elem; + dest = (uintptr_t) arr->addr + (pos * arr->es); + bcnt = cnt * arr->es; + memcpy((void*) dest, (void*) src, bcnt); + if (arr->len < pos + cnt) arr->len = pos + cnt; // Set length at end of replacement if not already same or longer +ret: + return err; +} + +static dynarray_err +corelibs_dynarray_mod_ct_rm(dynarray_t* arr, uintmax_t pos, uintmax_t cnt) +{ + dynarray_err err = CORELIBS_DYNARRAY_ERR_OK; + + if (arr == NULL) { + err = CORELIBS_DYNARRAY_ERR_NULL; + goto ret; + } + + // Are we removing elements we don't have? + if ((err = corelibs_dynarray_bcheck(arr, pos, cnt)) != CORELIBS_DYNARRAY_ERR_OK) goto ret; + + // Allocate intermediate buffer + void* tbuf = malloc(arr->es * arr->len); + if (tbuf == NULL) { + err = CORELIBS_DYNARRAY_ERR_ALLOC; + goto ret; + } + + // CHECKPOINT: From here it is safe to commit changes as required conditions are met + // and no errors should be possible (unless you have some special hardware) + + arr->len -= cnt; // We remove cnt elements from array + + uintptr_t src, // Source of copy address without pointer aritmethic + dest; // Destination to copy to + size_t bcnt; // Amount of bytes to copy + + dest = (uintptr_t) tbuf; + src = (uintptr_t) arr->addr; + bcnt = arr->len * arr->es; + memcpy((void*) dest, (void*) src, bcnt); // Copy current contents up to arr->len to intermediate buffer + + // If tail is the only thing removed, don't copy back + if (pos + cnt != arr->len) { + // Copy back slice and overwrite region removed + dest += pos * arr->es; + src += (pos + cnt) * arr->es; + bcnt -= pos * arr->es; + memcpy((void*) dest, (void*) src, bcnt); + } + + free(arr->addr); + arr->addr = tbuf; // Swap buffers and free previous one +ret: + return err; +} + +static dynarray_err +corelibs_dynarray_export_slice(const dynarray_t* arr, uintmax_t pos, uintmax_t cnt, void* save) +{ + dynarray_err err = CORELIBS_DYNARRAY_ERR_OK; + + if (arr == NULL || save == NULL) { + err = CORELIBS_DYNARRAY_ERR_NULL; + goto ret; + } + if (cnt == 0) goto ret; + + // Do we have the requested slice? + if ((err = corelibs_dynarray_bcheck(arr, pos, cnt)) != CORELIBS_DYNARRAY_ERR_OK) goto ret; + + uintptr_t src, // Source of copy address without pointer aritmethic + dest; // Destination to copy to + size_t bcnt; // Amount of bytes to copy + + src = (uintptr_t) arr->addr + (arr->es * pos); + dest = (uintptr_t) save; + bcnt = arr->es * cnt; + memcpy((void*) dest, (void*) src, bcnt); +ret: + return err; +} + +static dynarray_err +corelibs_dynarray_get_len(const dynarray_t* arr, uintmax_t* save) +{ + dynarray_err err = CORELIBS_DYNARRAY_ERR_OK; + + if (arr == NULL || save == NULL) { + err = CORELIBS_DYNARRAY_ERR_NULL; + goto ret; + } + + *save = arr->len; +ret: + return err; +} + +static dynarray_err +corelibs_dynarray_get_cap_len(const dynarray_t* arr, uintmax_t* save) +{ + dynarray_err err = CORELIBS_DYNARRAY_ERR_OK; + + if (arr == NULL || save == NULL) { + err = CORELIBS_DYNARRAY_ERR_NULL; + goto ret; + } + + *save = arr->cap.len; +ret: + return err; +} + +static dynarray_err +corelibs_dynarray_get_cap_lock(const dynarray_t* arr, bool* save) +{ + dynarray_err err = CORELIBS_DYNARRAY_ERR_OK; + + if (arr == NULL || save == NULL) { + err = CORELIBS_DYNARRAY_ERR_NULL; + goto ret; + } + + *save = arr->cap.lock; +ret: + return err; +} + +static dynarray_err +corelibs_dynarray_get_size(const dynarray_t* arr, size_t* save) +{ + dynarray_err err = CORELIBS_DYNARRAY_ERR_OK; + + if (arr == NULL || save == NULL) { + err = CORELIBS_DYNARRAY_ERR_NULL; + goto ret; + } + + *save = arr->es; +ret: + return err; +} + +static dynarray_err +corelibs_dynarray_cmp_data(const dynarray_t* a, const dynarray_t* b, bool* eq) +{ + dynarray_err err = CORELIBS_DYNARRAY_ERR_OK; + + if (a == NULL || b == NULL) { + err = CORELIBS_DYNARRAY_ERR_NULL; + goto ret; + } + + if (a->es != b->es) { + err = CORELIBS_DYNARRAY_ERR_NCOMPAT; + goto ret; + } + + if (a->len != b->len) { + *eq = false; + goto ret; + } + + *eq = (memcmp(a->addr, b->addr, a->len) != 0) ? false : true; +ret: + return err; +} + +// Private functions +static dynarray_err +corelibs_dynarray_bcheck(const dynarray_t* arr, uintmax_t pos, uintmax_t len) +{ + dynarray_err err = CORELIBS_DYNARRAY_ERR_OK; + + if (arr == NULL) { + err = CORELIBS_DYNARRAY_ERR_NULL; + goto ret; + } + + if (arr->cap.len < pos + len) { + err = CORELIBS_DYNARRAY_ERR_OOB; + goto ret; + } +ret: + return err; +} diff --git a/dynarray.h b/dynarray.h new file mode 100644 index 0000000..71d68a3 --- /dev/null +++ b/dynarray.h @@ -0,0 +1,109 @@ +/* + * This file is part of corelibs. (https://git.redxen.eu/caskd/corelibs) + * Copyright (c) 2021 Alex-David Denes + * + * corelibs is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * corelibs is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with corelibs. If not, see . + */ + +#include // bool +#include // size_t +#include // uintmax_t + +#ifndef CORELIBS_GUARD_DYNARRAY +#define CORELIBS_GUARD_DYNARRAY + +typedef signed short int dynarray_err; +typedef struct dynarray_t dynarray_t; + +struct corelibs_dynarray_interface { + + // Makers + const struct { + // New - allocate new array + dynarray_err (*const new)(size_t sbyte, dynarray_t** ptr); + // Slicer - gets a slice of a array + dynarray_err (*const slice)(const dynarray_t* arr, uintmax_t pos, uintmax_t len, dynarray_t** ssave); + } make; + + // Free + dynarray_err (*const free)(dynarray_t* arr); + + // Modifiers + const struct { + + const struct { + // Capacity + dynarray_err (*const cap)(dynarray_t* ptr, uintmax_t len); + // Capacity lock (dynamic / static capacity) + dynarray_err (*const lock)(dynarray_t* ptr, bool cap); + } arr; + + const struct { + // Appender - Adds element at end of array + dynarray_err (*const app)(dynarray_t* arr, uintmax_t cnt, const void* elem); + // Inserter - Adds element at position, pushing next element 1 position further + dynarray_err (*const ins)(dynarray_t* arr, uintmax_t pos, uintmax_t cnt, const void* elem); + // Replacer - Replaces array item or creates it if not existent + dynarray_err (*const rep)(dynarray_t* arr, uintmax_t pos, uintmax_t cnt, const void* elem); + // Remover - removes element from array + dynarray_err (*const rm)(dynarray_t* arr, uintmax_t pos, uintmax_t cnt); + } ct; + + } mod; + + // Fetchers + const struct { + dynarray_err (*const len)(const dynarray_t* arr, uintmax_t* save); + dynarray_err (*const size)(const dynarray_t* arr, size_t* save); + const struct { + dynarray_err (*const len)(const dynarray_t* arr, uintmax_t* save); + dynarray_err (*const lock)(const dynarray_t* arr, bool* save); + } cap; + } get; + + // Comparers + const struct { + dynarray_err (*const data)(const dynarray_t* a, const dynarray_t* b, bool* eq); + } cmp; + + // Exporter + const struct { + dynarray_err (*const slice)(const dynarray_t* arr, uintmax_t pos, uintmax_t cnt, void* save); + } export; + + // Errors - functions return them on run-time problems + const struct { + + const dynarray_err ok, // No error + unknown; // Unknown error + + const struct { + const dynarray_err alloc, // Memory allocation failed + null, // Passed a null pointer + oob; // Out of bounds modification or fetch + } mem; + + const struct { + const dynarray_err undef, // Undefined variable + immut, // Immutable (constant) variable + inval, // Invalid value passed + ncompat; // Incompatible comparasion + } var; + + } err; +}; +extern const struct corelibs_dynarray_interface dynarray; + +#endif /* CORELIBS_GUARD_DYNARRAY */ +