/* prick_darr.h: A dynamically sized array, all on the heap. * Created: 2024-10-01 * Author: Aryadev Chavali * License: see end of file * Commentary: To utilise this library, please put: #define PRICK_DARR_IMPL #include "prick_darr.h" in one of your code units. This library defines a dynamic array purely on the heap. We split the one dynamic array allocation into two parts: the metadata, and the actual data. Consumers of the library will only ever need to deal with the latter component i.e. they'll only ever have access to the data they require. Unfortuntely this does mean that the underlying data pointer is _not_ stable during capacity reallocation, as we're allocating a whole new pointer with the correct size. Therefore, if you're expecting the array to grow during the runtime of your program, you should ensure any pointers to components of it are updated as they will otherwise be dangling. */ #ifndef PRICK_DARR_H #define PRICK_DARR_H #include typedef struct { uint32_t size, capacity; uint8_t bytes[]; } prick_darr_t; #define PRICK_DARR_GET(P) (((prick_darr_t *)(P)) - 1) #define PRICK_DARR_SIZE(P) (PRICK_DARR_GET(P)->size) #define PRICK_DARR_CAP(P) (PRICK_DARR_GET(P)->capacity) void prick_darr_make(void **ptr, uint32_t size); void prick_darr_free(void **ptr); void prick_darr_append_byte(void **ptr, uint8_t byte); void prick_darr_append(void **ptr, void *data, uint32_t size); void prick_darr_clone(void **dest, void **src); void prick_darr_ensure_remaining(void **ptr, uint32_t space); #ifdef PRICK_DARR_IMPL #include #ifndef PRICK_DARR_MULT #define PRICK_DARR_MULT 2 #endif #define MAX(A, B) ((A) > (B) ? (A) : (B)) #define MIN(A, B) ((A) < (B) ? (A) : (B)) void prick_darr_make(void **ptr, uint32_t size) { if (!ptr) return; prick_darr_t *darr = calloc(1, sizeof(*darr) + size); darr->size = 0; darr->capacity = size; *ptr = (darr + 1); } void prick_darr_free(void **data) { if (!data || !*data) return; free(PRICK_DARR_GET(*data)); *data = NULL; } void prick_darr_ensure_remaining(void **ptr, uint32_t space) { if (!ptr || !*ptr) return; prick_darr_t *darr = PRICK_DARR_GET(*ptr); if (darr->capacity - darr->size < space) { void *new_darr = NULL; prick_darr_make(&new_darr, MAX(darr->capacity * PRICK_DARR_MULT, darr->size + space)); PRICK_DARR_SIZE(new_darr) = darr->size; memcpy(new_darr, *ptr, darr->size); prick_darr_free(ptr); *ptr = new_darr; } } void prick_darr_append_byte(void **ptr, uint8_t byte) { prick_darr_ensure_remaining(ptr, 1); prick_darr_t *darr = PRICK_DARR_GET(*ptr); darr->bytes[darr->size++] = byte; } void prick_darr_append(void **ptr, void *data, uint32_t size) { prick_darr_ensure_remaining(ptr, size); prick_darr_t *darr = PRICK_DARR_GET(*ptr); memcpy(*ptr + darr->size, data, size); darr->size += size; } void prick_darr_clone(void **dest, void **src) { if (!dest || !src || !*src) return; prick_darr_make(dest, PRICK_DARR_SIZE(*src)); memcpy(*dest, *src, PRICK_DARR_SIZE(*src)); PRICK_DARR_SIZE(*dest) = PRICK_DARR_SIZE(*src); } #endif #endif /* Copyright (C) 2024 Aryadev Chavali * This program 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 Unlicense * for details. * You may distribute and modify this code under the terms of the * Unlicense, which you should have received a copy of along with this * program. If not, please go to . */