// vim: tw=80 /******************************************************************************* * PK Single-Header-Library V@@PK_VERSION@@ * * Author: Jonathan Bradley * Copyright: © 2024-@@YEAR@@ Jonathan Bradley * Description: * * A collection of useful programming tools, available for C and C++ as a * single-header file. To enable, in ONE single C or C++ file, declare * PK_IMPL_ALL before including pk.h. * * Example: * * pk.h.include.c * ``` c * #define PK_IMPL_ALL * #include "pk.h" * ``` * * It is also possible to enable modules ad-hoc by defining each IMPL * individually: * * pk.h.include.c * ``` c * # define PK_IMPL_MEM_TYPES * # define PK_IMPL_MEM * # define PK_IMPL_STR * # define PK_IMPL_EV * # define PK_IMPL_ARR * # define PK_IMPL_STN * #include "pk.h" * ``` * ******************************************************************************** * pkmacros.h: * * Provides a set of useful macros for a variety of uses. * * The macros PK_LOG* provide simple logging utilities. These can be overridden * by providing your own implementations of each and defining PK_LOG_OVERRIDE * before including pk.h Note that each of these are no-op'd if NDEBUG is * defined. * * The TypeSafeInt_H and TypeSafeInt_B macros provide a way to define * type-specific integers, implemented via enums. * ******************************************************************************** * pktmpl.h: only contains c++ templates, no IMPL. * * Provides template structs for trampolines, allowing c-style callbacks with * capturing lambdas. * * Examples: * ```c++ * int some_counter = 0; * using IterCbWrapper = pk_tmpln_1; * IterCbWrapper cb_wrapper{}; * cb_wrapper.func = [&some_counter](int *lhs) * { * (void)lhs; * some_counter += 1; * return; * }; * pk_bkt_arr_iterate(&bkt_arr, &IterCbWrapper::invoke, &cb_wrapper); * assert(some_count == 1); * ``` * ******************************************************************************** * pkmem-types.h: def PK_IMPL_MEM_TYPES before including pk.h to enable ad-hoc. * * Provides the types needed by pkmem, as well as a generic pk_handle featuring a * bucket+item indexing system. * ******************************************************************************** * pkmem.h: def PK_IMPL_MEM before including pk.h to enable ad-hoc. * * A bucketed memory manager. Allows for the creation of ad-hoc buckets. * * Note: Each created pk_membucket MUST call pk_bucket_destroy(bkt). Memory * buckets are client managed. * * Thread safety: "pk_new" and "pk_delete" methods *are* thread-safe, but * thread-safety is implemented per-bucket via a single mutex with long-running * lock times. PRs for a more performant thread-safe strategy are welcome, * complexity and benchmark depending. * * The following definitions (shown with defaults) can be overridden: * PK_MEM_DEFAULT_BUCKET_SIZE 256MB (client-convenience only) * PK_MINIMUM_ALIGNMENT 1 * * For debugging purposes, define the following: * PK_MEMORY_DEBUGGER : enables a tracking system for all allocs and frees to * ensure bucket validity and consistency. * PK_MEMORY_FORCE_MALLOC : completely disables pkmem and its debugging features * in favor of directly using malloc and free. Useful for out-of-bounds * checking. * ******************************************************************************** * pkstr.h: def PK_IMPL_STR before including pk.h to enable ad-hoc. * * Provides a simple string structure, allowing the user to track the string * length and reserved buffer length. Limits max string length to uint32_t max * size, which is roughly 4GB. * * Tip: set reserved to 0 for compile-time strings as well as for strings alloc'd * in a larger buffer (such as bulk-loaded data). * ******************************************************************************** * pkev.h: def PK_IMPL_EV before including pk.h to enable ad-hoc. * * Provides a simple event callback system. While the _init and _teardown * functions are NOT thread-safe, the _register and _emit functions are. * Note: uses malloc. * * Each mgr is stored contiguously with its data. Consider the following layout: * [[mgr][ev 0][ev 1][..][ev N][ev 1 cb array][ev 2 cb array][..][ev N cb array]] * * The following definitions (shown with defaults) can be overridden: * PK_EV_INIT_MGR_COUNT 1 * PK_EV_INIT_EV_COUNT 16 * PK_EV_INIT_CB_COUNT 8 * PK_EV_GROW_RATIO 1.5 * * The number of evs and cbs (per ev) is stored as a uint8_t, so a hard-limit of * 255 is to be observed for each. The number of mgrs is stored as a uint64_t. * * Note that PK_EV_GROW_RATIO is used in two scenarios: * 1. When registering an ev on a full mgr. * 2. When registering a cb on a full ev. * The grow ratio is applied to the ev count and cb count in their respective * scenarios. This causes a new allocation for the entire mgr. The existing * mgr and its evs and cbs are copied to the new larger buffer space. * Explicitly, the number of mgrs does not grow dynamically. Use * PK_EV_INIT_MGR_COUNT to control the number of mgrs. * * Note that increasing PK_EV_INIT_MGR_COUNT isn't recommended, but you may * consider doing so if you have specific size or contiguity requirements. For * example, you could -DPK_EV_INIT_EV_COUNT=1 to reduce the memory footprint of * each event/mgr, and simply create a new mgr for each needed event. Be aware * that in this provided scenario a given mgr will still grow if a second EV is * registered. * ******************************************************************************** * pkarr.h: def PK_IMPL_ARR before including pk.h to enable ad-hoc * * Provides a structure for managing contiguous lists * * The following definitions (shown with defaults) can be overridden: * PK_ARR_INITIAL_COUNT 16 * PK_ARR_GROW_RATIO 1.5 * PK_ARR_MOVE_IN_PLACE (not defined) * * The macro `PK_ARR_MOVE_IN_PLACE` ensures that when possible, the pointer value * of `arr->data` is preserved. * It is used in the following methods: * `pk_arr_move_to_back` * `pk_arr_remove_at` * This has two additinal benefits: * 1. Minimizing the number and `sz` of calls to `pk_new` * 2. Ensuring `data[0]` to `data[(N - 1) * stride]` is not copied extraneously * to a new buffer. * The speed of this will vary depending on usage, platform, and compiler. * * Initialize `stride`, `alignment`, and `bkt` (optional) members * *before* calling any `pk_arr_*` methods. * Alternatively, if using c++, use the template ctor. * * Examples: * ``` c * struct pk_arr arr = {0}; * arr.stride = sizeof(obj); // required * arr.alignment = alignof(obj); // required * arr.bkt = bkt; // optional * pk_arr_reserve(&arr, 10); // optional * pk_arr_append(&arr, &obj); * ``` * ``` c++ * struct pk_arr arr(bkt); * pk_arr_reserve(&arr, 10); // optional * pk_arr_append(&arr, &obj); * ``` * ``` c * struct pk_arr arr = {0}; * arr.stride = sizeof(obj); // required * arr.alignment = alignof(obj); // required * arr.bkt = bkt; // optional * pk_arr_resize(&arr, 10); * obj* d = (obj*)arr->data; * d[0] = ...; * ``` * ``` c++ * struct pk_arr_t arr(); * pk_arr_resize(&arr, 10); * arr[0] = {}; * ``` * ******************************************************************************** * pkstn.h: def PK_IMPL_STN before including pk.h to enable ad-hoc. * * Provides a thorough interface for interacting with the `stoi` family of * procedures. * ******************************************************************************** * pktmr.h: No IMPL define, all methods are macros. * * Offers a set of `pk_tmr*` macros for elapsed time checking. * * The following definitions (shown with defaults) can be overridden: * PK_TMR_CLOCK CLOCK_MONOTONIC * * If your needs require you to use more than one clock, I recommend calling * `clock_gettime` manually instead of calling `pk_tmr_start`/`pk_tmr_stop`. * `pk_tmr.b` is the start time. * `pk_tmr.e` end the end time. * You could then call the `pk_tmr_duration...` convenience macros as needed. * ******************************************************************************** * pkuuid.h: define PK_IMPL_UUID before including pk.h to enable ad-hoc. * * Provides a 16-byte unsigned char array struct for uuids. * * The following definitions (shown with defaults) can be overridden: * PK_UUID_CLOCK CLOCK_TAI (preferred, if available) * PK_UUID_CLOCK CLOCK_REALTIME (fallback) * * The `PK_UUID_CLOCK` macro has minimal built-in fallback logic. * The uuidv7 specification states that the timestamp portion of the uuid must be * a unix epoch, leap seconds EXCLUDED. Only `CLOCK_TAI` meets this requirement * on Linux. * * Note that this currectly calls `srand()` once at startup, and calls `rand()` * 2 times for each uuidv7 to fill 74 bits with random data (with an XOR for the * remaining 10 bits). * ******************************************************************************** * pkbktarr.h: define PK_IMPL_BKTARR before including pk.h to enable ad-hoc. * * Provides a struct for bucketed data allocation. * * Maximum (default) bucket limits are as follows: * buckets: 0xFFFFFF (16777215) * items/bucket: 0x40 (64) * * Note that you may specify separate `pk_membucket`s for the the struct's * arrays `bucketed_data` + `idx_unused`, and the actual bucketed array data * found within `bucketed_data`. * If the `pk_membucket` for "data" is exclusive to this struct, each bucket (and * by extension, the data) will be contiguious in memory. * * Examples: * ```c * struct pk_bkt_arr_handle custom_limits; * custom_limits.b = 8; * custom_limits.i = 8; * struct pk_bkt_arr arr; * pk_bkt_arr_init( * &arr, sizeof(int), alignof(int), custom_limits, bkt_buckets, bkt_data); * struct pk_bkt_arr_handle h = pk_bkt_arr_new_handle(&arr); * int **int_ptrs = (int**)arr.bucketed_data; * int_ptrs[h.b][h.i] = 128; * pk_bkt_arr_free_handle(&arr, h); * pk_bkt_arr_teardown(&arr); * ``` * ```c++ * // default limits, no pk_membucket * struct pk_bkt_arr arr(); * struct pk_bkt_arr_handle h = pk_bkt_arr_new_handle(&arr); * arr[h] = 128; * pk_bkt_arr_free_handle(&arr, h); * pk_bkt_arr_teardown(&arr); * ``` * ******************************************************************************** * pkbktarr.h: define PK_IMPL_FUNCINSTR before including pk.h to enable ad-hoc. * * Provides function instrumentation. * * Note: Currently only supports gcc/g++. * Note: Currently only prints results. * * Examples: * ```c * main() { * pk_funcinstr_init(); * ... * pk_funcinstr_teardown(); * } * ``` * ******************************************************************************** * pktst.h: define PK_IMPL_TST before including pk.h to enable ad-hoc. * * Provides a simple testing framework * * Examples: * ```c * main() { * pk_test_run_test_groups(&my_get_test_group_func, 1); * } * ``` * *******************************************************************************/ #define PK_VERSION "@@PK_VERSION@@" #ifdef PK_IMPL_ALL # ifndef PK_IMPL_MEM_TYPES # define PK_IMPL_MEM_TYPES # endif # ifndef PK_IMPL_MEM # define PK_IMPL_MEM # endif # ifndef PK_IMPL_STR # define PK_IMPL_STR # endif # ifndef PK_IMPL_EV # define PK_IMPL_EV # endif # ifndef PK_IMPL_ARR # define PK_IMPL_ARR # endif # ifndef PK_IMPL_STN # define PK_IMPL_STN # endif # ifndef PK_IMPL_UUID # define PK_IMPL_UUID # endif # ifndef PK_IMPL_BKTARR # define PK_IMPL_BKTARR # endif # ifndef PK_IMPL_FUNCINSTR # define PK_IMPL_FUNCINSTR # endif # ifndef PK_IMPL_TST # define PK_IMPL_TST # endif #endif