/* * 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_VAR_NCOMPAT = -7, CORELIBS_DYNARRAY_ERR_VAR_INVAL, CORELIBS_DYNARRAY_ERR_VAR_IMMUT, CORELIBS_DYNARRAY_ERR_VAR_UNDEF, CORELIBS_DYNARRAY_ERR_MEM_OOB, CORELIBS_DYNARRAY_ERR_MEM_NULL, CORELIBS_DYNARRAY_ERR_MEM_ALLOC, CORELIBS_DYNARRAY_ERR_UNKOWN = 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 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; goto ret; } cl_dynarray_t* new = malloc(sizeof(*new)); if (new == NULL) { err = CORELIBS_DYNARRAY_ERR_MEM_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 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; 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; cl_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 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; goto ret; } if ((err = corelibs_dynarray_mod_arr_cap(arr, 0)) != CORELIBS_DYNARRAY_ERR_OK) goto ret; free(arr); ret: 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; 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_VAR_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_MEM_ALLOC; goto ret; } } else if (arr->cap.len != len) { if ((reg = realloc(arr->addr, nl)) == NULL) { err = CORELIBS_DYNARRAY_ERR_MEM_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 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; goto ret; } arr->cap.lock = lock; ret: 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; 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 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; goto ret; } if (corelibs_dynarray_bcheck(arr, pos, cnt) == CORELIBS_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 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; 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_MEM_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 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; 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 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; goto ret; } *save = arr->len; ret: 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; goto ret; } *save = arr->cap.len; ret: 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; goto ret; } *save = arr->cap.lock; ret: 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; goto ret; } *save = arr->es; ret: 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; goto ret; } if (a->es != b->es) { err = CORELIBS_DYNARRAY_ERR_VAR_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 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; goto ret; } if (arr->cap.len < pos + len) { err = CORELIBS_DYNARRAY_ERR_MEM_OOB; goto ret; } ret: return err; }