diff options
Diffstat (limited to 'libmisc/include')
l--------- | libmisc/include/assert.h | 1 | ||||
-rw-r--r-- | libmisc/include/libmisc/_intercept.h | 27 | ||||
-rw-r--r-- | libmisc/include/libmisc/assert.h | 24 | ||||
-rw-r--r-- | libmisc/include/libmisc/endian.h | 84 | ||||
-rw-r--r-- | libmisc/include/libmisc/linkedlist.h | 108 | ||||
-rw-r--r-- | libmisc/include/libmisc/log.h | 34 | ||||
-rw-r--r-- | libmisc/include/libmisc/macro.h | 107 | ||||
-rw-r--r-- | libmisc/include/libmisc/map.h | 146 | ||||
-rw-r--r-- | libmisc/include/libmisc/private.h | 38 | ||||
-rw-r--r-- | libmisc/include/libmisc/rand.h | 46 | ||||
-rw-r--r-- | libmisc/include/libmisc/vcall.h | 27 |
11 files changed, 584 insertions, 58 deletions
diff --git a/libmisc/include/assert.h b/libmisc/include/assert.h new file mode 120000 index 0000000..8473e36 --- /dev/null +++ b/libmisc/include/assert.h @@ -0,0 +1 @@ +libmisc/assert.h
\ No newline at end of file diff --git a/libmisc/include/libmisc/_intercept.h b/libmisc/include/libmisc/_intercept.h new file mode 100644 index 0000000..a264144 --- /dev/null +++ b/libmisc/include/libmisc/_intercept.h @@ -0,0 +1,27 @@ +/* libmisc/_intercept.h - Interceptable ("weak") functions + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _LIBMISC__INTERCEPT_H_ +#define _LIBMISC__INTERCEPT_H_ + +/* pico-sdk/newlib define these to be [[gnu:weak]] already, but + * depending on optimization options glibc might not, and GCC might + * assume it knows what they do and optimize them out. So define our + * own `__lm_` wrappers that GCC/glibc won't interfere with. + */ + +[[gnu::format(printf, 1, 2)]] +int __lm_printf(const char *format, ...); + +[[noreturn]] void __lm_abort(void); + +/* __lm_light_printf is expected to have less stack use than regular + * __lm_printf, if possible. */ + +[[gnu::format(printf, 1, 2)]] +int __lm_light_printf(const char *format, ...); + +#endif /* _LIBMISC__INTERCEPT_H_ */ diff --git a/libmisc/include/libmisc/assert.h b/libmisc/include/libmisc/assert.h new file mode 100644 index 0000000..8cf0735 --- /dev/null +++ b/libmisc/include/libmisc/assert.h @@ -0,0 +1,24 @@ +/* libmisc/assert.h - More assertions + * + * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _LIBMISC_ASSERT_H_ +#define _LIBMISC_ASSERT_H_ + +#ifdef NDEBUG +# define __assert_msg(expr, expr_str, msg) ((void)0) +#else +# define __assert_msg(expr, expr_str, msg) do { if (!(expr)) __assert_msg_fail(expr_str, __FILE__, __LINE__, __func__, msg); } while (0) +[[noreturn]] void __assert_msg_fail(const char *expr, + const char *file, unsigned int line, const char *func, + const char *msg); +#endif + +#define assert_msg(expr, msg) __assert_msg(expr, #expr, msg) /* libmisc */ +#define assert(expr) __assert_msg(expr, #expr, 0) /* C89, POSIX-2001 */ +#define assert_notreached(msg) do { __assert_msg(0, "notreached", msg); __builtin_unreachable(); } while (0) /* libmisc */ +#define static_assert _Static_assert /* C11 */ + +#endif /* _LIBMISC_ASSERT_H_ */ diff --git a/libmisc/include/libmisc/endian.h b/libmisc/include/libmisc/endian.h index 24d7d42..75240fe 100644 --- a/libmisc/include/libmisc/endian.h +++ b/libmisc/include/libmisc/endian.h @@ -1,15 +1,17 @@ /* libmisc/endian.h - Endian-conversion helpers * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later */ #ifndef _LIBMISC_ENDIAN_H_ #define _LIBMISC_ENDIAN_H_ -#include <assert.h> +#include <stddef.h> /* for size_t */ #include <stdint.h> /* for uint{n}_t */ +#include <libmisc/assert.h> + /* Big endian *****************************************************************/ typedef struct { @@ -70,6 +72,45 @@ static inline uint32_t uint32be_unmarshal(uint32be_t in) { return uint32be_decode(in.octets); } +typedef struct { + uint8_t octets[8]; +} uint64be_t; +static_assert(sizeof(uint64be_t) == 8); + +static inline size_t uint64be_encode(uint8_t *out, uint64_t in) { + out[0] = (uint8_t)((in >> 56) & 0xFF); + out[1] = (uint8_t)((in >> 48) & 0xFF); + out[2] = (uint8_t)((in >> 40) & 0xFF); + out[3] = (uint8_t)((in >> 32) & 0xFF); + out[4] = (uint8_t)((in >> 24) & 0xFF); + out[5] = (uint8_t)((in >> 16) & 0xFF); + out[6] = (uint8_t)((in >> 8) & 0xFF); + out[7] = (uint8_t)((in >> 0) & 0xFF); + return 8; +} + +static inline uint64_t uint64be_decode(uint8_t *in) { + return (((uint64_t)(in[0])) << 56) + | (((uint64_t)(in[1])) << 48) + | (((uint64_t)(in[2])) << 40) + | (((uint64_t)(in[3])) << 32) + | (((uint64_t)(in[4])) << 24) + | (((uint64_t)(in[5])) << 16) + | (((uint64_t)(in[6])) << 8) + | (((uint64_t)(in[7])) << 0) + ; +} + +static inline uint64be_t uint64be_marshal(uint64_t in) { + uint64be_t out; + uint64be_encode(out.octets, in); + return out; +} + +static inline uint64_t uint64be_unmarshal(uint64be_t in) { + return uint64be_decode(in.octets); +} + /* Little endian **************************************************************/ typedef struct { @@ -130,4 +171,43 @@ static inline uint32_t uint32le_unmarshal(uint32le_t in) { return uint32le_decode(in.octets); } +typedef struct { + uint8_t octets[8]; +} uint64le_t; +static_assert(sizeof(uint64le_t) == 8); + +static inline size_t uint64le_encode(uint8_t *out, uint64_t in) { + out[0] = (uint8_t)((in >> 0) & 0xFF); + out[1] = (uint8_t)((in >> 8) & 0xFF); + out[2] = (uint8_t)((in >> 16) & 0xFF); + out[3] = (uint8_t)((in >> 24) & 0xFF); + out[4] = (uint8_t)((in >> 32) & 0xFF); + out[5] = (uint8_t)((in >> 40) & 0xFF); + out[6] = (uint8_t)((in >> 48) & 0xFF); + out[7] = (uint8_t)((in >> 56) & 0xFF); + return 8; +} + +static inline uint64_t uint64le_decode(uint8_t *in) { + return (((uint64_t)(in[0])) << 0) + | (((uint64_t)(in[1])) << 8) + | (((uint64_t)(in[2])) << 16) + | (((uint64_t)(in[3])) << 24) + | (((uint64_t)(in[4])) << 32) + | (((uint64_t)(in[5])) << 40) + | (((uint64_t)(in[6])) << 48) + | (((uint64_t)(in[7])) << 56) + ; +} + +static inline uint64le_t uint64le_marshal(uint64_t in) { + uint64le_t out; + uint64le_encode(out.octets, in); + return out; +} + +static inline uint64_t uint64le_unmarshal(uint64le_t in) { + return uint64le_decode(in.octets); +} + #endif /* _LIBMISC_ENDIAN_H_ */ diff --git a/libmisc/include/libmisc/linkedlist.h b/libmisc/include/libmisc/linkedlist.h new file mode 100644 index 0000000..e8c65db --- /dev/null +++ b/libmisc/include/libmisc/linkedlist.h @@ -0,0 +1,108 @@ +/* libmisc/linkedlist.h - Singly- and doubly- linked lists + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _LIBMISC_LINKEDLIST_H_ +#define _LIBMISC_LINKEDLIST_H_ + +/* low-level (intrusive) singly linked list ***********************************/ + +struct _slist_node { + struct _slist_node *rear; +}; + +struct _slist_root { + struct _slist_node *front, *rear; +}; + +void _slist_push_to_rear(struct _slist_root *root, struct _slist_node *node); +void _slist_pop_from_front(struct _slist_root *root); + +/* low-level (intrusive) doubly linked list ***********************************/ + +struct _dlist_node { + struct _dlist_node *front, *rear; +}; + +struct _dlist_root { + struct _dlist_node *front, *rear; +}; + +void _dlist_push_to_rear(struct _dlist_root *root, struct _dlist_node *node); +void _dlist_remove(struct _dlist_root *root, struct _dlist_node *node); +void _dlist_pop_from_front(struct _dlist_root *root); + +/* singly linked list (non-intrusive) *****************************************/ + +#define SLIST_DECLARE(NAME) \ + struct NAME##_node; \ + struct NAME { \ + struct NAME##_node *front, *rear; \ + struct NAME *_slist_root_typ[0]; \ + } + +#define SLIST_DECLARE_NODE(NAME, VAL_T) \ + struct NAME##_node { \ + struct NAME##_node *rear; \ + VAL_T val; \ + } + +#define slist_push_to_rear(ROOT, NODE) { \ + /* These temporary variables are to get the compiler to check \ + * the types. */ \ + typeof(*(ROOT)->_slist_root_typ[0]) *_rootp = ROOT; \ + typeof(*_rootp->front) *_nodep = NODE; \ + _slist_push_to_rear((struct _slist_root *)_rootp, \ + (struct _slist_node *)_nodep); \ +} while(0) + +#define slist_pop_from_front(ROOT) { \ + /* This temporary variables are to get the compiler to check \ + * the type. */ \ + typeof(*(ROOT)->_slist_root_typ[0]) *_rootp = ROOT; \ + _slist_pop_from_front((struct _slist_root *)_rootp); \ +} while(0) + +/* doubly linked list (non-intrusive) *****************************************/ + +#define DLIST_DECLARE(NAME) \ + struct NAME##_node; \ + struct NAME { \ + struct NAME##_node *front, *rear; \ + struct NAME *_dlist_root_typ[0]; \ + } + +#define DLIST_DECLARE_NODE(NAME, VAL_T) \ + struct NAME##_node { \ + struct NAME##_node *front, *rear; \ + VAL_T val; \ + } + +#define dlist_push_to_rear(ROOT, NODE) { \ + /* These temporary variables are to get the compiler to check \ + * the types. */ \ + typeof(*(ROOT)->_dlist_root_typ[0]) *_rootp = ROOT; \ + typeof(*_rootp->front) *_nodep = NODE; \ + _dlist_push_to_rear((struct _dlist_root *)_rootp, \ + (struct _dlist_node *)_nodep); \ +} while(0) + +#define dlist_remove(ROOT, NODE) { \ + /* These temporary variables are to get the compiler to check \ + * the types. */ \ + typeof(*(ROOT)->_dlist_root_typ[0]) *_rootp = ROOT; \ + typeof(*_rootp->front) *_nodep = NODE; \ + _dlist_remove((struct _dlist_root *)_rootp, \ + (struct _dlist_node *)_nodep); \ +} while(0) + +#define dlist_pop_from_front(ROOT) { \ + /* This temporary variables are to get the compiler to check \ + * the type. */ \ + typeof(*(ROOT)->_dlist_root_typ[0]) *_rootp = ROOT; \ + _dlist_pop_from_front((struct _dlist_root *)_rootp); \ +} while(0) + +#endif /* _LIBMISC_LINKEDLIST_H_ */ diff --git a/libmisc/include/libmisc/log.h b/libmisc/include/libmisc/log.h new file mode 100644 index 0000000..79c0ab6 --- /dev/null +++ b/libmisc/include/libmisc/log.h @@ -0,0 +1,34 @@ +/* libmisc/log.h - stdio logging + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _LIBMISC_LOG_H_ +#define _LIBMISC_LOG_H_ + +#include <stdint.h> /* for uint8_t */ + +#include <libmisc/macro.h> +#include <libmisc/_intercept.h> + +#ifdef NDEBUG + #define _LOG_NDEBUG 1 +#else + #define _LOG_NDEBUG 0 +#endif + +const char *const_byte_str(uint8_t b); + +#define n_errorf(nam, fmt, ...) do { __lm_printf("error: " LM_STR_(nam) ": " fmt "\n" __VA_OPT__(,) __VA_ARGS__); } while (0) +#define n_infof(nam, fmt, ...) do { __lm_printf("info : " LM_STR_(nam) ": " fmt "\n" __VA_OPT__(,) __VA_ARGS__); } while (0) +#define n_debugf(nam, fmt, ...) do { if (LM_CAT3_(CONFIG_, nam, _DEBUG) && !_LOG_NDEBUG) \ + __lm_printf("debug: " LM_STR_(nam) ": " fmt "\n" __VA_OPT__(,) __VA_ARGS__); } while (0) + +#endif /* _LIBMISC_LOG_H_ */ + +#if defined(LOG_NAME) && !defined(errorf) +#define errorf(fmt, ...) n_errorf(LOG_NAME, fmt, __VA_ARGS__) +#define infof(fmt, ...) n_infof(LOG_NAME, fmt, __VA_ARGS__) +#define debugf(fmt, ...) n_debugf(LOG_NAME, fmt, __VA_ARGS__) +#endif diff --git a/libmisc/include/libmisc/macro.h b/libmisc/include/libmisc/macro.h new file mode 100644 index 0000000..6cb15fb --- /dev/null +++ b/libmisc/include/libmisc/macro.h @@ -0,0 +1,107 @@ +/* libmisc/macro.h - Useful C preprocessor macros + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _LIBMISC_MACRO_H_ +#define _LIBMISC_MACRO_H_ + +/* for function definitions */ + +#define LM_UNUSED(argname) +#define LM_ALWAYS_INLINE [[gnu::always_inline]] inline +#define LM_NEVER_INLINE [[gnu::noinline]] +#define LM_FLATTEN [[gnu::flatten]] + +/* types */ + +#define LM_ARRAY_LEN(ary) (sizeof(ary)/sizeof((ary)[0])) + +#define LM_CAST_FIELD_TO_STRUCT(STRUCT_TYP, FIELD_NAME, PTR_TO_FIELD) ({ \ + /* The _fptr assignment is to get the compiler to do type checking. */ \ + typeof(((STRUCT_TYP *)NULL)->FIELD_NAME) *_fptr = (PTR_TO_FIELD); \ + _fptr \ + ? ((STRUCT_TYP*)( ((void*)_fptr) - offsetof(STRUCT_TYP, FIELD_NAME))) \ + : NULL; \ +}) + +/* numeric */ + +#define LM_CEILDIV(n, d) ( ((n)+(d)-1) / (d) ) /** Return ceil(n/d) */ +#define LM_ROUND_UP(n, d) ( LM_CEILDIV(n, d) * (d) ) /** Return `n` rounded up to the nearest multiple of `d` */ +#define LM_ROUND_DOWN(n, d) ( ((n)/(d)) * (d) ) /** Return `n` rounded down to the nearest multiple of `d` */ +#define LM_NEXT_POWER_OF_2(x) ( (x) ? 1ULL<<((sizeof(unsigned long long)*8)-__builtin_clzll(x)) : 1) /** Return the lowest power of 2 that is > x */ +#define LM_FLOORLOG2(x) ((sizeof(unsigned long long)*8)-__builtin_clzll(x)-1) /** Return floor(log_2(x) */ + +/* strings */ + +#define LM_STR(x) #x +#define LM_STR_(x) LM_STR(x) + +/* token pasting */ + +#define LM_CAT2(a, b) a ## b +#define LM_CAT3(a, b, c) a ## b ## c + +#define LM_CAT2_(a, b) LM_CAT2(a, b) +#define LM_CAT3_(a, b, c) LM_CAT3(a, b, c) + +/* macro arguments */ + +#define LM_FIRST(a, ...) a +#define LM_SECOND(a, b, ...) b +#define LM_EAT(...) +#define LM_EXPAND(...) __VA_ARGS__ + +/* conditionals */ + +#define LM_T xxTxx +#define LM_F xxFxx + +#define LM_SENTINEL() bogus, LM_T /* a magic sentinel value */ +#define LM_IS_SENTINEL(...) LM_SECOND(__VA_ARGS__, LM_F) + +#define LM_IF(cond) LM_CAT2(_LM_IF__, cond) /* LM_IF(cond)(then)(else) */ +#define _LM_IF__xxTxx(...) __VA_ARGS__ LM_EAT +#define _LM_IF__xxFxx(...) LM_EXPAND + +/* tuples */ + +#define LM_IS_TUPLE(x) LM_IS_SENTINEL(_LM_IS_TUPLE x) +#define _LM_IS_TUPLE(...) LM_SENTINEL() + +/* `tuples` is a sequence of `(tuple1)(tuple2)(tuple3)` */ +#define _LM_TUPLES_COMMA(tuple...) (tuple), +#define LM_TUPLES_NONEMPTY(tuples) LM_IS_TUPLE(_LM_TUPLES_COMMA tuples) +#define LM_TUPLES_HEAD(tuples) LM_EXPAND(LM_FIRST LM_EAT() (_LM_TUPLES_COMMA tuples)) +#define LM_TUPLES_TAIL(tuples) LM_EAT tuples + +/* iteration */ + +/* BUG: LM_FOREACH_TUPLE maxes out at 1024 tuples. */ +#define LM_FOREACH_TUPLE(tuples, func, ...) \ + _LM_EVAL(_LM_FOREACH_TUPLE(tuples, func, __VA_ARGS__)) +#define _LM_FOREACH_TUPLE(tuples, func, ...) \ + LM_IF(LM_TUPLES_NONEMPTY(tuples))( \ + _LM_DEFER2(func)(__VA_ARGS__ __VA_OPT__(,) LM_EXPAND LM_TUPLES_HEAD(tuples)) \ + _LM_DEFER2(_LM_FOREACH_TUPLE_indirect)()(LM_TUPLES_TAIL(tuples), func, __VA_ARGS__) \ + )() +#define _LM_FOREACH_TUPLE_indirect() _LM_FOREACH_TUPLE + +#define _LM_DEFER2(macro) macro LM_EAT LM_EAT()() + +#define _LM_EVAL(...) _LM_EVAL__1024(__VA_ARGS__) /* 1024 iterations aught to be enough for anybody */ +#define _LM_EVAL__1024(...) _LM_EVAL__512(_LM_EVAL__512(__VA_ARGS__)) +#define _LM_EVAL__512(...) _LM_EVAL__256(_LM_EVAL__256(__VA_ARGS__)) +#define _LM_EVAL__256(...) _LM_EVAL__128(_LM_EVAL__128(__VA_ARGS__)) +#define _LM_EVAL__128(...) _LM_EVAL__64(_LM_EVAL__64(__VA_ARGS__)) +#define _LM_EVAL__64(...) _LM_EVAL__32(_LM_EVAL__32(__VA_ARGS__)) +#define _LM_EVAL__32(...) _LM_EVAL__16(_LM_EVAL__16(__VA_ARGS__)) +#define _LM_EVAL__16(...) _LM_EVAL__8(_LM_EVAL__8(__VA_ARGS__)) +#define _LM_EVAL__8(...) _LM_EVAL__4(_LM_EVAL__4(__VA_ARGS__)) +#define _LM_EVAL__4(...) _LM_EVAL__2(_LM_EVAL__2(__VA_ARGS__)) +#define _LM_EVAL__2(...) _LM_EVAL__1(_LM_EVAL__1(__VA_ARGS__)) +#define _LM_EVAL__1(...) __VA_ARGS__ + +#endif /* _LIBMISC_MACRO_H_ */ diff --git a/libmisc/include/libmisc/map.h b/libmisc/include/libmisc/map.h new file mode 100644 index 0000000..41ac069 --- /dev/null +++ b/libmisc/include/libmisc/map.h @@ -0,0 +1,146 @@ +/* libmisc/map.h - A map/dict data structure + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _LIBMISC_MAP_H_ +#define _LIBMISC_MAP_H_ + +#include <stdbool.h> +#include <stddef.h> /* for size_t */ +#include <stdint.h> /* for uint8_t */ + +#include <libmisc/linkedlist.h> + +/* Type ***********************************************************************/ + +DLIST_DECLARE(_map_kv_list); + +struct _map { + size_t len; + size_t nbuckets; + struct _map_kv_list *buckets; + + unsigned int iterating; + + size_t sizeof_kv; + size_t offsetof_k, sizeof_k; + size_t offsetof_v, sizeof_v; +}; + +/** + * MAP_DECLARE(MAPNAME, KEY_T, VAL_T) declares `struct MAPNAME`. + */ +#define MAP_DECLARE(MAPNAME, KEY_T, VAL_T) \ + struct _##MAPNAME##_kv { \ + uint8_t flags; \ + KEY_T key; \ + VAL_T val; \ + }; \ + DLIST_DECLARE_NODE(_##MAPNAME##_kv_list, struct _##MAPNAME##_kv); \ + struct MAPNAME { \ + struct _map core; \ + struct _##MAPNAME##_kv_list_node kv_typ[0]; \ + } + +#define _map_init(M) do { \ + if (!(M)->core.sizeof_kv) { \ + (M)->core.sizeof_kv = sizeof((M)->kv_typ[0]); \ + (M)->core.sizeof_k = sizeof((M)->kv_typ[0].val.key); \ + (M)->core.sizeof_v = sizeof((M)->kv_typ[0].val.val); \ + (M)->core.offsetof_k = offsetof(typeof((M)->kv_typ[0]), val.key); \ + (M)->core.offsetof_v = offsetof(typeof((M)->kv_typ[0]), val.val); \ + } \ +} while(0) + +/* Methods ********************************************************************/ + +/** + * map_len(map) returns the number of entries currently in `map`. + */ +#define map_len(M) ((M)->core.len) + +/** + * map_load(map, key) returns a pointer to the value in `map` + * associated with `key`, or else NULL. + */ +#define map_load(M, K) ({ \ + _map_init(M); \ + typeof((M)->kv_typ[0].val.key) _k = K; \ + (typeof((M)->kv_typ[0].val.val)*)_map_load(&(M)->core, &_k); \ +}) +void *_map_load(struct _map *m, void *kp); + +/** + * map_del(map, key) ensures that `key` is not present in `map`. + * Returns whether `key` was in `map` before the call. + */ +#define map_del(M, K) ({ \ + _map_init(M); \ + typeof((M)->kv_typ[0].val.key) _k = K; \ + _map_del(&(M)->core, &_k); \ +}) +bool _map_del(struct _map *m, void *kp); + +/** + * map_store(map, key, val) sets a value in the map. Returns a + * pointer to the map's copy of `val`. + */ +#define map_store(M, K, ...) ({ \ + _map_init(M); \ + typeof((M)->kv_typ[0].val.key) _k = K; \ + typeof((M)->kv_typ[0].val.val) _v = __VA_ARGS__; \ + (typeof((M)->kv_typ[0].val.val)*)_map_store(&(M)->core, &_k, &_v); \ +}) +void *_map_store(struct _map *m, void *kp, void *vp); + +/** + * map_free(map) frees the memory associated with the map. + */ +#define map_free(M) _map_free(&(M)->core); +void _map_free(struct _map *m); + +/* Iteration ******************************************************************/ + +struct _map_iter { + struct _map *m; + void *keyp; + void **valpp; + + size_t i; + struct _map_kv_list_node *kv; +}; + +/** + * MAP_FOREACH iterates over a map: + * + * MAP_FOREACH(MAP_EACH(MAP, key, valp)) { + * ... + * } + * + * It is safe to mutate the map with map_store() and, map_del() while + * iterating, but entries added by map_store() may or may not be + * visited by the iteration. + */ +#define MAP_FOREACH(M, KNAME, VNAME) _MAP_FOREACH(__COUNTER__, M, KNAME, VNAME) +#define _MAP_FOREACH(CNT, M, KNAME, VNAME) \ + for (bool _once_##CNT = true; _once_##CNT;) \ + for (typeof((M)->kv_typ[0].val.key) KNAME; _once_##CNT;) \ + for (typeof((M)->kv_typ[0].val.val) *VNAME; _once_##CNT;) \ + for ( \ + struct _map_iter _iter_##CNT = ({ \ + _map_init(M); \ + _map_iter_before(&(M)->core, &KNAME, (void**)&VNAME); \ + }); \ + _once_##CNT; \ + ({ \ + _once_##CNT = false; \ + _map_iter_after(&_iter_##CNT); \ + })) \ + while (_map_iter_next(&_iter_##CNT)) +struct _map_iter _map_iter_before(struct _map *m, void *keyp, void **valpp); +bool _map_iter_next(struct _map_iter *iter); +void _map_iter_after(struct _map_iter *iter); + +#endif /* _LIBMISC_MAP_H_ */ diff --git a/libmisc/include/libmisc/private.h b/libmisc/include/libmisc/private.h index 0eb8c42..5518d1f 100644 --- a/libmisc/include/libmisc/private.h +++ b/libmisc/include/libmisc/private.h @@ -1,37 +1,17 @@ /* libmisc/private.h - Utilities to hide struct fields * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later */ -#ifndef _LIBMISC_CPPUTIL_H_ -#define _LIBMISC_CPPUTIL_H_ +#ifndef _LIBMISC_PRIVATE_H_ +#define _LIBMISC_PRIVATE_H_ -/* primitive utilities */ +#include <libmisc/macro.h> -#define _SECOND(a, b, ...) b +#define YES LM_SENTINEL() +#define IS_IMPLEMENTATION_FOR(name) LM_IS_SENTINEL(IMPLEMENTATION_FOR_##name) +#define BEGIN_PRIVATE(name) LM_IF(IS_IMPLEMENTATION_FOR(name))()(struct {) struct {} LM_CAT2_(_PRIVATE_FORCE_SEMICOLON_, __COUNTER__) +#define END_PRIVATE(name) LM_IF(IS_IMPLEMENTATION_FOR(name))(struct {} LM_CAT2_(_PRIVATE_FORCE_SEMICOLON_, __COUNTER__))(} LM_CAT2_(_PRIVATE_, __COUNTER__)) -#define _CAT(a, b) a ## b -#define _CAT2(a, b) _CAT(a, b) -#define _EAT(...) -#define _EXPAND(...) __VA_ARGS__ - -/* conditionals */ - -#define _T xxTxx -#define _F xxFxx - -#define _SENTINEL() bogus, _T /* a magic sentinel value */ -#define _IS_SENTINEL(...) _SECOND(__VA_ARGS__, _F) - -#define _IF(cond) _CAT(_IF__, cond) /* _IF(cond)(then)(else) */ -#define _IF__xxTxx(...) __VA_ARGS__ _EAT -#define _IF__xxFxx(...) _EXPAND - -/* high-level functionality */ - -#define YES _SENTINEL() -#define BEGIN_PRIVATE(name) _IF(_IS_SENTINEL(IMPLEMENTATION_FOR_##name))()(struct {) -#define END_PRIVATE(name) _IF(_IS_SENTINEL(IMPLEMENTATION_FOR_##name))()(} _CAT2(_HIDDEN_, __COUNTER__);) - -#endif /* _LIBMISC_CPPUTIL_H_ */ +#endif /* _LIBMISC_PRIVATE_H_ */ diff --git a/libmisc/include/libmisc/rand.h b/libmisc/include/libmisc/rand.h new file mode 100644 index 0000000..7ef238b --- /dev/null +++ b/libmisc/include/libmisc/rand.h @@ -0,0 +1,46 @@ +/* libmisc/rand.h - Non-crytpographic random-number utilities + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _LIBMISC_RAND_H_ +#define _LIBMISC_RAND_H_ + +#include <stdint.h> /* for uint{n}_t, UINT{n}_C() */ +#include <stdlib.h> /* for random() */ + +#include <libmisc/assert.h> + +/** + * Return a psuedo-random number in the half-open interval [0,cnt). + * `cnt` must not be greater than 1<<63. + */ +static inline uint64_t rand_uint63n(uint64_t cnt) { + assert(cnt != 0 && ((cnt-1) & 0x8000000000000000) == 0); + if (cnt <= UINT64_C(1)<<31) { + uint32_t fair_cnt = ((UINT32_C(1)<<31) / cnt) * cnt; + uint32_t rnd; + do { + rnd = random(); + } while (rnd >= fair_cnt); + return rnd % cnt; + } else if (cnt <= UINT64_C(1)<<62) { + uint64_t fair_cnt = ((UINT64_C(1)<<62) / cnt) * cnt; + uint64_t rnd; + do { + rnd = (((uint64_t)random()) << 31) | random(); + } while (rnd >= fair_cnt); + return rnd % cnt; + } else if (cnt <= UINT64_C(1)<<63) { + uint64_t fair_cnt = ((UINT64_C(1)<<63) / cnt) * cnt; + uint64_t rnd; + do { + rnd = (((uint64_t)random()) << 62) | (((uint64_t)random()) << 31) | random(); + } while (rnd >= fair_cnt); + return rnd % cnt; + } + assert_notreached("cnt is out of bounds"); +} + +#endif /* _LIBMISC_RAND_H_ */ diff --git a/libmisc/include/libmisc/vcall.h b/libmisc/include/libmisc/vcall.h deleted file mode 100644 index ea9402e..0000000 --- a/libmisc/include/libmisc/vcall.h +++ /dev/null @@ -1,27 +0,0 @@ -/* libmisc/vcall.h - A simple Go-ish object system built on GCC -fplan9-extensions - * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -#ifndef _LIBMISC_VCALL_H_ -#define _LIBMISC_VCALL_H_ - -#include <assert.h> /* for assert() and static_assert() */ -#include <stddef.h> /* for offsetof() */ - -#define VCALL(o, m, ...) \ - ({ \ - assert(o); \ - (o)->vtable->m(o __VA_OPT__(,) __VA_ARGS__); \ - }) - -#define VCALL_SELF(obj_typ, iface_typ, iface_ptr) \ - ({ \ - static_assert(_Generic(iface_ptr, iface_typ *: 1, default: 0), \ - "typeof("#iface_ptr") != "#iface_typ); \ - assert(iface_ptr); \ - ((obj_typ*)(((void*)iface_ptr)-offsetof(obj_typ,iface_typ))); \ - }) - -#endif /* _LIBMISC_VCALL_H_ */ |