LCOV - code coverage report
Current view: top level - libs/mem/src - mem_core_buffer.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 70.9 % 79 56
Test Date: 2026-07-12 01:01:34 Functions: 100.0 % 1 1
Branches: 64.8 % 54 35

             Branch data     Line data    Source code
       1                 :             : #include "mem.h"
       2                 :             : #include "mem_internal.h"
       3                 :             : #include <stdint.h>
       4                 :             : #include <string.h>
       5                 :             : 
       6                 :             : /**
       7                 :             :  * @brief Internal raw-buffer transfer core used by append and copy entry points
       8                 :             :  *
       9                 :             :  * This helper is the data-mode counterpart of @ref mem_core_string. It never
      10                 :             :  * scans for terminators and always interprets @p source_buffer_size_bytes as an
      11                 :             :  * exact byte count for raw payload transfer
      12                 :             :  *
      13                 :             :  * Transfer flags:
      14                 :             :  * - `TRANSFER_APPEND`: append exact source bytes after the current
      15                 :             :  *   destination payload
      16                 :             :  * - `TRANSFER_REPLACE`: replace the current destination payload with the
      17                 :             :  *   exact source byte range
      18                 :             :  *
      19                 :             :  * Self-aliasing is supported when @p source_buffer points inside the current
      20                 :             :  * destination allocation. The source range must stay inside the current
      21                 :             :  * logical destination payload, not merely inside allocator reserve. The helper
      22                 :             :  * stores the source offset before @ref m_resize and rebuilds the source
      23                 :             :  * pointer afterwards before copying with `memmove`
      24                 :             :  *
      25                 :             :  * Empty-source rules:
      26                 :             :  * - append with `NULL + 0` or an explicit zero-size source is a no-op
      27                 :             :  * - replace with `NULL + 0` or an explicit zero-size source clears the
      28                 :             :  *   destination through @ref m_resize
      29                 :             :  *
      30                 :             :  * @param mode Binary mode flags. Raw-buffer cores only use transfer flags
      31                 :             :  * @param destination Pointer to the destination descriptor in data mode
      32                 :             :  * @param source_buffer_size_bytes Exact byte count to transfer
      33                 :             :  * @param source_buffer Pointer to exact source bytes
      34                 :             :  * @return `SUCCESS` on success; `FAILURE` otherwise
      35                 :             :  */
      36                 :         499 : Return mem_core_buffer(
      37                 :             :         const MEM_CORE_MODE mode,
      38                 :             :         memory              *destination,
      39                 :             :         const size_t        source_buffer_size_bytes,
      40                 :             :         const void *const   source_buffer)
      41                 :             : {
      42                 :             :         /* Status returned by this function through provide()
      43                 :             :            Default value assumes successful completion */
      44                 :         499 :         Return status = SUCCESS;
      45                 :             : 
      46                 :             :         /* Transfer-mode subset extracted from the combined mode flags */
      47                 :         499 :         const MEM_CORE_MODE transfer_mode = mode & TRANSFER_MASK;
      48                 :             : 
      49                 :             :         /* Number of destination elements represented by the exact source byte count */
      50                 :         499 :         size_t transfer_elements = 0;
      51                 :             : 
      52                 :             :         /* Total destination element count after append or replace */
      53                 :         499 :         size_t new_total_elements = 0;
      54                 :             : 
      55                 :             :         /* Byte offset where the transferred source bytes are written in destination */
      56                 :         499 :         size_t offset_bytes = 0;
      57                 :             : 
      58                 :             :         /* Current logical destination payload size in bytes, used to validate self-aliased sources */
      59                 :         499 :         size_t destination_bytes = 0;
      60                 :             : 
      61                 :             :         /* Offset of a self-aliased source from the beginning of destination data */
      62                 :         499 :         size_t source_offset = 0;
      63                 :             : 
      64                 :             :         /* True when source_buffer points into the current destination allocation */
      65                 :         499 :         bool source_is_inside_destination = false;
      66                 :             : 
      67         [ -  + ]:         499 :         if(destination == NULL)
      68                 :             :         {
      69                 :           0 :                 report("Memory management; Destination must be non-NULL");
      70                 :           0 :                 provide(FAILURE);
      71                 :             :         }
      72                 :             : 
      73   [ +  +  +  + ]:         499 :         if(destination->length > 0 && destination->data == NULL)
      74                 :             :         {
      75                 :           1 :                 report("Memory management; Descriptor has non-zero length with NULL data pointer");
      76                 :           1 :                 provide(FAILURE);
      77                 :             :         }
      78                 :             : 
      79         [ +  + ]:         498 :         if(destination->is_string == true)
      80                 :             :         {
      81                 :           1 :                 report("Memory management; Destination must be in data mode, but it is a string");
      82                 :           1 :                 provide(FAILURE);
      83                 :             :         }
      84                 :             : 
      85         [ -  + ]:         497 :         if(destination->single_element_size == 0)
      86                 :             :         {
      87                 :           0 :                 report("Memory management; Destination element size is zero (uninitialized)");
      88                 :           0 :                 provide(FAILURE);
      89                 :             :         }
      90                 :             : 
      91         [ +  + ]:         497 :         if(source_buffer == NULL)
      92                 :             :         {
      93         [ -  + ]:           3 :                 if(source_buffer_size_bytes > 0)
      94                 :             :                 {
      95                 :           0 :                         report("Memory management; Source is NULL while size is %zu",source_buffer_size_bytes);
      96                 :           0 :                         provide(FAILURE);
      97                 :             :                 }
      98                 :             : 
      99         [ -  + ]:           3 :                 if(transfer_mode & TRANSFER_APPEND)
     100                 :             :                 {
     101                 :           0 :                         provide(status);
     102                 :             :                 }
     103                 :             : 
     104                 :           3 :                 run(m_resize(destination,0));
     105                 :           3 :                 provide(status);
     106                 :             :         }
     107                 :             : 
     108         [ -  + ]:         494 :         if(source_buffer_size_bytes == 0)
     109                 :             :         {
     110         [ #  # ]:           0 :                 if(transfer_mode & TRANSFER_APPEND)
     111                 :             :                 {
     112                 :           0 :                         provide(status);
     113                 :             :                 }
     114                 :             : 
     115                 :           0 :                 run(m_resize(destination,0));
     116                 :           0 :                 provide(status);
     117                 :             :         }
     118                 :             : 
     119         [ -  + ]:         494 :         if((source_buffer_size_bytes % destination->single_element_size) != 0)
     120                 :             :         {
     121                 :           0 :                 report("Memory management; Size %zu is not divisible by element size %zu",source_buffer_size_bytes,destination->single_element_size);
     122                 :           0 :                 provide(FAILURE);
     123                 :             :         }
     124                 :             : 
     125                 :         494 :         transfer_elements = source_buffer_size_bytes / destination->single_element_size;
     126                 :             : 
     127         [ +  + ]:         494 :         if(transfer_mode & TRANSFER_APPEND)
     128                 :             :         {
     129                 :          14 :                 run(mem_guarded_add(destination->length,transfer_elements,&new_total_elements));
     130                 :             : 
     131         [ -  + ]:          14 :                 if(CRITICAL & status)
     132                 :             :                 {
     133                 :           0 :                         report("Memory management; Concatenation would overflow element count");
     134                 :           0 :                         provide(FAILURE);
     135                 :             :                 }
     136                 :             : 
     137                 :          14 :                 run(mem_guarded_byte_size(destination,destination->length,&offset_bytes));
     138                 :             : 
     139         [ -  + ]:          14 :                 if(CRITICAL & status)
     140                 :             :                 {
     141                 :           0 :                         report("Memory management; Destination byte offset overflows");
     142                 :           0 :                         provide(status);
     143                 :             :                 }
     144                 :             :         } else {
     145                 :         480 :                 new_total_elements = transfer_elements;
     146                 :         480 :                 offset_bytes = 0;
     147                 :             :         }
     148                 :             : 
     149                 :         494 :         run(mem_guarded_byte_size(destination,destination->length,&destination_bytes));
     150                 :             : 
     151   [ +  -  +  +  :         494 :         if((TRIUMPH & status) && destination->data != NULL && destination->actually_allocated_bytes > 0)
                   +  - ]
     152                 :             :         {
     153                 :             :                 /* Use integer addresses so external source buffers can be compared without pointer-order UB */
     154                 :         409 :                 const uintptr_t destination_begin = (uintptr_t)destination->data;
     155                 :         409 :                 const uintptr_t source_begin = (uintptr_t)source_buffer;
     156                 :             : 
     157                 :             :                 /* Both allocation and logical ends are derived from destination_begin and must not wrap */
     158         [ +  - ]:         409 :                 if(destination->actually_allocated_bytes > UINTPTR_MAX - destination_begin ||
     159         [ -  + ]:         409 :                         destination_bytes > UINTPTR_MAX - destination_begin)
     160                 :             :                 {
     161                 :           0 :                         report("Memory management; Destination address range overflows");
     162                 :           0 :                         provide(FAILURE);
     163                 :             :                 }
     164                 :             : 
     165                 :         409 :                 const uintptr_t allocation_end = destination_begin + destination->actually_allocated_bytes;
     166                 :         409 :                 const uintptr_t logical_end = destination_begin + destination_bytes;
     167                 :             : 
     168                 :             :                 /* A source pointer inside the allocation is destination-owned, even if it points into spare capacity */
     169   [ +  +  +  + ]:         409 :                 if(source_begin >= destination_begin && source_begin < allocation_end)
     170                 :             :                 {
     171                 :           7 :                         source_is_inside_destination = true;
     172                 :           7 :                         source_offset = source_begin - destination_begin;
     173                 :             : 
     174                 :             :                         /* Destination-owned sources may only expose the current logical payload */
     175   [ +  -  -  + ]:           7 :                         if(source_begin > logical_end || source_buffer_size_bytes > destination_bytes - source_offset)
     176                 :             :                         {
     177                 :           0 :                                 report("Memory management; Source range exceeds destination logical bounds");
     178                 :           0 :                                 provide(FAILURE);
     179                 :             :                         }
     180                 :             :                 }
     181                 :             :         }
     182                 :             : 
     183                 :             :         /* m_resize may call realloc, so any source_buffer pointer into destination data becomes invalid here */
     184                 :         494 :         run(m_resize(destination,new_total_elements));
     185                 :             : 
     186         [ +  - ]:         494 :         if(TRIUMPH & status)
     187                 :             :         {
     188                 :         494 :                 unsigned char *destination_data_rewritable = (unsigned char *)destination->data;
     189                 :             : 
     190         [ -  + ]:         494 :                 if(destination_data_rewritable == NULL)
     191                 :             :                 {
     192                 :           0 :                         report("Memory management; Destination data pointer is NULL after m_resize");
     193                 :           0 :                         status = FAILURE;
     194                 :             :                 } else {
     195                 :         494 :                         const unsigned char *source_data_view = (const unsigned char *)source_buffer;
     196                 :             : 
     197                 :             :                         /* Rebase self-aliased sources onto the post-resize allocation using the saved offset */
     198         [ +  + ]:         494 :                         if(source_is_inside_destination == true)
     199                 :             :                         {
     200                 :           7 :                                 source_data_view = destination_data_rewritable + source_offset;
     201                 :             :                         }
     202                 :             : 
     203                 :             :                         /* memmove handles valid self-overlap between the rebuilt source slice and target location */
     204                 :         494 :                         memmove(destination_data_rewritable + offset_bytes,source_data_view,source_buffer_size_bytes);
     205                 :             :                 }
     206                 :             :         }
     207                 :             : 
     208         [ +  - ]:         494 :         if(TRIUMPH & status)
     209                 :             :         {
     210                 :         494 :                 destination->string_length = 0;
     211                 :         494 :                 destination->is_string = false;
     212                 :             :         }
     213                 :             : 
     214                 :         494 :         provide(status);
     215                 :             : }
        

Generated by: LCOV version 2.0-1