/* * This file is part of corelibs. (https://git.redxen.eu/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 cl_dynarray_err corelibs_dynarray_export_slice (const cl_dynarray_t *, uintmax_t, uintmax_t, void *); // Lifetime static cl_dynarray_err corelibs_dynarray_make_new (size_t, cl_dynarray_t **); static cl_dynarray_err corelibs_dynarray_make_slice (const cl_dynarray_t *, uintmax_t, uintmax_t, cl_dynarray_t **); static cl_dynarray_err corelibs_dynarray_free (cl_dynarray_t *); // Array storage static cl_dynarray_err corelibs_dynarray_mod_arr_cap (cl_dynarray_t *, uintmax_t); static cl_dynarray_err corelibs_dynarray_mod_arr_lock (cl_dynarray_t *, bool); // Array contents static cl_dynarray_err corelibs_dynarray_mod_ct_app (cl_dynarray_t *, uintmax_t, const void *); static cl_dynarray_err corelibs_dynarray_mod_ct_ins (cl_dynarray_t *, uintmax_t, uintmax_t, const void *); static cl_dynarray_err corelibs_dynarray_mod_ct_rep (cl_dynarray_t *, uintmax_t, uintmax_t, const void *); static cl_dynarray_err corelibs_dynarray_mod_ct_rm (cl_dynarray_t *, uintmax_t, uintmax_t); // Get array properties static cl_dynarray_err corelibs_dynarray_get_len (const cl_dynarray_t *, uintmax_t *); static cl_dynarray_err corelibs_dynarray_get_size (const cl_dynarray_t *, size_t *); static cl_dynarray_err corelibs_dynarray_get_cap_len (const cl_dynarray_t *, uintmax_t *); static cl_dynarray_err corelibs_dynarray_get_cap_lock (const cl_dynarray_t *, bool *); // Compare arrays static cl_dynarray_err corelibs_dynarray_cmp_data (const cl_dynarray_t *a, const cl_dynarray_t *b, bool *eq); // Private functions static cl_dynarray_err corelibs_dynarray_bcheck (const cl_dynarray_t *, uintmax_t, uintmax_t); struct cl_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_OK = 0, CORELIBS_DYNARRAY_ERR_UNKOWN, CORELIBS_DYNARRAY_ERR_MEM_OOB, CORELIBS_DYNARRAY_ERR_MEM_NULL, CORELIBS_DYNARRAY_ERR_MEM_ALLOC, CORELIBS_DYNARRAY_ERR_VAR_NCOMPAT, CORELIBS_DYNARRAY_ERR_VAR_INVAL, CORELIBS_DYNARRAY_ERR_VAR_IMMUT, CORELIBS_DYNARRAY_ERR_VAR_UNDEF, }; // Present a unified structure that may receive changes, deprecations and renames without hassle for external users const struct corelibs_dynarray_interface cl_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_UNKOWN, .mem = { .alloc = CORELIBS_DYNARRAY_ERR_MEM_ALLOC, .null = CORELIBS_DYNARRAY_ERR_MEM_NULL, .oob = CORELIBS_DYNARRAY_ERR_MEM_OOB, }, .var = { .undef = CORELIBS_DYNARRAY_ERR_VAR_UNDEF, .immut = CORELIBS_DYNARRAY_ERR_VAR_IMMUT, .inval = CORELIBS_DYNARRAY_ERR_VAR_INVAL, .ncompat = CORELIBS_DYNARRAY_ERR_VAR_NCOMPAT, }, }, }; static cl_dynarray_err corelibs_dynarray_make_new (size_t sbyte, cl_dynarray_t **ptr) { cl_dynarray_err err = CORELIBS_DYNARRAY_ERR_OK; if (ptr == NULL) err = CORELIBS_DYNARRAY_ERR_MEM_NULL; if (!err) { cl_dynarray_t *new = malloc (sizeof (*new)); if (new != NULL) { new->addr = NULL; new->es = sbyte; new->len = 0; new->cap.len = 0; new->cap.lock = false; *ptr = new; } else { err = CORELIBS_DYNARRAY_ERR_MEM_ALLOC; } } return err; } static cl_dynarray_err corelibs_dynarray_make_slice (const cl_dynarray_t *arr, uintmax_t pos, uintmax_t cnt, cl_dynarray_t **save) { cl_dynarray_err err = CORELIBS_DYNARRAY_ERR_OK; if (save == NULL || arr == NULL) err = CORELIBS_DYNARRAY_ERR_MEM_NULL; if (!err) { if (cnt == 0) { // Don't copy anything, just make new array return corelibs_dynarray_make_new (arr->es, save); } // Can we slice these elements? if (!(err = corelibs_dynarray_bcheck (arr, pos, cnt))) { // Create new array cl_dynarray_t *new; if (!(err = corelibs_dynarray_make_new (arr->es, &new))) { // Resize array to fit contents if (!(err = corelibs_dynarray_mod_arr_cap (new, cnt))) { // Copy contents from old array to new one if (!(err = corelibs_dynarray_export_slice (arr, pos, cnt, new->addr))) { new->len = cnt; new->cap.lock = arr->cap.lock; // Inherit capacity lock *save = new; } } if (err) corelibs_dynarray_free (new); } } } return err; } static cl_dynarray_err corelibs_dynarray_free (cl_dynarray_t *arr) { cl_dynarray_err err = CORELIBS_DYNARRAY_ERR_OK; if (arr == NULL) err = CORELIBS_DYNARRAY_ERR_MEM_NULL; if (!err) { if (!(err = corelibs_dynarray_mod_arr_cap (arr, 0))) { free (arr); } } return err; } static cl_dynarray_err corelibs_dynarray_mod_arr_cap (cl_dynarray_t *arr, uintmax_t len) { cl_dynarray_err err = CORELIBS_DYNARRAY_ERR_OK; if (arr == NULL) err = CORELIBS_DYNARRAY_ERR_MEM_NULL; if (!err) { if (len == 0) { free (arr->addr); arr->cap.len = 0; } else if (!arr->cap.lock) { void * reg = NULL; const size_t nl = len * arr->es; if (arr->addr == NULL) { reg = malloc (nl); } else if (arr->cap.len != len) { reg = realloc (arr->addr, nl); } if (reg != NULL) { 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 } else { err = CORELIBS_DYNARRAY_ERR_MEM_ALLOC; } } else { err = CORELIBS_DYNARRAY_ERR_VAR_IMMUT; } } return err; } static cl_dynarray_err corelibs_dynarray_mod_arr_lock (cl_dynarray_t *arr, bool lock) { cl_dynarray_err err = CORELIBS_DYNARRAY_ERR_OK; if (arr == NULL) err = CORELIBS_DYNARRAY_ERR_MEM_NULL; if (!err) { arr->cap.lock = lock; } return err; } static cl_dynarray_err corelibs_dynarray_mod_ct_app (cl_dynarray_t *arr, uintmax_t cnt, const void *elem) { return corelibs_dynarray_mod_ct_ins (arr, arr->len, cnt, elem); } static cl_dynarray_err corelibs_dynarray_mod_ct_ins (cl_dynarray_t *arr, uintmax_t pos, uintmax_t cnt, const void *elem) { cl_dynarray_err err = CORELIBS_DYNARRAY_ERR_OK; if (arr == NULL || elem == NULL) err = CORELIBS_DYNARRAY_ERR_MEM_NULL; if (!err) { // Resize array to fit inserted objects if (!(err = corelibs_dynarray_mod_arr_cap (arr, ((arr->cap.len < pos) ? pos : arr->cap.len) + cnt))) { // 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 } } return err; } static cl_dynarray_err corelibs_dynarray_mod_ct_rep (cl_dynarray_t *arr, uintmax_t pos, uintmax_t cnt, const void *elem) { cl_dynarray_err err = CORELIBS_DYNARRAY_ERR_OK; if (arr == NULL || elem == NULL) err = CORELIBS_DYNARRAY_ERR_MEM_NULL; if (!err) { if (corelibs_dynarray_bcheck (arr, pos, cnt) == CORELIBS_DYNARRAY_ERR_MEM_OOB) { // Resize array to fit new elements err = corelibs_dynarray_mod_arr_cap (arr, pos + cnt); } if (!err) { // 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 } } return err; } static cl_dynarray_err corelibs_dynarray_mod_ct_rm (cl_dynarray_t *arr, uintmax_t pos, uintmax_t cnt) { cl_dynarray_err err = CORELIBS_DYNARRAY_ERR_OK; if (arr == NULL) err = CORELIBS_DYNARRAY_ERR_MEM_NULL; if (!err) { // Are we removing elements we don't have? if (!(err = corelibs_dynarray_bcheck (arr, pos, cnt))) { // Allocate intermediate buffer void *tbuf = malloc (arr->es * arr->len); if (tbuf != NULL) { // 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 } else { err = CORELIBS_DYNARRAY_ERR_MEM_ALLOC; } } } return err; } static cl_dynarray_err corelibs_dynarray_export_slice (const cl_dynarray_t *arr, uintmax_t pos, uintmax_t cnt, void *save) { cl_dynarray_err err = CORELIBS_DYNARRAY_ERR_OK; if (arr == NULL || save == NULL) err = CORELIBS_DYNARRAY_ERR_MEM_NULL; if (!err || cnt != 0) { // Do we have the requested slice? if (!(err = corelibs_dynarray_bcheck (arr, pos, cnt))) { 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); } } return err; } static cl_dynarray_err corelibs_dynarray_get_len (const cl_dynarray_t *arr, uintmax_t *save) { cl_dynarray_err err = CORELIBS_DYNARRAY_ERR_OK; if (arr == NULL || save == NULL) err = CORELIBS_DYNARRAY_ERR_MEM_NULL; if (!err) { *save = arr->len; } return err; } static cl_dynarray_err corelibs_dynarray_get_cap_len (const cl_dynarray_t *arr, uintmax_t *save) { cl_dynarray_err err = CORELIBS_DYNARRAY_ERR_OK; if (arr == NULL || save == NULL) err = CORELIBS_DYNARRAY_ERR_MEM_NULL; if (!err) { *save = arr->cap.len; } return err; } static cl_dynarray_err corelibs_dynarray_get_cap_lock (const cl_dynarray_t *arr, bool *save) { cl_dynarray_err err = CORELIBS_DYNARRAY_ERR_OK; if (arr == NULL || save == NULL) err = CORELIBS_DYNARRAY_ERR_MEM_NULL; if (!err) { *save = arr->cap.lock; } return err; } static cl_dynarray_err corelibs_dynarray_get_size (const cl_dynarray_t *arr, size_t *save) { cl_dynarray_err err = CORELIBS_DYNARRAY_ERR_OK; if (arr == NULL || save == NULL) err = CORELIBS_DYNARRAY_ERR_MEM_NULL; if (!err) { *save = arr->es; } return err; } static cl_dynarray_err corelibs_dynarray_cmp_data (const cl_dynarray_t *a, const cl_dynarray_t *b, bool *eq) { cl_dynarray_err err = CORELIBS_DYNARRAY_ERR_OK; if (a == NULL || b == NULL) err = CORELIBS_DYNARRAY_ERR_MEM_NULL; if (a->es != b->es) err = CORELIBS_DYNARRAY_ERR_VAR_NCOMPAT; if (!err) { if (a->len != b->len) { *eq = false; } else { *eq = (memcmp (a->addr, b->addr, a->len) != 0) ? false : true; } } return err; } // Private functions static cl_dynarray_err corelibs_dynarray_bcheck (const cl_dynarray_t *arr, uintmax_t pos, uintmax_t len) { cl_dynarray_err err = CORELIBS_DYNARRAY_ERR_OK; if (arr == NULL) err = CORELIBS_DYNARRAY_ERR_MEM_NULL; if (arr->cap.len < pos + len) err = CORELIBS_DYNARRAY_ERR_MEM_OOB; return err; }