Last active
February 25, 2025 10:29
-
-
Save nicebyte/86bd1f119d3ff5c8da06bc2fd59ad668 to your computer and use it in GitHub Desktop.
dyn_arr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#define DYN_ARR_OF(type) struct { \ | |
type *data; \ | |
type *endptr; \ | |
uint32_t capacity; \ | |
} | |
#if !defined(__cplusplus) | |
#define decltype(x) void* | |
#endif | |
#define DYN_ARR_RESET(a, c) { \ | |
a.data = (decltype(a.data))malloc(sizeof(a.data[0]) * c); \ | |
a.endptr = a.data; \ | |
a.capacity = c; \ | |
} | |
#define DYN_ARR_RESIZE(a, s) { \ | |
uint32_t size = (s); \ | |
if (a.capacity < size) { \ | |
a.data = (decltype(a.data))realloc(a.data, sizeof(a.data[0]) * size); \ | |
a.capacity = size; \ | |
} \ | |
a.endptr = a.data + size; \ | |
} | |
#define DYN_ARR_DESTROY(a) if(a.data != NULL) { \ | |
free(a.data); \ | |
a.data = a.endptr = NULL; \ | |
} | |
#define DYN_ARR_APPEND(a, v) { \ | |
ptrdiff_t cur_size = a.endptr - a.data; \ | |
assert(cur_size >= 0); \ | |
if ((size_t)cur_size >= a.capacity) { \ | |
a.capacity <<= 1u; \ | |
decltype(a.data) tmp = (decltype(a.data)) realloc(a.data, sizeof(a.data[0]) * a.capacity); \ | |
assert(tmp != NULL); \ | |
a.data = tmp; \ | |
a.endptr = &a.data[cur_size]; \ | |
} \ | |
*(a.endptr++) = v; \ | |
} | |
#define DYN_ARR_CLEAR(a) (a.endptr = a.data) | |
#define DYN_ARR_SIZE(a) ((uint32_t)(a.endptr - a.data)) | |
#define DYN_ARR_AT(a, i) (a.data[i]) | |
#define DYN_ARR_POP(a) {\ | |
assert(a.data != a.endptr); \ | |
--(a.endptr); \ | |
} | |
#define DYN_ARR_EMPTY(a) (a.endptr == a.data) | |
#define DYN_ARR_BACKPTR(a) (a.endptr - 1) | |
#define DYN_ARR_FOREACH(a, countername) \ | |
for (size_t countername = 0; (countername) < DYN_ARR_SIZE(a); ++(countername)) | |
/* | |
Example usage: | |
typedef struct point { uint32_t x, y } point; | |
void foo() { | |
DYN_ARR_OF(point) points; | |
DYN_ARR_RESET(points, 100u); | |
uint32_t npoints = 200u; | |
for (uint32_t i = 0u; i < npoints; ++i) { | |
point p = {i, i * 10u}; | |
DYN_ARR_APPEND(points, p); | |
} | |
assert(DYN_ARR_SIZE(points) == 200u); | |
DYN_ARR_FOREACH(points, i) { | |
assert(DYN_ARR_AT(points, i).x == i); | |
assert(DYN_ARR_AT(points, i).y == i * 10u); | |
} | |
DYN_ARR_CLEAR(points); | |
assert(DYN_ARR_SIZE(points) == 0u); | |
DYN_ARR_DESTROY(points); | |
} | |
*/ |
Nice! A couple of suggestions:
-
I would define the macros using "do { ... } while (false)". This makes DYN_XXX(a,b,c) behave more like an instruction rather than blocks of code to avoid some surprises in some corner cases here and there.
So, for instance:
#define DYN_ARR_RESIZE(a, s) do { \ uint32_t size = (s); \ if (a.capacity < size) { \ a.data = (decltype(a.data))realloc(a.data, sizeof(a.data[0]) * size); \ a.capacity = size; \ } \ a.endptr = a.data + size; \ } while(false)
-
in DYN_ARR_DESTROY(), the
free
function already handles the NULL case, no need toif(a.data != NULL)
. Here's the extract of the man pages:If ptr is a null pointer, no action shall occur.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I suggest using
size_t
, notuint32_t
, even if only because people might need more than 4G elements. But also it's by definition the right type for this.There's a risk of arithmetic overflow in the multiplications. See stdckdint.h, which is a standardized form of GCC and Clang
__builtin_mul_overflow
and friends.