Line data Source code
1 : #ifndef MEM_H
2 : #define MEM_H
3 :
4 : #include "rational.h"
5 : #include <stddef.h>
6 : #include <stdlib.h>
7 : #include <stdint.h> /* SIZE_MAX */
8 : #include <limits.h>
9 : #include "mem_telemetry.h"
10 :
11 : /** Minimum allocation block size (in bytes) for the helper. */
12 : #define MEMORY_BLOCK_BYTES (4UL * 1024)
13 :
14 : /**
15 : * @file mem.h
16 : * @brief Public API for a small, typed dynamic-memory helper.
17 : *
18 : * This library provides a tiny descriptor (`struct memory`) that stores the size
19 : * of one element, the current element count (`length`), and a data pointer. The element
20 : * size is fixed once via the `create(T, name)` macro and reused by allocation
21 : * routines.
22 : *
23 : * @see mem_resize.c and related implementation units for details.
24 : */
25 :
26 : /**
27 : * @brief Memory Helper API
28 : *
29 : * Top-level group for all public symbols of the memory helper.
30 : */
31 :
32 : /**
33 : * @struct memory
34 : * @brief Describes a typed dynamic memory block.
35 : *
36 : * @var memory::element_size
37 : * Size in bytes of one array element. Set once by @ref create.
38 : *
39 : * @var memory::length
40 : * Current number of elements in the allocated block.
41 : *
42 : * @var memory::actually_allocated_bytes
43 : * Actually allocated memory in bytes
44 : *
45 : * @var memory::data
46 : * Pointer to the beginning of the allocated block (or NULL if none).
47 : */
48 : typedef struct memory {
49 : size_t element_size;
50 : size_t length;
51 : size_t actually_allocated_bytes;
52 : void *data;
53 : } memory;
54 :
55 : /**
56 : * @brief Resize behavior flags for @ref memory_resize.
57 : *
58 : * These masks can be combined to tune how @ref memory_resize behaves:
59 : * - `ZERO_NEW_MEMORY` mirrors `calloc` semantics by clearing any bytes that
60 : * become newly addressable when a descriptor grows.
61 : * - `RELEASE_UNUSED` instructs the helper to release excess capacity immediately
62 : * when the requested length decreases, instead of holding on to the buffer.
63 : *
64 : * Flags may be OR-ed together (for example, `ZERO_NEW_MEMORY | RELEASE_UNUSED`)
65 : * so that both behaviors are enabled during one call.
66 : */
67 : typedef enum
68 : {
69 : ZERO_NEW_MEMORY = 0x01u,
70 : RELEASE_UNUSED = 0x02u
71 : } RESIZEMODES;
72 :
73 : /**
74 : * @brief Allocation functions
75 : *
76 : * Functions for allocating, resizing, and freeing memory.
77 : */
78 :
79 : /**
80 : * @brief Resize the managed block to hold the given number of elements.
81 : *
82 : * @param memory_object Pointer to a descriptor initialized via @ref create.
83 : * @param element_count New number of elements.
84 : * @param ... Optional @ref RESIZEMODES mask controlling zero-fill or shrink behavior.
85 : * @return `SUCCESS` on success; `FAILURE` otherwise. All failures are reported
86 : * through @ref slog for easier diagnostics.
87 : *
88 : * @post If @p element_count is 0, the function frees the block and sets
89 : * @ref memory::data to NULL and @ref memory::length to 0.
90 : *
91 : * @warning The returned data pointer may change; always refresh any cached
92 : * pointers after a successful resize.
93 : */
94 : Return memory_resize(
95 : memory *memory_object,
96 : size_t element_count,
97 : ...);
98 :
99 : /**
100 : * @brief Free the allocated block and reset the descriptor (except element size).
101 : *
102 : * @param memory_object Pointer to a descriptor.
103 : *
104 : * @post Sets @ref memory::data to NULL and @ref memory::length to 0.
105 : * The @ref memory::element_size remains unchanged so the descriptor can be
106 : * allocated again for the same element type.
107 : */
108 : Return memory_delete(memory *memory_object);
109 :
110 : /**
111 : * @brief Copy the contents of @p source descriptor into @p destination.
112 : *
113 : * @param destination Pointer to the destination descriptor (resized if needed).
114 : * @param source Pointer to the source descriptor to copy from.
115 : * @return `SUCCESS` on success; `FAILURE` otherwise.
116 : */
117 : Return memory_copy(
118 : memory *destination,
119 : const memory *source);
120 :
121 : /**
122 : * @brief Append the contents of @p source descriptor to @p destination.
123 : *
124 : * @param destination Pointer to the destination descriptor to extend.
125 : * @param source Pointer to the source descriptor to append from.
126 : * @return `SUCCESS` on success; `FAILURE` otherwise.
127 : */
128 : Return memory_append(
129 : memory *destination,
130 : const memory *source);
131 :
132 : /**
133 : * @brief Concatenate string data held in descriptors, keeping exactly one trailing NUL.
134 : *
135 : * Treats the managed memory as byte-oriented strings (element size must be 1). The
136 : * resulting descriptor is resized to `len(destination) + len(source) + 1` and made
137 : * null-terminated even if the inputs lacked a terminator.
138 : *
139 : * @param destination Pointer to the descriptor receiving the concatenated string.
140 : * @param source Pointer to the descriptor providing the appended string.
141 : * @return `SUCCESS` on success; `FAILURE` otherwise.
142 : */
143 : Return memory_concat_strings(
144 : memory *destination,
145 : const memory *source);
146 :
147 : /**
148 : * @brief Append a C-style literal string to a descriptor holding byte-sized elements.
149 : *
150 : * Resizes @p destination to `len(destination) + strlen(literal) + 1`, copies the literal,
151 : * and guarantees a single trailing null terminator.
152 : *
153 : * @param destination Pointer to the descriptor receiving the literal contents.
154 : * @param literal Pointer to a null-terminated C string.
155 : * @return `SUCCESS` on success; `FAILURE` otherwise.
156 : */
157 : Return memory_concat_literal(
158 : memory *destination,
159 : const char *literal);
160 :
161 : /**
162 : * @brief Copy a C-style literal string into a descriptor holding byte-sized elements.
163 : *
164 : * Resizes @p destination to `strlen(literal) + 1`, copies the literal, and guarantees a
165 : * trailing null terminator. Previous contents of @p destination are discarded.
166 : *
167 : * @param destination Pointer to the descriptor receiving the literal.
168 : * @param literal Pointer to a null-terminated C string.
169 : * @return `SUCCESS` on success; `FAILURE` otherwise.
170 : */
171 : Return memory_copy_literal(
172 : memory *destination,
173 : const char *literal);
174 :
175 : /** @cond INTERNAL */
176 : /**
177 : * @brief Multiply two size_t values with overflow detection.
178 : *
179 : * Used by implementation files to detect overflows when computing byte counts.
180 : *
181 : * @param left Left operand.
182 : * @param right Right operand.
183 : * @param product Output pointer for the product on success.
184 : * @return Return status indicating whether the multiplication succeeded.
185 : */
186 : Return memory_guarded_size(
187 : size_t left,
188 : size_t right,
189 : size_t *product);
190 : /** @endcond */
191 :
192 : /**
193 : * @brief Compute the visible length of string data stored in a descriptor.
194 : *
195 : * The scan stops either at the first null byte or once @ref memory::length bytes
196 : * have been inspected. This ensures the function respects both fully utilized blocks
197 : * and partially filled buffers.
198 : *
199 : * @param memory_object Descriptor whose contents are interpreted as a string.
200 : * @param length_out Output pointer that receives the computed length.
201 : * @return `SUCCESS` on success; `FAILURE` otherwise.
202 : */
203 : Return memory_string_length(
204 : const memory *memory_object,
205 : size_t *length_out);
206 :
207 : /**
208 : * @brief Provide a safe read-only pointer to descriptor-backed string data.
209 : *
210 : * Guarantees that callers always receive a valid, null-terminated byte sequence:
211 : * - When @p memory_object is NULL, uninitialized, or sized for non-byte
212 : * elements, the function returns an empty string.
213 : * - When the descriptor lacks a null terminator within @ref memory::length
214 : * bytes, the function also falls back to an empty string rather than exposing
215 : * potentially uninitialized memory.
216 : *
217 : * This helper is ideal when passing managed buffers to functions such as
218 : * `printf`, `puts`, or regex engines where a missing terminator would otherwise
219 : * lead to undefined behavior.
220 : *
221 : * @param memory_object Descriptor interpreted as a character buffer.
222 : * @return Pointer to a guaranteed null-terminated string (never NULL).
223 : */
224 : const char *memory_getcstring(const memory *memory_object);
225 :
226 : /**
227 : * @brief Provide a writable pointer to descriptor-backed string data, creating
228 : * an empty string fallback when needed.
229 : *
230 : * Ensures that callers can safely treat a descriptor as holding a mutable
231 : * C-style string:
232 : * - If the descriptor is NULL or its metadata is invalid, the helper returns a
233 : * pointer to a shared zero byte instead of NULL.
234 : * - If the descriptor has zero length, it is resized to hold at least one
235 : * null terminator.
236 : * - If the descriptor lacks a terminator within @ref memory::length bytes, the
237 : * first byte is set to `'\0'` before returning.
238 : *
239 : * @param memory_object Descriptor interpreted as a mutable character buffer.
240 : * @return Pointer to a writable string (never NULL). When fallbacks are used,
241 : * modifications affect only the shared zero byte.
242 : */
243 : char *memory_getstring(memory *memory_object);
244 :
245 : /**
246 : * @brief Checked typed access
247 : *
248 : * Runtime type verification and typed data access.
249 : */
250 :
251 : /**
252 : * @brief Verify that the descriptor's element size matches @p expected_element_size.
253 : *
254 : * @param memory_object Pointer to a descriptor.
255 : * @param expected_element_size Expected element size in bytes (typically `sizeof(T)`).
256 : * @return `SUCCESS` if the sizes match; `FAILURE` otherwise (or when
257 : * @p memory_object is NULL).
258 : */
259 : Return memory_verify_type(
260 : const memory *memory_object,
261 : size_t expected_element_size);
262 :
263 : /**
264 : * @brief Return a writable data pointer after verifying the element size.
265 : *
266 : * Performs a runtime check that the descriptor's element size matches
267 : * @p expected_element_size. On mismatch, the function logs the error and
268 : * returns `NULL`.
269 : *
270 : * @param memory_object Pointer to a descriptor.
271 : * @param expected_element_size Expected element size in bytes (typically `sizeof(T)`).
272 : * @return Non-NULL `void*` on success; `NULL` on error.
273 : */
274 : void *memory_data_checked(
275 : memory *memory_object,
276 : size_t expected_element_size);
277 :
278 : /**
279 : * @brief Return a read-only data pointer after verifying the element size.
280 : *
281 : * Same behavior as @ref memory_data_checked but returns a `const void*`.
282 : *
283 : * @param memory_object Pointer to a descriptor.
284 : * @param expected_element_size Expected element size in bytes (typically `sizeof(T)`).
285 : * @return Non-NULL `const void*` on success; `NULL` on error.
286 : */
287 : const void *memory_const_data_checked(
288 : const memory *memory_object,
289 : size_t expected_element_size);
290 :
291 : /**
292 : * @brief Return the descriptor's raw data pointer without additional checks.
293 : *
294 : * @param memory_object Pointer to the descriptor.
295 : * @return Underlying pointer or NULL when @p memory_object itself is NULL.
296 : */
297 1056 : static inline void *memory_rawdata(memory * const memory_object)
298 : {
299 1056 : if(memory_object == NULL)
300 : {
301 0 : return NULL;
302 : }
303 1056 : return memory_object->data;
304 : }
305 :
306 : /**
307 : * @brief Return the descriptor's raw read-only pointer without additional checks.
308 : *
309 : * @param memory_object Pointer to the descriptor.
310 : * @return Underlying pointer or NULL when @p memory_object itself is NULL.
311 : */
312 : static inline const void *memory_raw_const_data(const memory * const memory_object)
313 : {
314 : if(memory_object == NULL)
315 : {
316 : return NULL;
317 : }
318 : return memory_object->data;
319 : }
320 :
321 : /**
322 : * @brief Convenience macros
323 : *
324 : * Declarative and helper macros for user code.
325 : */
326 :
327 : /**
328 : * @def create(T, variable_name)
329 : * @brief Declare and initialize a @ref memory descriptor on the stack.
330 : *
331 : * Sets @ref memory::element_size to `sizeof(T)`, zeroes the count, and sets
332 : * @ref memory::data to `NULL`.
333 : *
334 : * @param T The element type (e.g., `int`, `struct point`, `double`).
335 : * @param variable_name The descriptor variable name to declare.
336 : *
337 : * Internally it declares a storage descriptor named `_variable_name` and exposes
338 : * `memory *variable_name` pointing to that storage so client code can always use
339 : * pointer-style access.
340 : *
341 : * @warning This macro expands to a variable declaration. Use it only where
342 : * declarations are allowed. The macro does not include a trailing
343 : * semicolon; add one at the call site.
344 : */
345 : #define create(T,variable_name) \
346 : memory _ ## variable_name = (memory){sizeof(T),0,0,NULL}; \
347 : memory *variable_name = &_ ## variable_name
348 :
349 : /**
350 : * @def resize(variable_name, number_of_elements)
351 : * @brief Resize (or allocate) the block to @p number_of_elements elements.
352 : */
353 : #define resize(descriptor_expression,number_of_elements,...) \
354 : memory_resize((descriptor_expression),(number_of_elements) \
355 : __VA_OPT__( ,__VA_ARGS__),UCHAR_MAX)
356 :
357 : /**
358 : * @def del(variable_name)
359 : * @brief Free the allocation and reset the descriptor (except element size).
360 : */
361 : #define del(descriptor_expression) \
362 : memory_delete((descriptor_expression))
363 :
364 : /**
365 : * @def data(T, variable_name)
366 : * @brief Get a writable typed pointer with a runtime check.
367 : *
368 : * Internally calls @ref memory_data_checked with `sizeof(T)` and casts the result
369 : * to `T*`. Returns `NULL` on mismatch and logs the error.
370 : */
371 : #define data(T,descriptor_expression) \
372 : ((T *)memory_data_checked((descriptor_expression),sizeof(T)))
373 :
374 : /**
375 : * @def rawdata(variable_name)
376 : * @brief Obtain the raw writable pointer (may be NULL if descriptor is NULL).
377 : */
378 : #define rawdata(descriptor_expression) \
379 : memory_rawdata((descriptor_expression))
380 :
381 : /**
382 : * @def cdata(T, variable_name)
383 : * @brief Get a read-only typed pointer with a runtime check.
384 : *
385 : * Internally calls @ref memory_const_data_checked with `sizeof(T)` and casts the result
386 : * to `const T*`. Returns `NULL` on mismatch and logs the error.
387 : */
388 : #define cdata(T,descriptor_expression) \
389 : ((const T *)memory_const_data_checked((descriptor_expression),sizeof(T)))
390 :
391 : /**
392 : * @def rawcdata(variable_name)
393 : * @brief Obtain the raw read-only pointer (may be NULL if descriptor is NULL).
394 : */
395 : #define rawcdata(descriptor_expression) \
396 : memory_raw_const_data((descriptor_expression))
397 :
398 : /**
399 : * @def getstring(variable_name)
400 : * @brief Obtain a writable C-style string pointer that is always safe to use.
401 : *
402 : * Internally calls @ref memory_getstring to guarantee that a null terminator is
403 : * available. The descriptor must describe byte-sized elements.
404 : */
405 : #define getstring(descriptor_expression) \
406 : memory_getstring((descriptor_expression))
407 :
408 : /**
409 : * @def getcstring(variable_name)
410 : * @brief Obtain a read-only C-style string pointer that is always safe to use.
411 : *
412 : * Internally calls @ref memory_getcstring to guard against NULL descriptors,
413 : * missing allocations, or absent null terminators.
414 : */
415 : #define getcstring(descriptor_expression) \
416 : memory_getcstring((descriptor_expression))
417 :
418 : /**
419 : * @def copy(destination, source)
420 : * @brief Copy one descriptor into another (resizing destination if needed).
421 : */
422 : #define copy(destination,source) \
423 : memory_copy((destination),(source))
424 :
425 : /**
426 : * @def append(destination, source)
427 : * @brief Append @p source contents to @p destination (resizing destination as needed).
428 : */
429 : #define append(destination,source) \
430 : memory_append((destination),(source))
431 :
432 : /**
433 : * @def concat_strings(destination, source)
434 : * @brief Concatenate string descriptors, ensuring a single trailing `'\0'`.
435 : */
436 : #define concat_strings(destination,source) \
437 : memory_concat_strings((destination),(source))
438 :
439 : /**
440 : * @def concat_literal(destination, literal_string)
441 : * @brief Append a null-terminated literal C string to a descriptor with byte-sized elements.
442 : */
443 : #define concat_literal(destination,literal_string) \
444 : memory_concat_literal((destination),(literal_string))
445 :
446 : /**
447 : * @def copy_literal(destination, literal_string)
448 : * @brief Copy a null-terminated literal C string into a descriptor with byte-sized elements.
449 : */
450 : #define copy_literal(destination,literal_string) \
451 : memory_copy_literal((destination),(literal_string))
452 :
453 : /**
454 : * @brief Free an arbitrary pointer and reset it to NULL.
455 : *
456 : * This helper mirrors the legacy `memold` API and works for any pointer, not just
457 : * descriptors created via @ref create.
458 : *
459 : * @param pointer_handle Address of the pointer to release.
460 : */
461 : void FREE_AND_RESET(void **pointer_handle);
462 :
463 : /**
464 : * @def reset(pointer_expression)
465 : * @brief Convenience macro that casts arguments to `void **` for @ref FREE_AND_RESET.
466 : */
467 : #define reset(pointer_expression) \
468 : FREE_AND_RESET((void **)(pointer_expression))
469 :
470 : /**
471 : * @def string_length(descriptor, length_out)
472 : * @brief Measure the utilized byte length within a descriptor interpreted as a string.
473 : */
474 : #define string_length(descriptor_expression,length_out) \
475 : memory_string_length((descriptor_expression),(length_out))
476 :
477 : /**
478 : * @page mem_usage Usage Guide & Best Practices
479 : *
480 : * @section mem_usage_intro Introduction
481 : * This page demonstrates typical patterns when using the memory helper.
482 : *
483 : * @section mem_usage_quick Quick start
484 : * @code
485 : * #include "mem.h"
486 : * typedef struct { int x, y; } point;
487 : *
488 : * int main(void)
489 : * {
490 : * create(point,points); // declare + initialize descriptor
491 : * if(resize(points,10) != SUCCESS) { } // handle error
492 : * point *p = data(point,points); // checked typed access
493 : * p[0] = (point){1,2};
494 : * if(resize(points,20) != SUCCESS) { } // handle error
495 : * p = data(point,points); // refresh pointer after resizing
496 : * del(points);
497 : * return 0;
498 : * }
499 : * @endcode
500 : *
501 : * @section mem_usage_notes Notes & Recommendations
502 : * - Always refresh any cached typed pointer after a successful @ref resize.
503 : * - Consider wrapping error codes with your project’s error-handling utilities.
504 : * - `resize(...,0)` is equivalent to `free` and resets the descriptor
505 : * (`data = NULL`, `length = 0`).
506 : * - Always go through the helper macros so pointer conversions stay explicit and safe.
507 : */
508 :
509 : #endif /* MEM_H */
|