min_array/src/min_array.h

81 lines
3.4 KiB
C
Raw Normal View History

2021-03-21 21:21:48 +00:00
/** Macro for a type-specific minimal dynamic array: `MIN_ARRAY(name, type)`,
where `name` is an identifier prefix that satisfies `C` naming conventions
when mangled and `type` is defined tag-type associated therewith. When
expanding the array, resizing may be necessary and incurs amortised cost; any
pointers to this memory may become stale. */
2021-03-21 18:11:08 +00:00
#include <stdlib.h> /* size_t realloc free */
#include <string.h> /* memmove strcmp memcpy */
#include <errno.h> /* errno */
#include <assert.h> /* assert */
#define MIN_ARRAY_IDLE { 0, 0, 0 }
#define MIN_ARRAY(name, type) \
struct name##_array { type *data; size_t size, capacity; }; \
/** Initialises `a` to idle. */ \
static void name##_array(struct name##_array *const a) \
2021-03-21 22:54:10 +00:00
{ assert(a); a->data = 0; a->capacity = a->size = 0; } \
2021-03-21 18:11:08 +00:00
/** Destroys `a` and returns it to idle. */ \
static void name##_array_(struct name##_array *const a) \
2021-03-21 22:54:10 +00:00
{ assert(a); free(a->data); name##_array(a); } \
2021-03-21 18:11:08 +00:00
/** Ensures `min_capacity` of `a`. @param[min_capacity] If zero, does nothing.
@return Success; otherwise, `errno` will be set.
@throws[ERANGE] Tried allocating more then can fit in `size_t` or `realloc`
doesn't follow POSIX. @throws[realloc, ERANGE] */ \
static int name##_array_reserve(struct name##_array *const a, \
const size_t min_capacity) { \
size_t c0; \
type *data; \
const size_t max_size = (size_t)-1 / sizeof *a->data; \
assert(a); \
if(a->data) { \
if(min_capacity <= a->capacity) return 1; \
c0 = a->capacity; \
} else { /* Idle. */ \
if(!min_capacity) return 1; \
c0 = 1; \
} \
if(min_capacity > max_size) return errno = ERANGE, 0; \
/* `c_n = a1.625^n`, approximation golden ratio `\phi ~ 1.618`. */ \
while(c0 < min_capacity) { \
size_t c1 = c0 + (c0 >> 1) + (c0 >> 3) + 1; \
if(c0 > c1) { c0 = max_size; break; } \
c0 = c1; \
} \
if(!(data = realloc(a->data, sizeof *a->data * c0))) \
{ if(!errno) errno = ERANGE; return 0; } \
a->data = data, a->capacity = c0; \
return 1; \
} \
2021-03-21 21:50:30 +00:00
/** Makes sure that there are at least `buffer` contiguous, un-initialised,
elements at the back of `a`.
@return A pointer to the start of `buffer` elements, namely `a.data + a.size`.
If `a` is idle and `buffer` is zero, a null pointer is returned, otherwise
null indicates an error and `errno` will be set. @throws[realloc, ERANGE] */ \
2021-03-21 18:11:08 +00:00
static type *name##_array_buffer(struct name##_array *const a, \
const size_t buffer) { \
assert(a); \
if(a->size > (size_t)-1 - buffer) \
{ errno = ERANGE; return 0; } /* Unlikely. */ \
if(!name##_array_reserve(a, a->size + buffer)) return 0; \
return a->data ? a->data + a->size : 0; \
} \
2021-05-12 04:52:15 +00:00
/** Adds `n` to the size of `a`; this must be no more than the maximum
remaining buffer capacity, set by <fn:<name>_array_buffer>. */ \
2021-03-21 18:11:08 +00:00
static void name##_array_emplace(struct name##_array *const a, \
const size_t n) { \
assert(a && a->capacity >= a->size && n <= a->capacity - a->size); \
a->size += n; \
} \
/** @return Push back a new un-initialized datum of `a`.
2021-05-12 04:52:15 +00:00
@throws[realloc, ERANGE] */ \
2021-03-21 18:11:08 +00:00
static type *name##_array_new(struct name##_array *const a) { \
type *const data = name##_array_buffer(a, 1); \
return data ? name##_array_emplace(a, 1), data : 0; \
} \
2021-03-21 19:45:06 +00:00
/* It's perfectly valid that these functions are not used. */ \
2021-03-21 18:11:08 +00:00
static void name##_unused_coda(void); static void name##_unused(void) { \
2021-05-12 04:52:15 +00:00
name##_array(0); name##_array_buffer(0, 0); name##_array_emplace(0, 0); \
name##_array_new(0); name##_unused_coda(); } \
2021-03-21 18:11:08 +00:00
static void name##_unused_coda(void) { name##_unused(); }