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
|
// 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
* #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.
*
********************************************************************************
* 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 and management of up to a
* well-defined number of buckets.
*
* Thread safety: Bucket creation and destruction is *not* thread-safe. On the
* other hand, the "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_DEFAULT_BUCKET_SIZE 256MB (used when bkt is NULL on first call)
* PK_MINIMUM_ALIGNMENT 1
* PK_MAXIMUM_ALIGNMENT 64
* PK_MAX_BUCKET_COUNT 8
*
* 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.
*
* 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 = {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] = ...;
* ```
*
*******************************************************************************/
#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
#endif
|