356 lines
14 KiB
C++
356 lines
14 KiB
C++
/* MIT License
|
|
*
|
|
* Copyright (c) 2025 Tyge Løvset
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
#ifndef STC_COMMON_H_INCLUDED
|
|
#define STC_COMMON_H_INCLUDED
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(disable: 4116 4996) // unnamed type definition in parentheses
|
|
#endif
|
|
#include <inttypes.h>
|
|
#include <stddef.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
typedef ptrdiff_t isize;
|
|
#ifndef STC_NO_INT_DEFS
|
|
typedef int8_t int8;
|
|
typedef uint8_t uint8;
|
|
typedef int16_t int16;
|
|
typedef uint16_t uint16;
|
|
typedef int32_t int32;
|
|
typedef uint32_t uint32;
|
|
typedef int64_t int64;
|
|
typedef uint64_t uint64;
|
|
#endif
|
|
#if !defined STC_HAS_TYPEOF && (_MSC_FULL_VER >= 193933428 || \
|
|
defined __GNUC__ || defined __clang__ || defined __TINYC__)
|
|
#define STC_HAS_TYPEOF 1
|
|
#endif
|
|
#if defined __GNUC__
|
|
#define c_GNUATTR(...) __attribute__((__VA_ARGS__))
|
|
#else
|
|
#define c_GNUATTR(...)
|
|
#endif
|
|
#define STC_INLINE static inline c_GNUATTR(unused)
|
|
#define c_ZI PRIiPTR
|
|
#define c_ZU PRIuPTR
|
|
#define c_NPOS INTPTR_MAX
|
|
|
|
// Macro overloading feature support
|
|
#define c_MACRO_OVERLOAD(name, ...) \
|
|
c_JOIN(name ## _,c_NUMARGS(__VA_ARGS__))(__VA_ARGS__)
|
|
#define c_JOIN0(a, b) a ## b
|
|
#define c_JOIN(a, b) c_JOIN0(a, b)
|
|
#define c_NUMARGS(...) _c_APPLY_ARG_N((__VA_ARGS__, _c_RSEQ_N))
|
|
#define _c_APPLY_ARG_N(args) _c_ARG_N args
|
|
#define _c_RSEQ_N 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
|
|
#define _c_ARG_N(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,N,...) N
|
|
|
|
// Saturated overloading
|
|
// #define foo(...) foo_I(__VA_ARGS__, c_COMMA_N(foo_3), c_COMMA_N(foo_2), c_COMMA_N(foo_1),)(__VA_ARGS__)
|
|
// #define foo_I(a,b,c, n, ...) c_TUPLE_AT_1(n, foo_n,)
|
|
#define c_TUPLE_AT_1(x,y,...) y
|
|
#define c_COMMA_N(x) ,x
|
|
#define c_EXPAND(...) __VA_ARGS__
|
|
|
|
// Select arg, e.g. for #define i_type A,B then c_GETARG(2, i_type) is B
|
|
#define c_GETARG(N, ...) c_ARG_##N(__VA_ARGS__,)
|
|
#define c_ARG_1(a, ...) a
|
|
#define c_ARG_2(a, b, ...) b
|
|
#define c_ARG_3(a, b, c, ...) c
|
|
#define c_ARG_4(a, b, c, d, ...) d
|
|
|
|
#define _i_new_n(T, n) ((T*)i_malloc((n)*c_sizeof(T)))
|
|
#define _i_new_zeros(T, n) ((T*)i_calloc(n, c_sizeof(T)))
|
|
#define _i_realloc_n(ptr, old_n, n) i_realloc(ptr, (old_n)*c_sizeof *(ptr), (n)*c_sizeof *(ptr))
|
|
#define _i_free_n(ptr, n) i_free(ptr, (n)*c_sizeof *(ptr))
|
|
|
|
#ifndef __cplusplus
|
|
#define c_new(T, ...) ((T*)c_safe_memcpy(c_malloc(c_sizeof(T)), ((T[]){__VA_ARGS__}), c_sizeof(T)))
|
|
#define c_literal(T) (T)
|
|
#define c_make_array(T, ...) ((T[])__VA_ARGS__)
|
|
#define c_make_array2d(T, N, ...) ((T[][N])__VA_ARGS__)
|
|
#else
|
|
#include <new>
|
|
#define c_new(T, ...) new (c_malloc(c_sizeof(T))) T(__VA_ARGS__)
|
|
#define c_literal(T) T
|
|
template<typename T, int M, int N> struct _c_Array { T data[M][N]; };
|
|
#define c_make_array(T, ...) (_c_Array<T, 1, sizeof((T[])__VA_ARGS__)/sizeof(T)>{{__VA_ARGS__}}.data[0])
|
|
#define c_make_array2d(T, N, ...) (_c_Array<T, sizeof((T[][N])__VA_ARGS__)/sizeof(T[N]), N>{__VA_ARGS__}.data)
|
|
#endif
|
|
|
|
#ifdef STC_ALLOCATOR
|
|
#define c_malloc c_JOIN(STC_ALLOCATOR, _malloc)
|
|
#define c_calloc c_JOIN(STC_ALLOCATOR, _calloc)
|
|
#define c_realloc c_JOIN(STC_ALLOCATOR, _realloc)
|
|
#define c_free c_JOIN(STC_ALLOCATOR, _free)
|
|
#else
|
|
#define c_malloc(sz) malloc(c_i2u_size(sz))
|
|
#define c_calloc(n, sz) calloc(c_i2u_size(n), c_i2u_size(sz))
|
|
#define c_realloc(ptr, old_sz, sz) realloc(ptr, c_i2u_size(1 ? (sz) : (old_sz)))
|
|
#define c_free(ptr, sz) ((void)(sz), free(ptr))
|
|
#endif
|
|
|
|
#define c_new_n(T, n) ((T*)c_malloc((n)*c_sizeof(T)))
|
|
#define c_free_n(ptr, n) c_free(ptr, (n)*c_sizeof *(ptr))
|
|
#define c_realloc_n(ptr, old_n, n) c_realloc(ptr, (old_n)*c_sizeof *(ptr), (n)*c_sizeof *(ptr))
|
|
#define c_delete_n(T, ptr, n) do { \
|
|
T* _tp = ptr; isize _n = n, _i = _n; \
|
|
while (_i--) T##_drop((_tp + _i)); \
|
|
c_free(_tp, _n*c_sizeof(T)); \
|
|
} while (0)
|
|
|
|
#define c_static_assert(expr) (void)sizeof(int[(expr) ? 1 : -1])
|
|
#if defined STC_NDEBUG || defined NDEBUG
|
|
#define c_assert(expr) (void)sizeof(expr)
|
|
#else
|
|
#define c_assert(expr) assert(expr)
|
|
#endif
|
|
#define c_container_of(p, C, m) ((C*)((char*)(1 ? (p) : &((C*)0)->m) - offsetof(C, m)))
|
|
#define c_const_cast(Tp, p) ((Tp)(1 ? (p) : (Tp)0))
|
|
#define c_litstrlen(literal) (c_sizeof("" literal) - 1)
|
|
#define c_countof(a) (isize)(sizeof(a)/sizeof 0[a])
|
|
#define c_arraylen(a) c_countof(a) // [deprecated]?
|
|
|
|
// expect signed ints to/from these (use with gcc -Wconversion)
|
|
#define c_sizeof (isize)sizeof
|
|
#define c_strlen(s) (isize)strlen(s)
|
|
#define c_strncmp(a, b, ilen) strncmp(a, b, c_i2u_size(ilen))
|
|
#define c_memcpy(d, s, ilen) memcpy(d, s, c_i2u_size(ilen))
|
|
#define c_memmove(d, s, ilen) memmove(d, s, c_i2u_size(ilen))
|
|
#define c_memset(d, val, ilen) memset(d, val, c_i2u_size(ilen))
|
|
#define c_memcmp(a, b, ilen) memcmp(a, b, c_i2u_size(ilen))
|
|
// library internal, but may be useful in user code:
|
|
#define c_u2i_size(u) (isize)(1 ? (u) : (size_t)1) // warns if u is signed
|
|
#define c_i2u_size(i) (size_t)(1 ? (i) : -1) // warns if i is unsigned
|
|
#define c_uless(a, b) ((size_t)(a) < (size_t)(b))
|
|
#define c_safe_cast(T, From, x) ((T)(1 ? (x) : (From){0}))
|
|
|
|
// x, y are i_keyraw* type, which defaults to i_key*. vp is i_key* type.
|
|
#define c_memcmp_eq(x, y) (memcmp(x, y, sizeof *(x)) == 0)
|
|
#define c_default_eq(x, y) (*(x) == *(y))
|
|
#define c_default_less(x, y) (*(x) < *(y))
|
|
#define c_default_cmp(x, y) (c_default_less(y, x) - c_default_less(x, y))
|
|
#define c_default_hash(vp) c_hash_n(vp, sizeof *(vp))
|
|
#define c_default_clone(v) (v)
|
|
#define c_default_toraw(vp) (*(vp))
|
|
#define c_default_drop(vp) ((void) (vp))
|
|
|
|
// non-owning char pointer
|
|
typedef const char* cstr_raw;
|
|
#define cstr_raw_cmp(x, y) strcmp(*(x), *(y))
|
|
#define cstr_raw_eq(x, y) (cstr_raw_cmp(x, y) == 0)
|
|
#define cstr_raw_hash(vp) c_hash_str(*(vp))
|
|
#define cstr_raw_clone(v) (v)
|
|
#define cstr_raw_drop(vp) ((void)vp)
|
|
|
|
// Control block macros
|
|
|
|
// [deprecated]:
|
|
#define c_init(...) c_make(__VA_ARGS__)
|
|
#define c_forlist(...) for (c_items(_VA_ARGS__))
|
|
#define c_foritems(...) for (c_items(__VA_ARGS__))
|
|
#define c_foreach(...) for (c_each(__VA_ARGS__))
|
|
#define c_foreach_n(...) for (c_each_n(__VA_ARGS__))
|
|
#define c_foreach_kv(...) for (c_each_kv(__VA_ARGS__))
|
|
#define c_foreach_reverse(...) for (c_each_reverse(__VA_ARGS__))
|
|
#define c_forrange(...) for (c_range(__VA_ARGS__))
|
|
#define c_forrange32(...) for (c_range32(__VA_ARGS__))
|
|
|
|
// New:
|
|
#define c_each(...) c_MACRO_OVERLOAD(c_each, __VA_ARGS__)
|
|
#define c_each_3(it, C, cnt) \
|
|
C##_iter it = C##_begin(&cnt); it.ref; C##_next(&it)
|
|
#define c_each_4(it, C, start, end) \
|
|
_c_each(it, C, start, (end).ref, _)
|
|
|
|
#define c_each_n(...) c_MACRO_OVERLOAD(c_each_n, __VA_ARGS__)
|
|
#define c_each_n_3(it, C, cnt) c_each_n_4(it, C, cnt, INTPTR_MAX)
|
|
#define c_each_n_4(it, C, cnt, n) \
|
|
struct {C##_iter iter; C##_value* ref; isize size, index;} \
|
|
it = {.iter=C##_begin(&cnt), .size=n}; (it.ref = it.iter.ref) && it.index < it.size; C##_next(&it.iter), ++it.index
|
|
|
|
#define c_each_reverse(...) c_MACRO_OVERLOAD(c_each_reverse, __VA_ARGS__)
|
|
#define c_each_reverse_3(it, C, cnt) /* works for stack, vec, queue, deque */ \
|
|
C##_iter it = C##_rbegin(&cnt); it.ref; C##_rnext(&it)
|
|
#define c_each_reverse_4(it, C, start, end) \
|
|
_c_each(it, C, start, (end).ref, _r)
|
|
|
|
#define _c_each(it, C, start, endref, rev) /* private */ \
|
|
C##_iter it = (start), *_endref_##it = c_safe_cast(C##_iter*, C##_value*, endref) \
|
|
; it.ref != (C##_value*)_endref_##it; C##rev##next(&it)
|
|
|
|
#define c_each_kv(...) c_MACRO_OVERLOAD(c_each_kv, __VA_ARGS__)
|
|
#define c_each_kv_4(key, val, C, cnt) /* structured binding for maps */ \
|
|
_c_each_kv(key, val, C, C##_begin(&cnt), NULL)
|
|
#define c_each_kv_5(key, val, C, start, end) \
|
|
_c_each_kv(key, val, C, start, (end).ref)
|
|
|
|
#define _c_each_kv(key, val, C, start, endref) /* private */ \
|
|
const C##_key *key = (const C##_key*)&key; key; ) \
|
|
for (C##_mapped *val; key; key = NULL) \
|
|
for (C##_iter _it_##key = start, *_endref_##key = c_safe_cast(C##_iter*, C##_value*, endref); \
|
|
_it_##key.ref != (C##_value*)_endref_##key && (key = &_it_##key.ref->first, val = &_it_##key.ref->second); \
|
|
C##_next(&_it_##key)
|
|
|
|
#define c_items(it, T, ...) \
|
|
struct {T* ref; int size, index;} \
|
|
it = {.ref=c_make_array(T, __VA_ARGS__), .size=(int)(sizeof((T[])__VA_ARGS__)/sizeof(T))} \
|
|
; it.index < it.size ; ++it.ref, ++it.index
|
|
|
|
// c_range, c_range32: python-like int range iteration
|
|
#define c_range_t(...) c_MACRO_OVERLOAD(c_range_t, __VA_ARGS__)
|
|
#define c_range_t_3(T, i, stop) c_range_t_4(T, i, 0, stop)
|
|
#define c_range_t_4(T, i, start, stop) \
|
|
T i=start, _c_end_##i=stop; i < _c_end_##i; ++i
|
|
#define c_range_t_5(T, i, start, stop, step) \
|
|
T i=start, _c_inc_##i=step, _c_end_##i=(stop) - (_c_inc_##i > 0) \
|
|
; (_c_inc_##i > 0) == (i <= _c_end_##i) ; i += _c_inc_##i
|
|
|
|
#define c_range(...) c_MACRO_OVERLOAD(c_range, __VA_ARGS__)
|
|
#define c_range_1(stop) c_range_t_4(isize, _c_i1, 0, stop)
|
|
#define c_range_2(i, stop) c_range_t_4(isize, i, 0, stop)
|
|
#define c_range_3(i, start, stop) c_range_t_4(isize, i, start, stop)
|
|
#define c_range_4(i, start, stop, step) c_range_t_5(isize, i, start, stop, step)
|
|
|
|
#define c_range32(...) c_MACRO_OVERLOAD(c_range32, __VA_ARGS__)
|
|
#define c_range32_2(i, stop) c_range_t_4(int32_t, i, 0, stop)
|
|
#define c_range32_3(i, start, stop) c_range_t_4(int32_t, i, start, stop)
|
|
#define c_range32_4(i, start, stop, step) c_range_t_5(int32_t, i, start, stop, step)
|
|
|
|
// make container from a literal list
|
|
#define c_make(C, ...) \
|
|
C##_from_n(c_make_array(C##_raw, __VA_ARGS__), c_sizeof((C##_raw[])__VA_ARGS__)/c_sizeof(C##_raw))
|
|
|
|
// put multiple raw-type elements from a literal list into a container
|
|
#define c_put_items(C, cnt, ...) \
|
|
C##_put_n(cnt, c_make_array(C##_raw, __VA_ARGS__), c_sizeof((C##_raw[])__VA_ARGS__)/c_sizeof(C##_raw))
|
|
|
|
// drop multiple containers of same type
|
|
#define c_drop(C, ...) \
|
|
do { for (c_items(_c_i2, C*, {__VA_ARGS__})) C##_drop(*_c_i2.ref); } while(0)
|
|
|
|
// RAII scopes
|
|
#define c_defer(...) \
|
|
for (int _c_i3 = 0; _c_i3++ == 0; __VA_ARGS__)
|
|
|
|
#define c_with(...) c_MACRO_OVERLOAD(c_with, __VA_ARGS__)
|
|
#define c_with_2(init, deinit) \
|
|
for (int _c_i4 = 0; _c_i4 == 0; ) for (init; _c_i4++ == 0; deinit)
|
|
#define c_with_3(init, condition, deinit) \
|
|
for (int _c_i5 = 0; _c_i5 == 0; ) for (init; _c_i5++ == 0 && (condition); deinit)
|
|
|
|
// General functions
|
|
|
|
STC_INLINE void* c_safe_memcpy(void* dst, const void* src, isize size)
|
|
{ return dst ? memcpy(dst, src, (size_t)size) : NULL; }
|
|
|
|
#if INTPTR_MAX == INT64_MAX
|
|
#define FNV_BASIS 0xcbf29ce484222325
|
|
#define FNV_PRIME 0x00000100000001b3
|
|
#else
|
|
#define FNV_BASIS 0x811c9dc5
|
|
#define FNV_PRIME 0x01000193
|
|
#endif
|
|
|
|
STC_INLINE size_t c_basehash_n(const void* key, isize len) {
|
|
const uint8_t* msg = (const uint8_t*)key;
|
|
size_t h = FNV_BASIS, block = 0;
|
|
|
|
while (len >= c_sizeof h) {
|
|
memcpy(&block, msg, sizeof h);
|
|
h ^= block;
|
|
h *= FNV_PRIME;
|
|
msg += c_sizeof h;
|
|
len -= c_sizeof h;
|
|
}
|
|
while (len--) {
|
|
h ^= *(msg++);
|
|
h *= FNV_PRIME;
|
|
}
|
|
return h;
|
|
}
|
|
|
|
STC_INLINE size_t c_hash_n(const void* key, isize len) {
|
|
uint64_t b8; uint32_t b4;
|
|
switch (len) {
|
|
case 8: memcpy(&b8, key, 8); return (size_t)(b8 * 0xc6a4a7935bd1e99d);
|
|
case 4: memcpy(&b4, key, 4); return b4 * FNV_BASIS;
|
|
default: return c_basehash_n(key, len);
|
|
}
|
|
}
|
|
|
|
STC_INLINE size_t c_hash_str(const char *str) {
|
|
const uint8_t* msg = (const uint8_t*)str;
|
|
size_t h = FNV_BASIS;
|
|
while (*msg) {
|
|
h ^= *(msg++);
|
|
h *= FNV_PRIME;
|
|
}
|
|
return h;
|
|
}
|
|
|
|
#define c_hash_mix(...) /* non-commutative hash combine */ \
|
|
c_hash_mix_n(c_make_array(size_t, {__VA_ARGS__}), c_sizeof((size_t[]){__VA_ARGS__})/c_sizeof(size_t))
|
|
|
|
STC_INLINE size_t c_hash_mix_n(size_t h[], isize n) {
|
|
for (isize i = 1; i < n; ++i) h[0] += h[0] ^ h[i];
|
|
return h[0];
|
|
}
|
|
|
|
// generic typesafe swap
|
|
#define c_swap(xp, yp) do { \
|
|
(void)sizeof((xp) == (yp)); \
|
|
char _tv[sizeof *(xp)]; \
|
|
void *_xp = xp, *_yp = yp; \
|
|
memcpy(_tv, _xp, sizeof _tv); \
|
|
memcpy(_xp, _yp, sizeof _tv); \
|
|
memcpy(_yp, _tv, sizeof _tv); \
|
|
} while (0)
|
|
|
|
// get next power of two
|
|
STC_INLINE isize c_next_pow2(isize n) {
|
|
n--;
|
|
n |= n >> 1, n |= n >> 2;
|
|
n |= n >> 4, n |= n >> 8;
|
|
n |= n >> 16;
|
|
#if INTPTR_MAX == INT64_MAX
|
|
n |= n >> 32;
|
|
#endif
|
|
return n + 1;
|
|
}
|
|
|
|
STC_INLINE char* c_strnstrn(const char *str, isize slen, const char *needle, isize nlen) {
|
|
if (nlen == 0) return (char *)str;
|
|
if (nlen > slen) return NULL;
|
|
slen -= nlen;
|
|
do {
|
|
if (*str == *needle && !c_memcmp(str, needle, nlen))
|
|
return (char *)str;
|
|
++str;
|
|
} while (slen--);
|
|
return NULL;
|
|
}
|
|
#endif // STC_COMMON_H_INCLUDED
|