LCOV - code coverage report
Current view: top level - libs/mem/src - mem_resize.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 91.4 % 174 159
Test Date: 2026-07-12 01:01:34 Functions: 100.0 % 2 2
Branches: 77.9 % 154 120

             Branch data     Line data    Source code
       1                 :             : #include "mem.h"
       2                 :             : #include "mem_internal.h"
       3                 :             : #include <string.h>
       4                 :             : 
       5                 :             : /**
       6                 :             :  * @brief Round a byte size up to the slab-style block size used by libmem.
       7                 :             :  *
       8                 :             :  * Rounds @p requested_bytes up to the next multiple of @ref MEMORY_BLOCK_BYTES, preserving zero
       9                 :             :  * and guarding against overflow. Returns non-zero on error so callers can log and fail early.
      10                 :             :  *
      11                 :             :  * @param requested_bytes Number of bytes requested by the caller.
      12                 :             :  * @param slab_size       Output pointer receiving the rounded-up slab size when successful.
      13                 :             :  * @return 0 on success; non-zero if @p slab_size is NULL or an overflow occurred.
      14                 :             :  */
      15                 :       72852 : static int round_up_to_block_size(
      16                 :             :         size_t requested_bytes,
      17                 :             :         size_t *slab_size)
      18                 :             : {
      19         [ -  + ]:       72852 :         if(slab_size == NULL)
      20                 :             :         {
      21                 :           0 :                 return 1;
      22                 :             :         }
      23                 :             : 
      24         [ -  + ]:       72852 :         if(requested_bytes == 0)
      25                 :             :         {
      26                 :           0 :                 *slab_size = 0;
      27                 :           0 :                 return 0;
      28                 :             :         }
      29                 :             : 
      30                 :       72852 :         const size_t remainder = requested_bytes % MEMORY_BLOCK_BYTES;
      31                 :             : 
      32         [ +  + ]:       72852 :         if(remainder == 0)
      33                 :             :         {
      34                 :           4 :                 *slab_size = requested_bytes;
      35                 :           4 :                 return 0;
      36                 :             :         }
      37                 :             : 
      38                 :       72848 :         const size_t padding = MEMORY_BLOCK_BYTES - remainder;
      39                 :             : 
      40         [ -  + ]:       72848 :         if(requested_bytes > SIZE_MAX - padding)
      41                 :             :         {
      42                 :           0 :                 return 1;
      43                 :             :         }
      44                 :             : 
      45                 :       72848 :         *slab_size = requested_bytes + padding;
      46                 :       72848 :         return 0;
      47                 :             : }
      48                 :             : 
      49                 :             : /**
      50                 :             :  * @brief Resize a descriptor in logical elements while preserving its current mode
      51                 :             :  *
      52                 :             :  * Use this helper when a descriptor needs more or less logical room. The new
      53                 :             :  * size is measured in elements, not in bytes, so `m_resize(points,10)` means
      54                 :             :  * ten `point` objects, while `m_resize(text,10)` means ten characters or code
      55                 :             :  * units
      56                 :             :  *
      57                 :             :  * In data mode the helper treats the descriptor as plain raw storage. In that
      58                 :             :  * mode `string_length` must already be `0`, and every successful resize writes
      59                 :             :  * `0` there again so raw buffers keep clean non-string metadata. In string
      60                 :             :  * mode the helper preserves string mode and never invents new visible content
      61                 :             :  * when capacity grows. After a successful resize the element at index
      62                 :             :  * @ref memory::string_length is guaranteed to be a zero-valued terminator, so
      63                 :             :  * the buffer is always a valid C string up to that point. Bytes that lie
      64                 :             :  * beyond the terminator are not initialized by this function — pass
      65                 :             :  * @ref ZERO_NEW_MEMORY if newly exposed bytes should be cleared. If the new
      66                 :             :  * logical size leaves less room than the current visible payload, the visible
      67                 :             :  * string is truncated to fit and a fresh terminator is written. Descriptors
      68                 :             :  * whose reserved byte count no longer covers the current logical payload are
      69                 :             :  * rejected instead of being treated as successful no-ops. In this mode
      70                 :             :  * `string_length` describes only the visible string prefix, while `length`
      71                 :             :  * describes the logical descriptor span in elements. For a tightly packed
      72                 :             :  * string they usually differ by one because the terminator occupies the extra
      73                 :             :  * slot, but they may differ by much more after proactive reserve growth. For
      74                 :             :  * example, a descriptor that holds `"abc"` normally has `string_length == 3`
      75                 :             :  * and `length == 4`; after `m_resize(text,64)` the visible text is still
      76                 :             :  * `"abc"` and `string_length` stays `3`, while `length` becomes `64`
      77                 :             :  *
      78                 :             :  * Without @ref RELEASE_UNUSED the helper reuses the current slab-rounded
      79                 :             :  * reserve whenever possible. That includes `m_resize(desc,0)`: for a valid
      80                 :             :  * descriptor it clears the logical contents, resets the cached string length,
      81                 :             :  * keeps string descriptors in string mode, keeps data descriptors in data mode,
      82                 :             :  * and keeps the allocated storage available for later reuse. With
      83                 :             :  * @ref RELEASE_UNUSED the helper is allowed to
      84                 :             :  * return spare slab-rounded capacity to the allocator during shrink. A resize
      85                 :             :  * to zero with @ref RELEASE_UNUSED clears the logical contents, keeps the
      86                 :             :  * descriptor in its previous string or data mode, and physically releases the
      87                 :             :  * descriptor storage
      88                 :             :  *
      89                 :             :  * Supported behavior flags (combine with `|`):
      90                 :             :  * - @ref ZERO_NEW_MEMORY zero-fills only the bytes that become newly
      91                 :             :  *   addressable after growth, leaving previously reserved bytes untouched
      92                 :             :  * - @ref RELEASE_UNUSED returns spare slab-rounded capacity to the allocator on
      93                 :             :  *   shrink, and physically frees the block on `m_resize(...,0,RELEASE_UNUSED)`
      94                 :             :  *
      95                 :             :  * Small example:
      96                 :             :  * @code
      97                 :             :  * m_create(char,greeting,MEMORY_STRING); // empty string descriptor
      98                 :             :  * m_concat_literal(greeting,"Hi"); // length == 3, string_length == 2
      99                 :             :  *
     100                 :             :  * m_resize(greeting,32); // reserve more room
     101                 :             :  * // length == 32, string_length == 2, visible payload still "Hi"
     102                 :             :  *
     103                 :             :  * m_concat_literal(greeting," there"); // appends without extra reallocation
     104                 :             :  *
     105                 :             :  * m_resize(greeting,0,RELEASE_UNUSED); // drop contents and release the buffer
     106                 :             :  * @endcode
     107                 :             :  *
     108                 :             :  * @param memory_structure Descriptor to resize
     109                 :             :  * @param new_count Requested logical size in elements
     110                 :             :  * @param behavior_flags Resize behavior mask. Pass `0`, a single
     111                 :             :  *        @ref RESIZEMODES flag, or any bitwise OR of flags
     112                 :             :  * @return `SUCCESS` on success; `FAILURE` otherwise. Failures are reported through @ref report for easier diagnostics
     113                 :             :  *
     114                 :             :  * @pre @p memory_structure must be non-NULL and initialized
     115                 :             :  *      (@ref memory::single_element_size must be non-zero)
     116                 :             :  * @pre A valid descriptor must not advertise `length > 0` while
     117                 :             :  *      @ref memory::data is `NULL`
     118                 :             :  * @pre A valid descriptor must not advertise
     119                 :             :  *      @ref memory::actually_allocated_bytes greater than `0` while
     120                 :             :  *      @ref memory::data is `NULL`
     121                 :             :  * @pre A valid descriptor must keep
     122                 :             :  *      @ref memory::actually_allocated_bytes large enough to cover the
     123                 :             :  *      current logical payload
     124                 :             :  * @pre Data descriptors (`is_string == false`) must already have
     125                 :             :  *      @ref memory::string_length equal to `0`
     126                 :             :  *
     127                 :             :  * @warning Any physical reallocation may move @ref memory::data, so refresh cached raw pointers after a successful resize
     128                 :             :  */
     129                 :       74649 : Return mem_resize(
     130                 :             :         memory      *memory_structure,
     131                 :             :         size_t      new_count,
     132                 :             :         RESIZEMODES behavior_flags)
     133                 :             : {
     134                 :             :         /* Status returned by this function through provide()
     135                 :             :            Default value assumes successful completion */
     136                 :       74649 :         Return status = SUCCESS;
     137                 :             : 
     138                 :             :         /* Remembers whether bytes that become newly reachable after growth must be zero-filled */
     139                 :       74649 :         const bool zero_new_memory = (behavior_flags & ZERO_NEW_MEMORY) != 0;
     140                 :             : 
     141                 :             :         /* Remembers whether shrink operations may return spare slab-rounded reserve to the allocator */
     142                 :       74649 :         const bool allow_shrink = (behavior_flags & RELEASE_UNUSED) != 0;
     143                 :             : 
     144   [ +  -  -  + ]:       74649 :         if(memory_structure == NULL || memory_structure->single_element_size == 0)
     145                 :             :         {
     146                 :           0 :                 report("Memory management; Descriptor is NULL or not initialized");
     147                 :           0 :                 provide(FAILURE);
     148                 :             :         }
     149                 :             : 
     150   [ +  +  +  + ]:       74649 :         if(memory_structure->length > 0 && memory_structure->data == NULL)
     151                 :             :         {
     152                 :           1 :                 report("Memory management; Descriptor has non-zero length with NULL data pointer");
     153                 :           1 :                 provide(FAILURE);
     154                 :             :         }
     155                 :             : 
     156   [ +  +  +  + ]:       74648 :         if(memory_structure->is_string == false && memory_structure->string_length != 0)
     157                 :             :         {
     158                 :           1 :                 report("Memory management; Data descriptor has non-zero string_length during resize");
     159                 :           1 :                 provide(FAILURE);
     160                 :             :         }
     161                 :             : 
     162                 :             :         /* Byte size of the payload described by the descriptor before this resize starts */
     163                 :       74647 :         size_t previous_payload_bytes = 0;
     164                 :             : 
     165                 :             :         /* Total bytes the allocator says are currently reserved for the descriptor */
     166                 :       74647 :         size_t previous_allocated_bytes = 0;
     167                 :             : 
     168                 :             :         /* Element count visible to callers before any resize logic runs */
     169                 :       74647 :         size_t previous_length = 0;
     170                 :             : 
     171                 :             :         /* Old block-overhead bytes beyond the logical payload, used only for telemetry accounting */
     172                 :       74647 :         size_t previous_block_overhead = 0;
     173                 :             : 
     174                 :             :         /* Byte size needed for the requested new_count payload */
     175                 :       74647 :         size_t total_size_in_bytes = 0;
     176                 :             : 
     177                 :             :         /* Cached visible string length captured before resize mutates any metadata */
     178                 :       74647 :         size_t previous_string_length = 0;
     179                 :             : 
     180                 :       74647 :         previous_length = memory_structure->length;
     181                 :       74647 :         previous_allocated_bytes = memory_structure->actually_allocated_bytes;
     182                 :       74647 :         previous_string_length = memory_structure->string_length;
     183                 :             : 
     184                 :       74647 :         run(mem_guarded_byte_size(memory_structure,previous_length,&previous_payload_bytes));
     185                 :             : 
     186         [ +  + ]:       74647 :         if(previous_allocated_bytes > previous_payload_bytes)
     187                 :             :         {
     188                 :             :                 /* Direct subtraction is safe because the if-guard above proves no underflow */
     189                 :       52251 :                 previous_block_overhead = previous_allocated_bytes - previous_payload_bytes;
     190                 :             :         }
     191                 :             : 
     192   [ +  -  +  + ]:       74647 :         if((TRIUMPH & status) &&
     193                 :       52256 :                 previous_allocated_bytes > 0 &&
     194         [ -  + ]:       52256 :                 memory_structure->data == NULL)
     195                 :             :         {
     196                 :           0 :                 report("Memory management; Descriptor has reserved bytes with NULL data pointer during resize");
     197                 :           0 :                 provide(FAILURE);
     198                 :             :         }
     199                 :             : 
     200   [ +  -  +  + ]:       74647 :         if((TRIUMPH & status) &&
     201                 :       52253 :                 previous_length > 0 &&
     202         [ +  + ]:       52253 :                 previous_allocated_bytes < previous_payload_bytes)
     203                 :             :         {
     204                 :           2 :                 report("Memory management; Descriptor reserve is smaller than logical payload during resize");
     205                 :           2 :                 provide(FAILURE);
     206                 :             :         }
     207                 :             : 
     208         [ +  - ]:       74645 :         if((TRIUMPH & status) &&
     209   [ +  +  +  + ]:       74645 :                 memory_structure->is_string == true &&
     210         [ +  + ]:       51665 :                 previous_length > 0 &&
     211                 :             :                 previous_string_length >= previous_length)
     212                 :             :         {
     213                 :           1 :                 report("Memory management; String descriptor cache is inconsistent during resize");
     214                 :           1 :                 provide(FAILURE);
     215                 :             :         }
     216                 :             : 
     217         [ +  - ]:       74644 :         if((TRIUMPH & status) &&
     218   [ +  +  +  + ]:       74644 :                 memory_structure->is_string == true &&
     219         [ -  + ]:       20717 :                 previous_length == 0 &&
     220                 :             :                 previous_string_length != 0)
     221                 :             :         {
     222                 :           0 :                 report("Memory management; String descriptor has non-zero string_length with zero length during resize");
     223                 :           0 :                 provide(FAILURE);
     224                 :             :         }
     225                 :             : 
     226   [ +  -  +  + ]:       74644 :         if((TRIUMPH & status) && new_count == previous_length)
     227                 :             :         {
     228   [ +  +  +  +  :        1783 :                 if(new_count != 0 || allow_shrink == false || previous_allocated_bytes == 0)
                   -  + ]
     229                 :             :                 {
     230                 :        1781 :                         telemetry_noop_resizes();
     231                 :        1781 :                         telemetry_consecutive_noop_resizes_advanced();
     232                 :        1781 :                         provide(status);
     233                 :             :                 }
     234                 :             :         }
     235                 :             : 
     236                 :       72863 :         telemetry_current_consecutive_noop_resizes_reset();
     237                 :             : 
     238         [ +  - ]:       72863 :         if(TRIUMPH & status)
     239                 :             :         {
     240                 :       72863 :                 call(mem_guarded_byte_size(memory_structure,new_count,&total_size_in_bytes));
     241                 :             :         }
     242                 :             : 
     243         [ -  + ]:       72863 :         if(CRITICAL & status)
     244                 :             :         {
     245                 :           0 :                 report("Memory management; Overflow for length=%zu (single_element_size=%zu)",new_count,memory_structure->single_element_size);
     246                 :             :         }
     247                 :             : 
     248         [ +  - ]:       72863 :         if(TRIUMPH & status)
     249                 :             :         {
     250         [ +  + ]:       72863 :                 if(new_count == 0)
     251                 :             :                 {
     252   [ +  +  +  - ]:          11 :                         if(allow_shrink == true && previous_allocated_bytes > 0)
     253                 :             :                         {
     254                 :           2 :                                 telemetry_release_unused_shrinks();
     255                 :           2 :                                 telemetry_total_release_unused_heap_reserved_bytes_released(previous_allocated_bytes);
     256                 :             : 
     257                 :           2 :                                 run(mem_delete(memory_structure));
     258                 :             :                         } else {
     259         [ +  - ]:           9 :                                 if(previous_payload_bytes > 0)
     260                 :             :                                 {
     261                 :           9 :                                         telemetry_current_payload_bytes_removed(previous_payload_bytes);
     262                 :           9 :                                         telemetry_block_overhead_bytes_added(previous_payload_bytes);
     263                 :             :                                 }
     264                 :             : 
     265                 :           9 :                                 memory_structure->length = 0;
     266                 :           9 :                                 memory_structure->string_length = 0;
     267                 :             : 
     268         [ +  + ]:           9 :                                 if(memory_structure->is_string == true &&
     269         [ +  - ]:           5 :                                         memory_structure->data != NULL &&
     270         [ +  - ]:           5 :                                         memory_structure->actually_allocated_bytes >= memory_structure->single_element_size)
     271                 :             :                                 {
     272                 :           5 :                                         run(mem_write_zero_terminator(memory_structure,0));
     273                 :             :                                 }
     274                 :             :                         }
     275                 :             :                 } else {
     276                 :             :                         /* Requested payload rounded up to the slab-style block size */
     277                 :       72852 :                         size_t slab_size_in_bytes = 0;
     278                 :             : 
     279         [ -  + ]:       72852 :                         if(round_up_to_block_size(total_size_in_bytes,&slab_size_in_bytes) != 0)
     280                 :             :                         {
     281                 :           0 :                                 report("Memory management; Slab-size rounding overflow for %zu bytes",total_size_in_bytes);
     282                 :           0 :                                 provide(FAILURE);
     283                 :             :                         } else {
     284                 :             :                                 /* True when the descriptor has no allocation yet and needs its first buffer */
     285                 :       72852 :                                 const bool needs_fresh_allocation = memory_structure->data == NULL;
     286                 :             :                                 /* True when the slab-rounded target size is larger than the currently reserved block */
     287                 :       72852 :                                 const bool needs_growth = slab_size_in_bytes > memory_structure->actually_allocated_bytes;
     288                 :             :                                 /* True when RELEASE_UNUSED allows an immediate shrink of the reserved block */
     289         [ +  + ]:       72885 :                                 const bool should_shrink = (allow_shrink == true) &&
     290         [ +  + ]:          33 :                                         slab_size_in_bytes < memory_structure->actually_allocated_bytes;
     291                 :             : 
     292   [ +  +  +  +  :       72852 :                                 if((needs_fresh_allocation == true) || (needs_growth == true) || (should_shrink == true))
                   +  + ]
     293                 :       22311 :                                 {
     294                 :             :                                         /* Result pointer returned by malloc()/realloc() before it is committed to the descriptor */
     295                 :       22311 :                                         void *resized_pointer = NULL;
     296                 :             : 
     297         [ +  + ]:       22311 :                                         if(needs_fresh_allocation == true)
     298                 :             :                                         {
     299                 :       22278 :                                                 resized_pointer = malloc(slab_size_in_bytes);
     300                 :             :                                         } else {
     301                 :          33 :                                                 resized_pointer = realloc(memory_structure->data,slab_size_in_bytes);
     302                 :             :                                         }
     303                 :             : 
     304         [ +  + ]:       22311 :                                         if(resized_pointer == NULL)
     305                 :             :                                         {
     306                 :           2 :                                                 report("Memory management; Memory allocation failed for %zu bytes",slab_size_in_bytes);
     307                 :           2 :                                                 status = FAILURE;
     308                 :             : 
     309         [ +  + ]:           2 :                                                 if(needs_fresh_allocation == true)
     310                 :             :                                                 {
     311                 :           1 :                                                         telemetry_heap_allocation_failures();
     312                 :             :                                                 } else {
     313                 :           1 :                                                         telemetry_heap_reallocation_failures();
     314                 :             :                                                 }
     315                 :             : 
     316                 :             :                                         } else {
     317                 :       22309 :                                                 memory_structure->data = resized_pointer;
     318                 :             : 
     319         [ +  + ]:       22309 :                                                 if(needs_fresh_allocation == true)
     320                 :             :                                                 {
     321                 :       22277 :                                                         telemetry_active_descriptors_acquired();
     322                 :       22277 :                                                         telemetry_fresh_heap_allocations();
     323                 :             : 
     324         [ +  - ]:       22277 :                                                         if(slab_size_in_bytes > 0)
     325                 :             :                                                         {
     326                 :       22277 :                                                                 telemetry_heap_reserved_bytes_acquired(slab_size_in_bytes);
     327                 :             :                                                         }
     328         [ +  + ]:          32 :                                                 } else if(needs_growth == true){
     329                 :             :                                                         /* Direct subtraction is safe because needs_growth implies slab_size_in_bytes > previous_allocated_bytes */
     330                 :          28 :                                                         const size_t added_bytes = slab_size_in_bytes - previous_allocated_bytes;
     331                 :             : 
     332                 :          28 :                                                         telemetry_heap_reallocations();
     333                 :             : 
     334         [ +  - ]:          28 :                                                         if(added_bytes > 0)
     335                 :             :                                                         {
     336                 :          28 :                                                                 telemetry_heap_reserved_bytes_acquired(added_bytes);
     337                 :             :                                                         }
     338         [ +  - ]:           4 :                                                 } else if(should_shrink == true){
     339                 :             :                                                         /* Direct subtraction is safe because should_shrink implies slab_size_in_bytes < previous_allocated_bytes */
     340                 :           4 :                                                         const size_t reclaimed_bytes = previous_allocated_bytes - slab_size_in_bytes;
     341                 :             : 
     342                 :           4 :                                                         telemetry_heap_reallocations();
     343                 :             : 
     344         [ +  - ]:           4 :                                                         if(reclaimed_bytes > 0)
     345                 :             :                                                         {
     346                 :           4 :                                                                 telemetry_release_unused_shrinks();
     347                 :           4 :                                                                 telemetry_total_release_unused_heap_reserved_bytes_released(reclaimed_bytes);
     348                 :           4 :                                                                 telemetry_current_heap_reserved_bytes_released(reclaimed_bytes);
     349                 :           4 :                                                                 telemetry_total_heap_reserved_bytes_released(reclaimed_bytes);
     350                 :             :                                                         }
     351                 :             :                                                 }
     352                 :             : 
     353                 :       22309 :                                                 memory_structure->actually_allocated_bytes = slab_size_in_bytes;
     354                 :             :                                         }
     355                 :             :                                 } else {
     356                 :       50541 :                                         telemetry_in_place_resizes();
     357                 :             :                                 }
     358                 :             : 
     359         [ +  + ]:       72852 :                                 if(TRIUMPH & status)
     360                 :             :                                 {
     361                 :             :                                         /* Newly reachable payload bytes that must be cleared for ZERO_NEW_MEMORY */
     362                 :       72850 :                                         size_t bytes_to_zero = 0;
     363                 :             : 
     364   [ +  +  +  + ]:       72850 :                                         if((zero_new_memory == true) && total_size_in_bytes > previous_payload_bytes)
     365                 :             :                                         {
     366                 :             :                                                 /* Direct subtraction is safe because the if-guard above proves no underflow */
     367                 :        1277 :                                                 bytes_to_zero = total_size_in_bytes - previous_payload_bytes;
     368                 :             :                                         }
     369                 :             : 
     370         [ +  + ]:       72850 :                                         if(bytes_to_zero > 0)
     371                 :             :                                         {
     372                 :             :                                                 /* Writable byte view used by memset() when zero-filling fresh payload space */
     373                 :        1277 :                                                 unsigned char *memory_structure_data_rewritable = (unsigned char *)memory_structure->data;
     374                 :             : 
     375         [ -  + ]:        1277 :                                                 if(memory_structure_data_rewritable == NULL)
     376                 :             :                                                 {
     377                 :           0 :                                                         report("Memory management; Data pointer is NULL during zero-fill");
     378                 :           0 :                                                         provide(FAILURE);
     379                 :             :                                                 } else {
     380                 :        1277 :                                                         memset(memory_structure_data_rewritable + previous_payload_bytes,0,bytes_to_zero);
     381                 :        1277 :                                                         telemetry_zero_initialized_payload_growths();
     382                 :             :                                                 }
     383                 :             :                                         }
     384                 :             : 
     385         [ +  - ]:       72850 :                                         if(TRIUMPH & status)
     386                 :             :                                         {
     387                 :             :                                                 /* Change in logical payload bytes, used only for telemetry bookkeeping */
     388                 :       72850 :                                                 size_t payload_delta = 0;
     389                 :             : 
     390         [ +  + ]:       72850 :                                                 if(total_size_in_bytes > previous_payload_bytes)
     391                 :             :                                                 {
     392                 :             :                                                         /* Direct subtraction is safe because the if-guard above proves no underflow */
     393                 :       67427 :                                                         payload_delta = total_size_in_bytes - previous_payload_bytes;
     394                 :             : 
     395         [ +  - ]:       67427 :                                                         if(payload_delta > 0)
     396                 :             :                                                         {
     397                 :       67427 :                                                                 telemetry_payload_bytes_added(payload_delta);
     398                 :             :                                                         }
     399         [ +  - ]:        5423 :                                                 } else if(total_size_in_bytes < previous_payload_bytes){
     400                 :             :                                                         /* Direct subtraction is safe because the else-if guard proves no underflow */
     401                 :        5423 :                                                         payload_delta = previous_payload_bytes - total_size_in_bytes;
     402                 :             : 
     403         [ +  - ]:        5423 :                                                         if(payload_delta > 0)
     404                 :             :                                                         {
     405                 :        5423 :                                                                 telemetry_current_payload_bytes_removed(payload_delta);
     406                 :             :                                                         }
     407                 :             :                                                 }
     408                 :             :                                         }
     409                 :             : 
     410                 :             :                                         /* Growing a string changes only available capacity. The visible
     411                 :             :                                            payload is not rewritten here, so helper-driven normalization
     412                 :             :                                            may legitimately become a no-op and rely on the existing
     413                 :             :                                            zero terminator that all string mutators must preserve */
     414   [ +  -  +  + ]:       72850 :                                         if((TRIUMPH & status) && memory_structure->is_string == true)
     415                 :       70607 :                                         {
     416                 :             :                                                 /* Visible string length that should remain after resize finishes and terminator is normalized */
     417                 :       70607 :                                                 size_t resulting_string_length = 0;
     418                 :             : 
     419         [ +  + ]:       70607 :                                                 if(previous_length > 0)
     420                 :             :                                                 {
     421                 :       50004 :                                                         resulting_string_length = previous_string_length;
     422                 :             : 
     423         [ +  + ]:       50004 :                                                         if(resulting_string_length >= new_count)
     424                 :             :                                                         {
     425                 :        5206 :                                                                 resulting_string_length = new_count - 1;
     426                 :             :                                                         }
     427                 :             :                                                 }
     428                 :             : 
     429                 :       70607 :                                                 memory_structure->length = new_count;
     430                 :       70607 :                                                 memory_structure->string_length = resulting_string_length;
     431                 :       70607 :                                                 run(mem_string_truncate(memory_structure,resulting_string_length));
     432         [ +  - ]:        2243 :                                         } else if(TRIUMPH & status){
     433                 :        2243 :                                                 memory_structure->length = new_count;
     434                 :        2243 :                                                 memory_structure->string_length = 0;
     435                 :             :                                         }
     436                 :             :                                 }
     437                 :             :                         }
     438                 :             :                 }
     439                 :             :         }
     440                 :             : 
     441   [ +  +  +  + ]:       72863 :         if((TRIUMPH & status) && new_count != 0)
     442                 :             :         {
     443                 :             :                 /* Final reserve size after resize logic, used to recalculate block overhead */
     444                 :       72850 :                 const size_t resulting_allocated_bytes = memory_structure->actually_allocated_bytes;
     445                 :             :                 /* New block-overhead bytes beyond the logical payload after the resize completes */
     446                 :       72850 :                 size_t new_block_overhead = 0;
     447                 :             : 
     448         [ +  + ]:       72850 :                 if(resulting_allocated_bytes > total_size_in_bytes)
     449                 :             :                 {
     450                 :             :                         /* Direct subtraction is safe because the if-guard above proves no underflow */
     451                 :       72846 :                         new_block_overhead = resulting_allocated_bytes - total_size_in_bytes;
     452                 :             :                 }
     453                 :             : 
     454         [ +  + ]:       72850 :                 if(new_block_overhead > previous_block_overhead)
     455                 :             :                 {
     456                 :             :                         /* Direct subtraction is safe because the if-guard above proves no underflow */
     457                 :       27714 :                         const size_t block_overhead_delta = new_block_overhead - previous_block_overhead;
     458                 :             : 
     459         [ +  - ]:       27714 :                         if(block_overhead_delta > 0)
     460                 :             :                         {
     461                 :       27714 :                                 telemetry_block_overhead_bytes_added(block_overhead_delta);
     462                 :             :                         }
     463         [ +  + ]:       45136 :                 } else if(new_block_overhead < previous_block_overhead){
     464                 :             :                         /* Direct subtraction is safe because the else-if guard proves no underflow */
     465                 :       45130 :                         const size_t block_overhead_delta = previous_block_overhead - new_block_overhead;
     466                 :             : 
     467         [ +  - ]:       45130 :                         if(block_overhead_delta > 0)
     468                 :             :                         {
     469                 :       45130 :                                 telemetry_current_block_overhead_bytes_removed(block_overhead_delta);
     470                 :             :                         }
     471                 :             :                 }
     472                 :             :         }
     473                 :             : 
     474                 :       72863 :         provide(status);
     475                 :             : }
        

Generated by: LCOV version 2.0-1