LCOV - code coverage report
Current view: top level - libs/mem/src - mem.h (source / functions) Coverage Total Hit
Test: coverage.info Lines: 75.0 % 4 3
Test Date: 2026-01-12 05:34:38 Functions: 100.0 % 1 1

            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 */
        

Generated by: LCOV version 2.0-1