Минимальные векторные динамические массивы, min_array.h
. Это совсем не ново, но поскольку я использую много подобных функций в более крупных проектах, я надеялся получить комментарии по документации, особенно по именам и интерфейсу.
/** 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. */
#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)
{ 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`. @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;
}
/** 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] */
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`; `n` must be smaller than or equal to the
highest remaining buffer value 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;
}
/** @return Push back a new un-initialized datum of `a`.
@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;
}
/* 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(); }
Использование min_array.h
буферизовать поток и извлекать из него информацию; в данном случае числа. Это использует однопроходный режим и расширяет буфер по мере необходимости. С самого начала FILE
будет скопирован в память, я использовал fread
вместо того, чтобы идти построчно.
/** This is a test for `min_array.h`: takes an arbitrary stream, (Cntl-d stops
in some implementations of interactive terminals,) and greedily extracts
integers in the style of `strtol` until it gets to the end-of-file or a
null-terminator. */
#include <stdlib.h> /* size_t EXIT_ strtol */
#include <stdio.h> /* FILE fopen fclose fread [f]printf ferror perror */
#include <errno.h> /* errno */
#include <ctype.h> /* isdigit */
#include <limits.h> /* LONG_ INT_ _MIN _MAX */
#include <assert.h> /* assert */
#include "min_array.h"
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.
@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) {
const size_t granularity = 1024;
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);
if(ferror(fp) || !(cursor = char_array_new(string))) return 0;
*cursor=" ";
return 1;
}
/** Stores the entire stream and then stores the extracted `int` numbers. */
int main(void) {
int success = EXIT_FAILURE;
struct char_array str = MIN_ARRAY_IDLE;
struct num_array nums = MIN_ARRAY_IDLE;
struct num *num;
long big_num;
char *a, *anum = 0;
size_t i, line = 1 /* Unix: delimited by 'n'. */;
errno = 0; /* In case we are running it as part of another editor. */
if(!fp_to_str(stdin, &str)) goto catch;
/* It would be conceivable use the length to continue past the first
sentinel ' ', but not that complex. */
fprintf(stderr, "Read:n<<<%s>>>n", str.data);
for(a = str.data; a = strpbrk(a, "-0123456789n"); ) {
if(*a == 'n') { line++; a++; continue; }
if(*a == '-') { anum = a++; continue; }
if(!anum || anum != a - 1) anum = a; /* Wasn't negative. */
big_num = strtol(anum, &a, 0);
if((!big_num || big_num == LONG_MIN || big_num == LONG_MAX) && errno)
goto catch; /* Long conversion failed, (not all platforms.) */
if(big_num < INT_MIN || big_num > INT_MAX)
{ errno = ERANGE; goto catch; }
if(!(num = num_array_new(&nums))) goto catch;
num->line = line;
num->num = (int)big_num;
}
fprintf(stderr, "Extracted:n");
for(i = 0; i < nums.size; i++) printf("Line %lu: %dn",
(unsigned long)nums.data[i].line, nums.data[i].num);
success = EXIT_SUCCESS;
goto finally;
catch:
fprintf(stderr, "Line %lu ", (unsigned long)line), perror("stdin");
finally:
char_array_(&str);
num_array_(&nums);
return success;
}