This commit is contained in:
r4
2022-05-29 17:42:16 +02:00
commit ab779b8506
19 changed files with 2093 additions and 0 deletions

100
include/ds/error.h Normal file
View File

@@ -0,0 +1,100 @@
// Copyright 2022 Darwin Schuppan <darwin@nobrain.org>
// SPDX license identifier: MIT
#ifndef __DS_ERROR_H__
#define __DS_ERROR_H__
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
/* It's pretty important that error messages get through, so we reserve 32kiB
* which we can free if malloc fails. Then we should hopefully at least have
* enough memory to allocate for the error message. */
#define ERROR_RESERVATION_SIZE 32768
extern void *error_reserved_for_error;
/* Some useful shortcuts for constructing errors. */
/* Essentially just a "no error" error. */
#define OK() (Error){ .kind = ErrorNone }
/* Use when malloc fails, for example. */
#define ERROR_OUT_OF_MEMORY() (Error){ .kind = ErrorOutOfMemory }
/* Use for string constants ("this is a string constant"). */
#define ERROR_STRING(_str) (Error){ .kind = ErrorString, .str = (char *)_str }
/* Use for heap-allocated strings (e.g. by malloc(), strdup() etc.). */
#define ERROR_HEAP_STRING(_str) (Error){ .kind = ErrorString, .str = _str, .str_on_heap = true }
/* Embed the current file and line information in the error message. */
#define ERROR_OUT_OF_MEMORY_HERE() (Error){ .kind = ErrorOutOfMemory, .has_location = true, .file = __FILE__, .line = __LINE__ }
#define ERROR_STRING_HERE(_str) (Error){ .kind = ErrorString, .str = (char *)_str, .has_location = true, .file = __FILE__, .line = __LINE__ }
#define ERROR_HEAP_STRING_HERE(_str) (Error){ .kind = ErrorString, .str = _str, .str_on_heap = true, .has_location = true, .file = __FILE__, .line = __LINE__ }
/* Embed custom file and line information in the error message. */
#define ERROR_OUT_OF_MEMORY_LOCATION(_file, _line) (Error){ .kind = ErrorOutOfMemory, .has_location = true, .file = _file, .line = _line }
#define ERROR_STRING_LOCATION(_file, _line, _str) (Error){ .kind = ErrorString, .str = (char *)_str, .has_location = true, .file = _file, .line = _line }
#define ERROR_HEAP_STRING_LOCATION(_file, _line, _str) (Error){ .kind = ErrorString, .str = _str, .str_on_heap = true, .has_location = true, .file = _file, .line = _line }
/* Append one error to another (will show as "error1: error2" in the error message). */
#define ERROR_NESTED(base, _annex) (Error){ .kind = base.kind, .str = base.str, .has_annex = true, .annex = _error_heapify(_annex) }
/* Write the current file and line to an error and return it. */
#define ERROR_HEREIFY(_err) _error_hereify(__FILE__, __LINE__, _err)
/* Propagates any potential errors. Use the otherwise field for any uninitialization code. */
#define TRY(expr, otherwise) { Error _err = expr; if (_err.kind != ErrorNone) { otherwise; return _err; } }
/* If x is an error, this prints a nice error message and then exits. */
#define ERROR_ASSERT(x) /* use with moderation */ { \
Error _err = x; \
if (_err.kind != ErrorNone) { \
if (!_err.has_location) { \
_err.has_location = true; \
_err.file = __FILE__; \
_err.line = __LINE__; \
} \
char _buf[512]; error_to_string(_buf, 512, _err, true); \
fprintf(stderr, "Fatal error: %s\n", _buf); \
exit(1); \
} \
}
typedef enum {
ErrorNone = 0,
ErrorOutOfMemory,
ErrorString,
} ErrorKind;
typedef struct Error {
ErrorKind kind;
char *str;
struct Error *annex;
const char *file;
size_t line;
bool str_on_heap;
bool has_annex;
bool has_location;
} Error;
/* Returns true if the given error structure actually contains a real error. */
static inline bool error_is(Error e) { return e.kind != ErrorNone; }
/* Same as error_is but takes a pointer and does NULL checking. */
static inline bool error_ptr_is(Error *e) { return e != NULL && e->kind != ErrorNone; }
/* Call this at the beginning of any program that makes use of any of the more
* advanced error features (those which involve heap allocation). */
void error_init();
/* Call this at the end of any program that used error_init() to clean up. */
void error_term();
/* Writes an error to a string. It is recommended to use ds/fmt.h in most cases, e.g.:
#include <ds/fmt.h>;
fmt("%{Error:destroy}", err);
*/
size_t error_to_string(char *buf, size_t size, Error e, bool destroy);
Error *_error_heapify(Error e);
Error _error_hereify(const char *file, size_t line, Error e);
#endif

92
include/ds/fmt.h Normal file
View File

@@ -0,0 +1,92 @@
// Copyright 2022 Darwin Schuppan <darwin@nobrain.org>
// SPDX license identifier: MIT
#ifndef __DS_FMT_H__
#define __DS_FMT_H__
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <ds/error.h>
/* Limit definitions. */
#define FMT_MAX_ATTRS 8
#define FMT_MAX_ATTR_LEN 32 /* including null termination */
/* An FmtContext defines how and where a print function should write its output.
*
* putc_func is the fundamental function which "writes" a char in whatever
* way the context requires.
* ctx_data serves as input, output or both to putc_func.
*
* For example, in the fmt functions which write to string buffers, ctx_data
* holds the buffer, its size and how many bytes were written so far. */
typedef struct FmtContext {
void *ctx_data;
void (*putc_func)(struct FmtContext *restrict ctx, char c);
} FmtContext;
/* FmtAttrs holds attributes that are passed like additional parameters to a
* print function. Which attributes are valid is entirely dependent on the
* print function and the type being printed.
*
* len is the number of attributes, names contains the attribute names and
* vals contains the attribute values (default is -1 if no value is given).
*
* For example, when printing an integer, you could use: %{int:p=4,c='0'}, where
* p is the width, the rest of which is filled in by c. Resulting in an integer
* with a width of at least 4 characters padded by zeroes. Attributes can have
* integer or char values, or no value at all (e.g. %{int:X} just sets the "X"
* attribute with a default value of -1).
*
* When creating a custom print function, you have to iterate over the FmtAttrs
* manually. See src/ds/fmt.c for reference examples.
* */
typedef struct FmtAttrs {
size_t len;
char names[FMT_MAX_ATTRS][FMT_MAX_ATTR_LEN];
int vals[FMT_MAX_ATTRS]; /* number/char value */
} FmtAttrs;
/* FmtPrintFuncRet is the return value of a print function. It can either
* return no error or specify an unrecognized attribute name. You should
* always use the macros FMT_PRINT_FUNC_RET_OK() and
* FMT_PRINT_FUNC_RET_INVALID_ATTR() to construct this class in a return
* statement. */
typedef struct FmtPrintFuncRet {
bool invalid_attr;
size_t invalid_attr_idx;
} FmtPrintFuncRet;
#define FMT_PRINT_FUNC_RET_OK() (FmtPrintFuncRet){0}
#define FMT_PRINT_FUNC_RET_INVALID_ATTR(_idx) (FmtPrintFuncRet){ .invalid_attr = true, .invalid_attr_idx = _idx }
/* An FmtPrintFunc describes any function that is designed to print a specific
* type so fmt functions can output it. */
typedef FmtPrintFuncRet (*FmtPrintFunc)(FmtContext *restrict ctx, FmtAttrs *restrict attrs, va_list v);
/* Add a formatter for your custom types. See src/ds/fmt.c for some examples. */
void fmt_register(const char *keyword, FmtPrintFunc print_func);
void _fmtv(const char *restrict file, size_t line, const char *restrict format, va_list args);
void _fmt(const char *restrict file, size_t line, const char *restrict format, ...);
size_t _fmtsv(const char *restrict file, size_t line, char *restrict buf, size_t size, const char *restrict format, va_list args);
size_t _fmts(const char *restrict file, size_t line, char *restrict buf, size_t size, const char *restrict format, ...);
void _fmtcv(const char *restrict file, size_t line, FmtContext *ctx, const char *restrict format, va_list args);
void _fmtc(const char *restrict file, size_t line, FmtContext *ctx, const char *restrict format, ...);
/* Format to stdout. */
#define fmtv(format, ...) _fmtv(__FILE__, __LINE__, format, ##__VA_ARGS__)
#define fmt(format, ...) _fmt(__FILE__, __LINE__, format, ##__VA_ARGS__)
/* Format to char buffer. */
#define fmtsv(buf, size, format, ...) _fmtsv(__FILE__, __LINE__, buf, size, format, ##__VA_ARGS__)
#define fmts(buf, size, format, ...) _fmts(__FILE__, __LINE__, buf, size, format, ##__VA_ARGS__)
/* Format with custom context (e.g. for custom output functions). */
#define fmtcv(ctx, format, ...) _fmtcv(__FILE__, __LINE__, ctx, format, ##__VA_ARGS__)
#define fmtc(ctx, format, ...) _fmtc(__FILE__, __LINE__, ctx, format, ##__VA_ARGS__)
void fmt_init();
void fmt_term();
#endif

214
include/ds/generic/map.h Normal file
View File

@@ -0,0 +1,214 @@
// Copyright 2022 Darwin Schuppan <darwin@nobrain.org>
// SPDX license identifier: MIT
/* Example Usage:
// something.h:
#define GENERIC_KEY_TYPE int // Key type
#define GENERIC_VALUE_TYPE int // Value type
#define GENERIC_NAME IntIntMap // Name of the resulting map type
#define GENERIC_PREFIX int_int_map // Prefix for functions
#include "map.h"
// something.c:
#define GENERIC_IMPL // We want something.c to define the actual function implementations
#include "something.h"
*/
#include <stdbool.h>
#include <stddef.h>
#include <ds/error.h>
#include <ds/fmt.h>
#define GENERIC_REQUIRE_VALUE_TYPE
#define GENERIC_REQUIRE_KEY_TYPE
#include "../internal/generic/begin.h"
#define ITEM_TYPE GENERIC_CONCAT(NAME, Item)
#define EMPTY 0
#define TOMBSTONE 1
#define OCCUPIED 2
typedef struct ITEM_TYPE {
unsigned char state;
KTYPE key;
VTYPE val;
} ITEM_TYPE;
typedef struct NAME {
ITEM_TYPE *data;
size_t cap, len;
} NAME;
VARDECL(const char *, __val_fmt);
VARDECL(const char *, __key_fmt);
FUNCDECL(NAME, )();
FUNCDECL(void, _term)(NAME m);
FUNCDECL(void, _fmt_register)(const char *key_fmt, const char *val_fmt);
FUNCDECL(VTYPE *, _get)(NAME m, KTYPE key);
FUNCDECL(Error, _set)(NAME *m, KTYPE key, VTYPE val);
FUNCDECL(bool, _del)(NAME m, KTYPE key);
FUNCDECL(Error, _rehash)(NAME *m, size_t new_minimum_cap);
FUNCDECL(bool, _it_next)(NAME m, ITEM_TYPE **it);
#ifdef GENERIC_IMPL
VARDEF(const char *, __val_fmt) = NULL;
VARDEF(const char *, __key_fmt) = NULL;
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#ifndef _GENERIC_MAP_IMPL_ONCE
#define _GENERIC_MAP_IMPL_ONCE
static size_t _pow_of_2_from_minimum(size_t n) {
n--; /* we want to handle the case of n already being a power of 2 */
/* We use the leftmost bit set to 1 to also set any bits to its right
* to 1. Then we just increment to carry and get our desired power of 2. */
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
#if INTPTR_MAX == INT64_MAX /* only do the last shift on 64-bit systems */
n |= n >> 32;
#endif
return n + 1;
}
static uint32_t _fnv1a32(const void *data, size_t n) {
uint32_t res = 2166136261u;
for (size_t i = 0; i < n; i++) {
res ^= ((uint8_t*)data)[i];
res *= 16777619u;
}
return res;
}
#endif
static FUNCDEF(FmtPrintFuncRet, __print_func)(FmtContext *restrict ctx, FmtAttrs *restrict attrs, va_list v) {
if (attrs != NULL) {
if (attrs->len != 0)
return FMT_PRINT_FUNC_RET_INVALID_ATTR(0);
}
NAME m = va_arg(v, NAME);
ctx->putc_func(ctx, '{');
ITEM_TYPE *it = NULL;
bool first = true;
while (FUNC(_it_next)(m, &it)) {
if (!first)
fmtc(ctx, ", ");
fmtc(ctx, VAR(__key_fmt), it->key);
fmtc(ctx, ": ");
fmtc(ctx, VAR(__val_fmt), it->val);
first = false;
}
ctx->putc_func(ctx, '}');
return FMT_PRINT_FUNC_RET_OK();
}
FUNCDEF(NAME, )() {
return (NAME){0};
}
FUNCDEF(void, _term)(NAME m) {
free(m.data);
}
FUNCDEF(void, _fmt_register)(const char *key_fmt, const char *val_fmt) {
VAR(__key_fmt) = key_fmt;
VAR(__val_fmt) = val_fmt;
fmt_register(NAME_STR, FUNC(__print_func));
}
FUNCDEF(VTYPE *, _get)(NAME m, KTYPE key) {
size_t i = _fnv1a32(&key, sizeof(KTYPE)) & (m.cap - 1);
while (m.data[i].state != EMPTY) {
if (m.data[i].state != TOMBSTONE && memcmp(&m.data[i].key, &key, sizeof(KTYPE)) == 0)
return &m.data[i].val;
i = (i + 1) % m.cap;
}
return NULL;
}
FUNCDEF(Error, _set)(NAME *m, KTYPE key, VTYPE val) {
if (m->cap == 0 || (float)m->len / (float)m->cap > 0.7f)
TRY(FUNC(_rehash)(m, m->cap == 0 ? 8 : m->cap * 2), );
size_t i = _fnv1a32(&key, sizeof(KTYPE)) & (m->cap - 1);
while (m->data[i].state != EMPTY) {
if (m->data[i].state == TOMBSTONE) {
m->data[i].state = OCCUPIED;
m->data[i].key = key;
m->data[i].val = val;
return OK();
} else if (memcmp(&m->data[i].key, &key, sizeof(KTYPE)) == 0) {
m->data[i].val = val;
return OK();
}
i = (i + 1) % m->cap;
}
m->data[i].state = OCCUPIED;
m->data[i].key = key;
m->data[i].val = val;
m->len++;
return OK();
}
FUNCDEF(bool, _del)(NAME m, KTYPE key) {
size_t i = _fnv1a32(&key, sizeof(KTYPE)) & (m.cap - 1);
while (m.data[i].state != EMPTY) {
if (m.data[i].state != TOMBSTONE && memcmp(&m.data[i].key, &key, sizeof(KTYPE)) == 0) {
m.data[i].state = TOMBSTONE;
return true;
}
i = (i + 1) % m.cap;
}
return false;
}
FUNCDEF(Error, _rehash)(NAME *m, size_t new_minimum_cap) {
size_t new_cap = _pow_of_2_from_minimum(new_minimum_cap > m->len ? new_minimum_cap : m->len);
NAME new_m = {
.data = malloc(sizeof(ITEM_TYPE) * new_cap),
.cap = new_cap,
.len = 0,
};
if (new_m.data == NULL)
return ERROR_OUT_OF_MEMORY();
for (size_t i = 0; i < new_m.cap; i++)
new_m.data[i].state = EMPTY;
for (size_t i = 0; i < m->cap; i++) {
if (m->data[i].state == OCCUPIED) {
size_t j = _fnv1a32(&m->data[i].key, sizeof(KTYPE)) & (new_m.cap - 1);
while (new_m.data[j].state != EMPTY) { j = (j + 1) % new_m.cap; }
new_m.data[j].state = OCCUPIED;
new_m.data[j].key = m->data[i].key;
new_m.data[j].val = m->data[i].val;
new_m.len++;
}
}
free(m->data);
*m = new_m;
return OK();
}
FUNCDEF(bool, _it_next)(NAME m, ITEM_TYPE **it) {
*it == NULL ? *it = m.data : (*it)++;
while (*it < m.data + m.cap && (*it)->state != OCCUPIED) { (*it)++; }
return *it < m.data + m.cap;
}
#endif
#undef ITEM_TYPE
#undef EMPTY
#undef TOMBSTONE
#undef OCCUPIED
#include "../internal/generic/end.h"

211
include/ds/generic/smap.h Normal file
View File

@@ -0,0 +1,211 @@
// Copyright 2022 Darwin Schuppan <darwin@nobrain.org>
// SPDX license identifier: MIT
#include <stdbool.h>
#include <stddef.h>
#include <ds/error.h>
#include <ds/fmt.h>
#include <ds/string.h>
/* Example Usage:
// something.h:
#define GENERIC_TYPE int // Value type
#define GENERIC_NAME StringIntMap // Name of the resulting map type
#define GENERIC_PREFIX string_int_map // Prefix for functions
#include "smap.h"
// something.c:
#define GENERIC_IMPL // We want something.c to define the actual function implementations
#include "something.h"
*/
#define GENERIC_REQUIRE_TYPE
#include "../internal/generic/begin.h"
#define ITEM_TYPE GENERIC_CONCAT(NAME, Item)
#define TOMBSTONE ((char*)UINTPTR_MAX)
typedef struct ITEM_TYPE {
char *key;
TYPE val;
} ITEM_TYPE;
typedef struct NAME {
ITEM_TYPE *data;
size_t cap, len;
} NAME;
VARDECL(const char *, __val_fmt);
FUNCDECL(NAME, )();
FUNCDECL(void, _term)(NAME m);
FUNCDECL(void, _fmt_register)(const char *val_fmt);
FUNCDECL(TYPE *, _get)(NAME m, const char *key);
FUNCDECL(Error, _set)(NAME *m, const char *key, TYPE val);
FUNCDECL(bool, _del)(NAME m, const char *key);
FUNCDECL(Error, _rehash)(NAME *m, size_t new_minimum_cap);
FUNCDECL(bool, _it_next)(NAME m, ITEM_TYPE **it);
#ifdef GENERIC_IMPL
VARDEF(const char *, __val_fmt) = NULL;
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#ifndef _GENERIC_MAP_IMPL_ONCE
#define _GENERIC_MAP_IMPL_ONCE
static size_t _pow_of_2_from_minimum(size_t n) {
n--; /* we want to handle the case of n already being a power of 2 */
/* We use the leftmost bit set to 1 to also set any bits to its right
* to 1. Then we just increment to carry and get our desired power of 2. */
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
#if INTPTR_MAX == INT64_MAX /* only do the last shift on 64-bit systems */
n |= n >> 32;
#endif
return n + 1;
}
static uint32_t _fnv1a32(const void *data, size_t n) {
uint32_t res = 2166136261u;
for (size_t i = 0; i < n; i++) {
res ^= ((uint8_t*)data)[i];
res *= 16777619u;
}
return res;
}
#endif
static FUNCDEF(FmtPrintFuncRet, __print_func)(FmtContext *restrict ctx, FmtAttrs *restrict attrs, va_list v) {
if (attrs != NULL) {
if (attrs->len != 0)
return FMT_PRINT_FUNC_RET_INVALID_ATTR(0);
}
NAME m = va_arg(v, NAME);
ctx->putc_func(ctx, '{');
ITEM_TYPE *it = NULL;
bool first = true;
while (FUNC(_it_next)(m, &it)) {
if (!first)
fmtc(ctx, ", ");
fmtc(ctx, "\"%s\": ", it->key);
fmtc(ctx, VAR(__val_fmt), it->val);
first = false;
}
ctx->putc_func(ctx, '}');
return FMT_PRINT_FUNC_RET_OK();
}
FUNCDEF(NAME, )() {
return (NAME){0};
}
FUNCDEF(void, _term)(NAME m) {
for (size_t i = 0; i < m.cap; i++) {
if (m.data[i].key && m.data[i].key != TOMBSTONE)
free(m.data[i].key);
}
free(m.data);
}
FUNCDEF(void, _fmt_register)(const char *val_fmt) {
VAR(__val_fmt) = val_fmt;
fmt_register(NAME_STR, FUNC(__print_func));
}
FUNCDEF(TYPE *, _get)(NAME m, const char *key) {
size_t i = _fnv1a32(key, strlen(key)) & (m.cap - 1);
while (m.data[i].key) {
if (m.data[i].key != TOMBSTONE && strcmp(m.data[i].key, key) == 0)
return &m.data[i].val;
i = (i + 1) % m.cap;
}
return NULL;
}
FUNCDEF(Error, _set)(NAME *m, const char *key, TYPE val) {
if (m->cap == 0 || (float)m->len / (float)m->cap > 0.7f)
TRY(FUNC(_rehash)(m, m->cap == 0 ? 8 : m->cap * 2), );
size_t i = _fnv1a32(key, strlen(key)) & (m->cap - 1);
while (m->data[i].key) {
if (m->data[i].key == TOMBSTONE) {
char *new_key = strdup(key);
if (new_key == NULL)
return ERROR_OUT_OF_MEMORY();
m->data[i].key = new_key;
m->data[i].val = val;
return OK();
} else if (strcmp(m->data[i].key, key) == 0) {
m->data[i].val = val;
return OK();
}
i = (i + 1) % m->cap;
}
char *new_key = strdup(key);
if (new_key == NULL)
return ERROR_OUT_OF_MEMORY();
m->data[i].key = new_key;
m->data[i].val = val;
m->len++;
return OK();
}
FUNCDEF(bool, _del)(NAME m, const char *key) {
size_t i = _fnv1a32(key, strlen(key)) & (m.cap - 1);
while (m.data[i].key) {
if (m.data[i].key != TOMBSTONE && strcmp(m.data[i].key, key) == 0) {
free(m.data[i].key);
m.data[i].key = TOMBSTONE;
return true;
}
i = (i + 1) % m.cap;
}
return false;
}
FUNCDEF(Error, _rehash)(NAME *m, size_t new_minimum_cap) {
size_t new_cap = _pow_of_2_from_minimum(new_minimum_cap > m->len ? new_minimum_cap : m->len);
NAME new_m = {
.data = malloc(sizeof(ITEM_TYPE) * new_cap),
.cap = new_cap,
.len = 0,
};
if (new_m.data == NULL)
return ERROR_OUT_OF_MEMORY();
for (size_t i = 0; i < new_m.cap; i++)
new_m.data[i].key = NULL;
for (size_t i = 0; i < m->cap; i++) {
if (m->data[i].key && m->data[i].key != TOMBSTONE) {
size_t j = _fnv1a32(m->data[i].key, strlen(m->data[i].key)) & (new_m.cap - 1);
while (new_m.data[j].key) { j = (j + 1) % new_m.cap; }
new_m.data[j].key = m->data[i].key;
new_m.data[j].val = m->data[i].val;
new_m.len++;
}
}
free(m->data);
*m = new_m;
return OK();
}
FUNCDEF(bool, _it_next)(NAME m, ITEM_TYPE **it) {
*it == NULL ? *it = m.data : (*it)++;
while (*it < m.data + m.cap && (!(*it)->key || (*it)->key == TOMBSTONE)) { (*it)++; }
return *it < m.data + m.cap;
}
#endif
#undef ITEM_TYPE
#undef TOMBSTONE
#include "../internal/generic/end.h"

156
include/ds/generic/vec.h Normal file
View File

@@ -0,0 +1,156 @@
// Copyright 2022 Darwin Schuppan <darwin@nobrain.org>
// SPDX license identifier: MIT
/* Example Usage:
// something.h:
#define GENERIC_TYPE int // Key type
#define GENERIC_NAME IntVec // Name of the resulting vector type
#define GENERIC_PREFIX int_vec // Prefix for functions
#include "vec.h"
// something.c:
#define GENERIC_IMPL // We want something.c to define the actual function implementations
#include "something.h"
*/
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <ds/error.h>
#include <ds/fmt.h>
#define GENERIC_REQUIRE_TYPE
#include "../internal/generic/begin.h"
#ifndef _GENERIC_VEC_ONCE
#define _GENERIC_VEC_ONCE
size_t vec_len(void *v);
size_t vec_cap(void *v);
#endif
typedef TYPE *NAME;
VARDECL(const char *, __val_fmt);
FUNCDECL(NAME, )();
FUNCDECL(void, _term)(NAME v);
FUNCDECL(void, _fmt_register)(const char *val_fmt);
static inline FUNCDEF(size_t, _len)(NAME v) { return vec_len((void*)v); }
static inline FUNCDEF(size_t, _cap)(NAME v) { return vec_cap((void*)v); }
FUNCDECL(size_t, _len)(NAME v);
FUNCDECL(size_t, _cap)(NAME v);
FUNCDECL(Error, _fit)(NAME *v, size_t new_minimum_cap);
FUNCDECL(Error, _push)(NAME *v, TYPE val);
FUNCDECL(TYPE, _pop)(NAME v);
FUNCDECL(TYPE *, _back)(NAME v);
FUNCDECL(TYPE, _del)(NAME v, size_t idx);
FUNCDECL(Error, _insert)(NAME *v, size_t idx, TYPE val);
#ifdef GENERIC_IMPL
VARDEF(const char *, __val_fmt) = NULL;
#define _VEC_HEADER(vec) ((_VecHeader*)(vec) - 1)
#ifndef _GENERIC_VEC_IMPL_ONCE
#define _GENERIC_VEC_IMPL_ONCE
typedef struct _VecHeader {
size_t cap, len;
} _VecHeader;
size_t vec_len(void *v) {
return v == NULL ? 0 : _VEC_HEADER(v)->len;
}
size_t vec_cap(void *v) {
return v == NULL ? 0 : _VEC_HEADER(v)->cap;
}
#endif
static FUNCDEF(FmtPrintFuncRet, __print_func)(FmtContext *restrict ctx, FmtAttrs *restrict attrs, va_list v) {
if (attrs != NULL) {
if (attrs->len != 0)
return FMT_PRINT_FUNC_RET_INVALID_ATTR(0);
}
NAME vec = va_arg(v, NAME);
ctx->putc_func(ctx, '{');
for (size_t i = 0; i < vec_len(vec); i++) {
if (i != 0)
fmtc(ctx, ", ");
fmtc(ctx, VAR(__val_fmt), vec[i]);
}
ctx->putc_func(ctx, '}');
return FMT_PRINT_FUNC_RET_OK();
}
FUNCDEF(NAME, )() {
return NULL;
}
FUNCDEF(void, _term)(NAME v) {
free(_VEC_HEADER(v));
}
FUNCDEF(void, _fmt_register)(const char *val_fmt) {
VAR(__val_fmt) = val_fmt;
fmt_register(NAME_STR, FUNC(__print_func));
}
FUNCDEF(Error, _fit)(NAME *v, size_t new_minimum_cap) {
size_t new_cap;
size_t len;
_VecHeader *h;
if (*v == NULL) {
h = NULL;
new_cap = new_minimum_cap;
len = 0;
} else {
h = _VEC_HEADER(*v);
new_cap = new_minimum_cap < h->len ? h->len : new_minimum_cap;
len = h->len;
}
_VecHeader *new_h = realloc(h, sizeof(_VecHeader) + sizeof(TYPE) * new_cap);
if (new_h == NULL)
return ERROR_OUT_OF_MEMORY();
new_h->len = len;
new_h->cap = new_minimum_cap;
*v = (NAME)(new_h + 1);
return OK();
}
FUNCDEF(Error, _push)(NAME *v, TYPE val) {
if (vec_len(*v) + 1 > vec_cap(*v))
TRY(FUNC(_fit)(v, vec_cap(*v) == 0 ? 8 : vec_cap(*v) * 2), );
(*v)[_VEC_HEADER(*v)->len++] = val;
return OK();
}
FUNCDEF(TYPE, _pop)(NAME v) {
return v[--_VEC_HEADER(v)->len];
}
FUNCDEF(TYPE *, _back)(NAME v) {
return &v[vec_len(v) - 1];
}
FUNCDEF(TYPE, _del)(NAME v, size_t idx) {
TYPE val = v[idx];
memmove(v + idx, v + idx + 1, sizeof(TYPE) * (--_VEC_HEADER(v)->len - idx));
return val;
}
FUNCDEF(Error, _insert)(NAME *v, size_t idx, TYPE val) {
if (vec_len(*v) + 1 > vec_cap(*v))
TRY(FUNC(_fit)(v, vec_cap(*v) == 0 ? 8 : vec_cap(*v) * 2), );
memmove(*v + idx + 1, *v + idx, sizeof(TYPE) * (_VEC_HEADER(*v)->len++ - idx));
(*v)[idx] = val;
return OK();
}
#undef _VEC_HEADER
#endif
#include "../internal/generic/end.h"

View File

@@ -0,0 +1,70 @@
// Copyright 2022 Darwin Schuppan <darwin@nobrain.org>
// SPDX license identifier: MIT
#if !defined(GENERIC_NAME) || !defined(GENERIC_PREFIX)
#error GENERIC_NAME and GENERIC_PREFIX must be defined before including a header for defining a generic class
#endif
#define _GENERIC_CONCAT(pfx, x) pfx##x
#define GENERIC_CONCAT(pfx, x) _GENERIC_CONCAT(pfx, x)
#define _GENERIC_STRINGIZE(s) #s
#define GENERIC_STRINGIZE(s) _GENERIC_STRINGIZE(s)
#define NAME GENERIC_NAME
#define NAME_STR GENERIC_STRINGIZE(NAME)
#define FUNC(name) GENERIC_CONCAT(GENERIC_PREFIX, name)
#define VAR(name) GENERIC_CONCAT(GENERIC_PREFIX, name)
#ifdef GENERIC_IMPL_STATIC
#define GENERIC_IMPL
#define FUNCDECL(ret_type, name) __attribute__((unused)) static ret_type FUNC(name)
#define FUNCDEF(ret_type, name) __attribute__((unused)) ret_type FUNC(name)
#define VARDECL(type, name) static type VAR(name)
#define VARDEF(type, name) static type VAR(name)
#else
#define FUNCDECL(ret_type, name) ret_type FUNC(name)
#define FUNCDEF(ret_type, name) ret_type FUNC(name)
#define VARDECL(type, name) extern type VAR(name)
#define VARDEF(type, name) type VAR(name)
#endif
#ifdef GENERIC_REQUIRE_TYPE
#define GENERIC_ALLOW_TYPE
#endif
#ifdef GENERIC_REQUIRE_VALUE_TYPE
#define GENERIC_ALLOW_VALUE_TYPE
#endif
#ifdef GENERIC_REQUIRE_KEY_TYPE
#define GENERIC_ALLOW_KEY_TYPE
#endif
#if defined(GENERIC_REQUIRE_TYPE) && !defined(GENERIC_TYPE)
#error Defining GENERIC_TYPE is required for defining this generic class
#endif
#if defined(GENERIC_REQUIRE_VALUE_TYPE) && !defined(GENERIC_VALUE_TYPE)
#error Defining GENERIC_VALUE_TYPE is required for defining this generic class
#endif
#if defined(GENERIC_REQUIRE_KEY_TYPE) && !defined(GENERIC_KEY_TYPE)
#error Defining GENERIC_KEY_TYPE is required for defining this generic class
#endif
#if !defined(GENERIC_ALLOW_TYPE) && defined(GENERIC_TYPE)
#error Defining GENERIC_TYPE is not allowed for defining this generic class
#endif
#if !defined(GENERIC_ALLOW_VALUE_TYPE) && defined(GENERIC_VALUE_TYPE)
#error Defining GENERIC_VALUE_TYPE is not allowed for defining this generic class
#endif
#if !defined(GENERIC_ALLOW_KEY_TYPE) && defined(GENERIC_KEY_TYPE)
#error Defining GENERIC_KEY_TYPE is not allowed for defining this generic class
#endif
#if defined(GENERIC_TYPE)
#define TYPE GENERIC_TYPE
#endif
#if defined(GENERIC_VALUE_TYPE)
#define VTYPE GENERIC_VALUE_TYPE
#endif
#if defined(GENERIC_KEY_TYPE)
#define KTYPE GENERIC_KEY_TYPE
#endif

View File

@@ -0,0 +1,53 @@
// Copyright 2022 Darwin Schuppan <darwin@nobrain.org>
// SPDX license identifier: MIT
#if defined(GENERIC_TYPE)
#undef TYPE
#undef GENERIC_TYPE
#endif
#if defined(GENERIC_VALUE_TYPE)
#undef VTYPE
#undef GENERIC_VALUE_TYPE
#endif
#if defined(GENERIC_KEY_TYPE)
#undef KTYPE
#undef GENERIC_KEY_TYPE
#endif
#ifdef GENERIC_REQUIRE_TYPE
#undef GENERIC_REQUIRE_TYPE
#endif
#ifdef GENERIC_REQUIRE_VALUE_TYPE
#undef GENERIC_REQUIRE_VALUE_TYPE
#endif
#ifdef GENERIC_REQUIRE_KEY_TYPE
#undef GENERIC_REQUIRE_KEY_TYPE
#endif
#ifdef GENERIC_ALLOW_TYPE
#undef GENERIC_ALLOW_TYPE
#endif
#ifdef GENERIC_ALLOW_VALUE_TYPE
#undef GENERIC_ALLOW_VALUE_TYPE
#endif
#ifdef GENERIC_ALLOW_KEY_TYPE
#undef GENERIC_ALLOW_KEY_TYPE
#endif
#undef GENERIC_PREFIX
#undef FUNC
#undef FUNCDECL
#undef FUNCDEF
#undef VAR
#undef VARDECL
#undef VARDEF
#undef NAME
#undef NAME_STR
#undef GENERIC_NAME
#undef _GENERIC_STRINGIZE
#undef GENERIC_STRINGIZE
#undef _GENERIC_CONCAT
#undef GENERIC_CONCAT

11
include/ds/string.h Normal file
View File

@@ -0,0 +1,11 @@
// Copyright 2022 Darwin Schuppan <darwin@nobrain.org>
// SPDX license identifier: MIT
#ifndef __DS_STRING_H__
#define __DS_STRING_H__
#ifndef strdup /* we may want to replace strdup for testing */
char *strdup(const char *s);
#endif
#endif

15
include/ds/types.h Normal file
View File

@@ -0,0 +1,15 @@
// Copyright 2022 Darwin Schuppan <darwin@nobrain.org>
// SPDX license identifier: MIT
#ifndef __DS_TYPE_H__
#define __DS_TYPE_H__
/* ssize_t */
#ifdef _WIN32
#include <windows.h>
typedef SSIZE_T ssize_t;
#else
#include <sys/types.h>
#endif
#endif