/** Macro for a generic 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. @param[MIN_ARRAY_CAPACITY] Must be greater than 1; defaults to 3. */ #include /* size_t realloc free */ #include /* errno */ #include /* assert */ #define MIN_ARRAY_IDLE { 0, 0, 0 } #ifndef MIN_ARRAY_CAPACITY #define MIN_ARRAY_CAPACITY 3 /* > 1 */ #endif #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) \ { assert(a); a->data = 0; a->capacity = a->size = 0; } \ /** Destroys `a` and returns it to idle. */ \ static void name##_array_(struct name##_array *const a) \ { assert(a); free(a->data); name##_array(a); } \ /** Ensures `min_capacity` of `a`. @return Success; otherwise, `errno` will be set. @throws[ERANGE] Tried allocating more then can fit in an object or doesn't follow POSIX. @throws[realloc] */ \ static int name##_array_reserve(struct name##_array *const a, \ const size_t min) { \ size_t c0; \ type *data; \ const size_t max_size = (size_t)-1 / sizeof *a->data; \ assert(a); \ if(a->data) { \ assert(a->size <= a->capacity); \ if(min <= a->capacity) return 1; \ c0 = a->capacity < MIN_ARRAY_CAPACITY \ ? MIN_ARRAY_CAPACITY : a->capacity; \ } else { /* Idle. */ \ assert(!a->size && !a->capacity); \ if(!min) return 1; \ c0 = MIN_ARRAY_CAPACITY; \ } \ if(min > max_size) return errno = ERANGE, 0; \ /* `c_n = a1.625^n`, approximation golden ratio `\phi ~ 1.618`. */ \ while(c0 < min) { \ size_t c1 = c0 + (c0 >> 1) + (c0 >> 3); \ 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; \ } \ /** Increases the capacity of `a` to at least `n` elements beyond the size. @return The start of the buffered space at the back of the array. If `a` is idle and `buffer` is zero, a null pointer is returned, otherwise null indicates an error. @throws[realloc, ERANGE] */ \ static type *name##_array_buffer(struct name##_array *const a, \ const size_t n) { \ assert(a); \ if(a->size > (size_t)-1 - n) { errno = ERANGE; return 0; } /* Unlikely. */ \ return name##_array_reserve(a, a->size + n) && a->data \ ? a->data + a->size : 0; \ } \ /** Adds `n` elements to the back of `a`. @return A pointer to the elements. If `a` is idle and `n` is zero, a null pointer will be returned, otherwise null indicates an error. @throws[realloc, ERANGE] */ \ static type *name##_array_append(struct name##_array *const a, \ const size_t n) { \ type *buffer; \ if(!(buffer = name##_array_buffer(a, n))) return 0; \ assert(n <= a->capacity && a->size <= a->capacity - n); \ return a->size += n, buffer; \ } \ /** @return Adds (push back) one new element of `a`. @throws[realloc, ERANGE] */ \ static type *name##_array_new(struct name##_array *const a) \ { return name##_array_append(a, 1); } \ /* It's perfectly valid that these functions are not used. */ \ static void name##_unused_coda(void); static void name##_unused(void) { \ name##_array(0); name##_array_new(0); name##_unused_coda(); } \ static void name##_unused_coda(void) { name##_unused(); }