Skip to content

Instantly share code, notes, and snippets.

@nicebyte
Last active January 23, 2024 00:10
Show Gist options
  • Star 78 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save nicebyte/86bd1f119d3ff5c8da06bc2fd59ad668 to your computer and use it in GitHub Desktop.
Save nicebyte/86bd1f119d3ff5c8da06bc2fd59ad668 to your computer and use it in GitHub Desktop.
dyn_arr
#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);
}
*/
@noncombatant
Copy link

I suggest using size_t, not uint32_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.

@meuter
Copy link

meuter commented Mar 1, 2023

Nice! A couple of suggestions:

  1. 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)
  2. in DYN_ARR_DESTROY(), the free function already handles the NULL case, no need to if(a.data != NULL). Here's the extract of the man pages:

    If ptr is a null pointer, no action shall occur.

    (https://man7.org/linux/man-pages/man3/free.3p.html)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment