init
This commit is contained in:
100
include/ds/error.h
Normal file
100
include/ds/error.h
Normal 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
92
include/ds/fmt.h
Normal 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
214
include/ds/generic/map.h
Normal 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
211
include/ds/generic/smap.h
Normal 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
156
include/ds/generic/vec.h
Normal 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"
|
||||
70
include/ds/internal/generic/begin.h
Normal file
70
include/ds/internal/generic/begin.h
Normal 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
|
||||
53
include/ds/internal/generic/end.h
Normal file
53
include/ds/internal/generic/end.h
Normal 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
11
include/ds/string.h
Normal 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
15
include/ds/types.h
Normal 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
|
||||
Reference in New Issue
Block a user