Updated with array.

This commit is contained in:
Neil 2021-08-29 11:49:27 -07:00
parent d432704f62
commit 5821f30362
2 changed files with 42 additions and 36 deletions

View File

@ -10,6 +10,7 @@
#include <assert.h> /* assert */
#define MIN_ARRAY_IDLE { 0, 0, 0 }
#define MIN_ARRAY_CAPACITY 3
#define MIN_ARRAY(name, type) \
struct name##_array { type *data; size_t size, capacity; }; \
/** Initialises `a` to idle. */ \
@ -23,23 +24,25 @@ static void name##_array_(struct name##_array *const a) \
@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) { \
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_capacity <= a->capacity) return 1; \
c0 = a->capacity; \
if(min <= a->capacity) return 1; \
c0 = a->capacity < MIN_ARRAY_CAPACITY \
? MIN_ARRAY_CAPACITY : a->capacity; \
} else { /* Idle. */ \
if(!min_capacity) return 1; \
c0 = 1; \
assert(!a->size && !a->capacity); \
if(!min) return 1; \
c0 = MIN_ARRAY_CAPACITY; \
} \
if(min_capacity > max_size) return errno = ERANGE, 0; \
if(min > 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; \
while(c0 < min) { \
size_t c1 = c0 + (c0 >> 1) + (c0 >> 3); \
if(c0 > c1) { c0 = max_size; break; } \
c0 = c1; \
} \
@ -48,34 +51,37 @@ static int name##_array_reserve(struct name##_array *const a, \
a->data = data, a->capacity = c0; \
return 1; \
} \
/** 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] */ \
/** The capacity of `a` will be increased to at least `n` elements beyond the
size. Invalidates any pointers in `a`.
@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 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; \
} \
/** Adds `n` to the size of `a`; this must be no more than the maximum
remaining buffer capacity, set by <fn:<name>_array_buffer>. */ \
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; \
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; \
} \
/** @return Push back a new un-initialized datum of `a`.
/** Adds `n` elements to the back of `a`. It will invalidate pointers in `a` if
`n` is greater than the buffer space.
@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_new(struct name##_array *const a) { \
type *const data = name##_array_buffer(a, 1); \
return data ? name##_array_emplace(a, 1), data : 0; \
static type *name##_array_append(struct name##_array *const a, \
const size_t n) { \
type *buffer; \
assert(a); \
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`. The buffer holds an
element or it will invalidate pointers in `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_buffer(0, 0); name##_array_emplace(0, 0); \
name##_array(0); name##_array_buffer(0, 0); name##_array_append(0, 0); \
name##_array_new(0); name##_unused_coda(); } \
static void name##_unused_coda(void) { name##_unused(); }

View File

@ -16,8 +16,8 @@ struct num { size_t line; int num; };
MIN_ARRAY(char, char)
MIN_ARRAY(num, struct num)
/** Reads the contents of `fp` after the file pointer and copies them to
`string`; adds a null-terminator.
/** Reads the contents of `fp` after the file pointer and appends them to
`string`; adds a null-terminator. Does not check for nulls in `fp` or `string`.
@return Success, otherwise `string` may have read a partial read and may not
be terminated. @throws[fread, malloc] */
static int fp_to_str(FILE *const fp, struct char_array *const string) {
@ -25,10 +25,10 @@ static int fp_to_str(FILE *const fp, struct char_array *const string) {
size_t nread;
char *cursor;
assert(fp && string);
do {
if(!(cursor = char_array_buffer(string, granularity))) return 0;
char_array_emplace(string, nread = fread(cursor, 1, granularity, fp));
} while(nread == granularity); assert(nread < granularity);
do if(!(cursor = char_array_buffer(string, granularity))
|| !char_array_append(string,
nread = fread(cursor, 1, granularity, fp))) return 0;
while(nread == granularity); assert(nread < granularity);
if(ferror(fp) || !(cursor = char_array_new(string))) return 0;
*cursor = '\0';
return 1;