1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
|
// 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<void, int*, void*>;
* 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<some_type> 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<some_type> 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<int> 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
|