LCOV - code coverage report
Current view: top level - source/flat_buffer.c (source / functions) Coverage Total Hit
Test: CCC Test Suite Coverage Report Lines: 99.7 % 310 309
Test Date: 2026-05-12 15:05:06 Functions: 100.0 % 37 37

            Line data    Source code
       1              : /** Copyright 2025 Alexander G. Lopez
       2              : 
       3              : Licensed under the Apache License, Version 2.0 (the "License");
       4              : you may not use this file except in compliance with the License.
       5              : You may obtain a copy of the License at
       6              : 
       7              :    http://www.apache.org/licenses/LICENSE-2.0
       8              : 
       9              : Unless required by applicable law or agreed to in writing, software
      10              : distributed under the License is distributed on an "AS IS" BASIS,
      11              : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12              : See the License for the specific language governing permissions and
      13              : limitations under the License. */
      14              : /** C23 provided headers. */
      15              : #include <stddef.h>
      16              : 
      17              : /** CCC provided headers. */
      18              : #include "ccc/configuration.h"
      19              : #include "ccc/flat_buffer.h"
      20              : #include "ccc/private/private_flat_buffer.h"
      21              : #include "ccc/types.h"
      22              : 
      23              : enum : size_t {
      24              :     START_CAPACITY = 8,
      25              : };
      26              : 
      27              : /*==========================   Prototypes    ================================*/
      28              : 
      29              : static void *at(CCC_Flat_buffer const *, size_t);
      30              : static size_t max(size_t, size_t);
      31              : 
      32              : /*==========================    Interface    ================================*/
      33              : 
      34              : CCC_Result
      35           63 : CCC_flat_buffer_allocate(
      36              :     CCC_Flat_buffer *const buffer,
      37              :     size_t const capacity,
      38              :     CCC_Allocator const *const allocator
      39              : ) {
      40           63 :     if (!buffer || !allocator) {
      41            2 :         return CCC_RESULT_ARGUMENT_ERROR;
      42              :     }
      43           61 :     if (!allocator->allocate) {
      44            9 :         return CCC_RESULT_NO_ALLOCATION_FUNCTION;
      45              :     }
      46          208 :     void *const new_data = allocator->allocate((CCC_Allocator_arguments){
      47           52 :         .input = buffer->data,
      48           52 :         .bytes = buffer->sizeof_type * capacity,
      49           52 :         .context = allocator->context,
      50              :     });
      51           52 :     if (capacity && !new_data) {
      52            4 :         return CCC_RESULT_ALLOCATOR_ERROR;
      53              :     }
      54           48 :     buffer->data = new_data;
      55           48 :     buffer->capacity = capacity;
      56           48 :     return CCC_RESULT_OK;
      57           63 : }
      58              : 
      59              : CCC_Result
      60           51 : CCC_flat_buffer_reserve(
      61              :     CCC_Flat_buffer *const buffer,
      62              :     size_t const to_add,
      63              :     CCC_Allocator const *const allocator
      64              : ) {
      65           51 :     if (!buffer || !allocator || !allocator->allocate || !to_add) {
      66            9 :         return CCC_RESULT_ARGUMENT_ERROR;
      67              :     }
      68           42 :     size_t needed = buffer->count + to_add;
      69           42 :     if (needed <= buffer->capacity) {
      70            1 :         return CCC_RESULT_OK;
      71              :     }
      72           41 :     if (needed < START_CAPACITY) {
      73            3 :         needed = START_CAPACITY;
      74            3 :     }
      75          164 :     void *const new_data = allocator->allocate((CCC_Allocator_arguments){
      76           41 :         .input = buffer->data,
      77           41 :         .bytes = buffer->sizeof_type * needed,
      78           41 :         .context = allocator->context,
      79              :     });
      80           41 :     if (!new_data) {
      81            1 :         return CCC_RESULT_ALLOCATOR_ERROR;
      82              :     }
      83           40 :     buffer->data = new_data;
      84           40 :     buffer->capacity = needed;
      85           40 :     return CCC_RESULT_OK;
      86           51 : }
      87              : 
      88              : CCC_Result
      89            4 : CCC_flat_buffer_clear(
      90              :     CCC_Flat_buffer *const buffer, CCC_Destructor const *const destructor
      91              : ) {
      92            4 :     if (!buffer || !destructor) {
      93            2 :         return CCC_RESULT_ARGUMENT_ERROR;
      94              :     }
      95            2 :     if (!destructor->destroy) {
      96            1 :         buffer->count = 0;
      97            1 :         return CCC_RESULT_OK;
      98              :     }
      99            9 :     for (void *i = CCC_flat_buffer_begin(buffer);
     100            9 :          i != CCC_flat_buffer_end(buffer);
     101            8 :          i = CCC_flat_buffer_next(buffer, i)) {
     102           24 :         destructor->destroy((CCC_Arguments){
     103            8 :             .type = i,
     104            8 :             .context = destructor->context,
     105              :         });
     106            8 :     }
     107            1 :     buffer->count = 0;
     108            1 :     return CCC_RESULT_OK;
     109            4 : }
     110              : 
     111              : CCC_Result
     112           20 : CCC_flat_buffer_clear_and_free(
     113              :     CCC_Flat_buffer *const buffer,
     114              :     CCC_Destructor const *const destructor,
     115              :     CCC_Allocator const *const allocator
     116              : ) {
     117           20 :     if (!buffer || !allocator || !destructor || !allocator->allocate) {
     118            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     119              :     }
     120           19 :     if (destructor->destroy) {
     121            9 :         for (void *i = CCC_flat_buffer_begin(buffer);
     122            9 :              i != CCC_flat_buffer_end(buffer);
     123            8 :              i = CCC_flat_buffer_next(buffer, i)) {
     124           24 :             destructor->destroy((CCC_Arguments){
     125            8 :                 .type = i,
     126            8 :                 .context = destructor->context,
     127              :             });
     128            8 :         }
     129            1 :     }
     130           57 :     (void)allocator->allocate((CCC_Allocator_arguments){
     131           19 :         .input = buffer->data,
     132              :         .bytes = 0,
     133           19 :         .context = allocator->context,
     134              :     });
     135           19 :     buffer->data = NULL;
     136           19 :     buffer->count = 0;
     137           19 :     buffer->capacity = 0;
     138           19 :     return CCC_RESULT_OK;
     139           20 : }
     140              : 
     141              : void *
     142         3696 : CCC_flat_buffer_at(CCC_Flat_buffer const *const buffer, size_t const i) {
     143         3696 :     if (!buffer || i >= buffer->capacity) {
     144            3 :         return NULL;
     145              :     }
     146         3693 :     return ((char *)buffer->data + (i * buffer->sizeof_type));
     147         3696 : }
     148              : 
     149              : void *
     150           90 : CCC_flat_buffer_back(CCC_Flat_buffer const *const buffer) {
     151           90 :     return CCC_flat_buffer_at(buffer, buffer->count - 1);
     152              : }
     153              : 
     154              : void *
     155            7 : CCC_flat_buffer_front(CCC_Flat_buffer const *const buffer) {
     156            7 :     return CCC_flat_buffer_at(buffer, 0);
     157              : }
     158              : 
     159              : void *
     160         4877 : CCC_flat_buffer_allocate_back(
     161              :     CCC_Flat_buffer *const buffer, CCC_Allocator const *const allocator
     162              : ) {
     163         4877 :     if (!buffer || !allocator) {
     164            4 :         return NULL;
     165              :     }
     166         4873 :     if (buffer->count == buffer->capacity) {
     167           40 :         CCC_Result const resize_res = CCC_flat_buffer_allocate(
     168           20 :             buffer, max(buffer->capacity * 2, START_CAPACITY), allocator
     169              :         );
     170           20 :         if (resize_res != CCC_RESULT_OK) {
     171            6 :             return NULL;
     172              :         }
     173           20 :     }
     174         9734 :     void *const ret
     175         4867 :         = ((char *)buffer->data + (buffer->sizeof_type * buffer->count));
     176         4867 :     ++buffer->count;
     177         4867 :     return ret;
     178         4877 : }
     179              : 
     180              : void *
     181          149 : CCC_flat_buffer_push_back(
     182              :     CCC_Flat_buffer *const buffer,
     183              :     void const *const data,
     184              :     CCC_Allocator const *const allocator
     185              : ) {
     186          149 :     if (!data) {
     187            1 :         return NULL;
     188              :     }
     189          148 :     void *const slot = CCC_flat_buffer_allocate_back(buffer, allocator);
     190          148 :     if (slot) {
     191          142 :         (void)memcpy(slot, data, buffer->sizeof_type);
     192          142 :     }
     193          148 :     return slot;
     194          149 : }
     195              : 
     196              : CCC_Result
     197            5 : CCC_flat_buffer_swap(
     198              :     CCC_Flat_buffer const *const buffer,
     199              :     void *const temp,
     200              :     size_t const index,
     201              :     size_t const swap_index
     202              : ) {
     203            5 :     if (!buffer || !temp || index >= buffer->capacity
     204            5 :         || swap_index >= buffer->capacity || swap_index == index) {
     205            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     206              :     }
     207            4 :     (void)memcpy(temp, at(buffer, index), buffer->sizeof_type);
     208            4 :     (void)memcpy(
     209            4 :         at(buffer, index), at(buffer, swap_index), buffer->sizeof_type
     210              :     );
     211            4 :     (void)memcpy(at(buffer, swap_index), temp, buffer->sizeof_type);
     212            4 :     return CCC_RESULT_OK;
     213            5 : }
     214              : 
     215              : void *
     216            3 : CCC_flat_buffer_move(
     217              :     CCC_Flat_buffer const *const buffer,
     218              :     size_t const destination,
     219              :     size_t const source
     220              : ) {
     221            3 :     if (!buffer || destination >= buffer->capacity
     222            3 :         || source >= buffer->capacity) {
     223            1 :         return NULL;
     224              :     }
     225            2 :     if (destination == source) {
     226            1 :         return at(buffer, destination);
     227              :     }
     228            1 :     return memcpy(
     229            1 :         at(buffer, destination), at(buffer, source), buffer->sizeof_type
     230              :     );
     231            3 : }
     232              : 
     233              : CCC_Result
     234           10 : CCC_flat_buffer_write(
     235              :     CCC_Flat_buffer const *const buffer, size_t const i, void const *const data
     236              : ) {
     237           10 :     if (!buffer || !buffer->data || !data) {
     238            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     239              :     }
     240            9 :     void *const pos = CCC_flat_buffer_at(buffer, i);
     241            9 :     if (!pos || data == pos) {
     242            2 :         return CCC_RESULT_ARGUMENT_ERROR;
     243              :     }
     244            7 :     (void)memcpy(pos, data, buffer->sizeof_type);
     245            7 :     return CCC_RESULT_OK;
     246           10 : }
     247              : 
     248              : CCC_Result
     249            6 : CCC_flat_buffer_erase(CCC_Flat_buffer *const buffer, size_t const i) {
     250            6 :     if (!buffer || !buffer->count || i >= buffer->count) {
     251            2 :         return CCC_RESULT_ARGUMENT_ERROR;
     252              :     }
     253            4 :     if (1 == buffer->count) {
     254            1 :         buffer->count = 0;
     255            1 :         return CCC_RESULT_OK;
     256              :     }
     257            3 :     if (i == buffer->count - 1) {
     258            1 :         --buffer->count;
     259            1 :         return CCC_RESULT_OK;
     260              :     }
     261            2 :     (void)memmove(
     262            2 :         at(buffer, i),
     263            2 :         at(buffer, i + 1),
     264            2 :         buffer->sizeof_type * (buffer->count - (i + 1))
     265              :     );
     266            2 :     --buffer->count;
     267            2 :     return CCC_RESULT_OK;
     268            6 : }
     269              : 
     270              : void *
     271           11 : CCC_flat_buffer_insert(
     272              :     CCC_Flat_buffer *const buffer,
     273              :     size_t const i,
     274              :     void const *const data,
     275              :     CCC_Allocator const *const allocator
     276              : ) {
     277           11 :     if (!buffer || !buffer->data || i > buffer->count || !allocator) {
     278            4 :         return NULL;
     279              :     }
     280            7 :     if (i == buffer->count) {
     281            1 :         return CCC_flat_buffer_push_back(buffer, data, allocator);
     282              :     }
     283            6 :     if (buffer->count == buffer->capacity) {
     284            2 :         CCC_Result const r = CCC_flat_buffer_allocate(
     285            1 :             buffer, max(buffer->count * 2, START_CAPACITY), allocator
     286              :         );
     287            1 :         if (r != CCC_RESULT_OK) {
     288            1 :             return NULL;
     289              :         }
     290            1 :     }
     291            5 :     (void)memmove(
     292            5 :         at(buffer, i + 1),
     293            5 :         at(buffer, i),
     294            5 :         buffer->sizeof_type * (buffer->count - i)
     295              :     );
     296            5 :     ++buffer->count;
     297            5 :     return memcpy(at(buffer, i), data, buffer->sizeof_type);
     298           11 : }
     299              : 
     300              : CCC_Result
     301           66 : CCC_flat_buffer_pop_back_n(CCC_Flat_buffer *const buffer, size_t count) {
     302           66 :     if (!buffer || count > buffer->count) {
     303            4 :         return CCC_RESULT_ARGUMENT_ERROR;
     304              :     }
     305           62 :     buffer->count -= count;
     306           62 :     return CCC_RESULT_OK;
     307           66 : }
     308              : 
     309              : CCC_Result
     310           64 : CCC_flat_buffer_pop_back(CCC_Flat_buffer *const buffer) {
     311           64 :     return CCC_flat_buffer_pop_back_n(buffer, 1);
     312              : }
     313              : 
     314              : CCC_Count
     315         1078 : CCC_flat_buffer_count(CCC_Flat_buffer const *const buffer) {
     316         1078 :     if (!buffer) {
     317            1 :         return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
     318              :     }
     319         1077 :     return (CCC_Count){.count = buffer->count};
     320         1078 : }
     321              : 
     322              : CCC_Count
     323           29 : CCC_flat_buffer_capacity(CCC_Flat_buffer const *const buffer) {
     324           29 :     if (!buffer) {
     325            1 :         return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
     326              :     }
     327           28 :     return (CCC_Count){.count = buffer->capacity};
     328           29 : }
     329              : 
     330              : CCC_Count
     331          128 : CCC_flat_buffer_sizeof_type(CCC_Flat_buffer const *const buffer) {
     332          128 :     if (!buffer) {
     333            1 :         return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
     334              :     }
     335          127 :     return (CCC_Count){.count = buffer->sizeof_type};
     336          128 : }
     337              : 
     338              : CCC_Tribool
     339         2066 : CCC_flat_buffer_is_empty(CCC_Flat_buffer const *const buffer) {
     340         2066 :     if (!buffer) {
     341            1 :         return CCC_TRIBOOL_ERROR;
     342              :     }
     343         2065 :     return !buffer->count;
     344         2066 : }
     345              : 
     346              : CCC_Tribool
     347           29 : CCC_flat_buffer_is_full(CCC_Flat_buffer const *const buffer) {
     348           29 :     if (!buffer) {
     349            1 :         return CCC_TRIBOOL_ERROR;
     350              :     }
     351           28 :     if (!buffer->capacity) {
     352            1 :         return CCC_FALSE;
     353              :     }
     354           27 :     return buffer->count == buffer->capacity ? CCC_TRUE : CCC_FALSE;
     355           29 : }
     356              : 
     357              : void *
     358         2156 : CCC_flat_buffer_begin(CCC_Flat_buffer const *const buffer) {
     359         2156 :     return buffer ? buffer->data : NULL;
     360              : }
     361              : 
     362              : void *
     363           64 : CCC_flat_buffer_reverse_begin(CCC_Flat_buffer const *const buffer) {
     364           64 :     if (!buffer || !buffer->data) {
     365            2 :         return NULL;
     366              :     }
     367              :     /* OK if count is 0. Negative offset puts at reverse_end anyway. */
     368          124 :     return (unsigned char *)buffer->data
     369           62 :          + ((buffer->count - 1) * buffer->sizeof_type);
     370           64 : }
     371              : 
     372              : void *
     373         1415 : CCC_flat_buffer_next(
     374              :     CCC_Flat_buffer const *const buffer, void const *const iterator
     375              : ) {
     376         1415 :     if (!buffer || !buffer->capacity || !iterator) {
     377            1 :         return NULL;
     378              :     }
     379         1414 :     if ((char *)iterator
     380         1414 :         >= (char *)buffer->data + ((buffer->count - 1) * buffer->sizeof_type)) {
     381           78 :         return CCC_flat_buffer_end(buffer);
     382              :     }
     383         1336 :     return (unsigned char *)iterator + buffer->sizeof_type;
     384         1415 : }
     385              : 
     386              : void *
     387          384 : CCC_flat_buffer_reverse_next(
     388              :     CCC_Flat_buffer const *const buffer, void const *const iterator
     389              : ) {
     390          384 :     if (!buffer || !buffer->data || !iterator) {
     391            1 :         return NULL;
     392              :     }
     393          383 :     if (iterator <= CCC_flat_buffer_reverse_end(buffer)) {
     394            1 :         return CCC_flat_buffer_reverse_end(buffer);
     395              :     }
     396          382 :     return (char *)iterator - buffer->sizeof_type;
     397          384 : }
     398              : 
     399              : void *
     400         1251 : CCC_flat_buffer_end(CCC_Flat_buffer const *const buffer) {
     401         1251 :     if (!buffer || !buffer->data) {
     402            2 :         return NULL;
     403              :     }
     404         2498 :     return (unsigned char *)buffer->data
     405         1249 :          + (buffer->count * buffer->sizeof_type);
     406         1251 : }
     407              : 
     408              : /** We accept that reverse_end is out of bounds and the address before start.
     409              : Even if the array base was somehow 0 and wrapping occurred upon subtraction the
     410              : iterator would eventually reach this same address through reverse_next and be
     411              : compared to it in the main user loop. */
     412              : void *
     413          738 : CCC_flat_buffer_reverse_end(CCC_Flat_buffer const *const buffer) {
     414          738 :     if (!buffer || !buffer->data) {
     415            1 :         return NULL;
     416              :     }
     417          737 :     return (unsigned char *)buffer->data - buffer->sizeof_type;
     418          738 : }
     419              : 
     420              : CCC_Count
     421           91 : CCC_flat_buffer_index(
     422              :     CCC_Flat_buffer const *const buffer, void const *const slot
     423              : ) {
     424           91 :     if (!buffer || !buffer->data || !slot || slot < buffer->data
     425           89 :         || (char *)slot
     426          176 :                >= ((char *)buffer->data
     427           88 :                    + (buffer->capacity * buffer->sizeof_type))) {
     428            3 :         return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
     429              :     }
     430            0 :     assert(
     431           88 :         slot >= buffer->data && "positive pointer difference is caught at entry"
     432              :     );
     433          176 :     return (CCC_Count){
     434              :         .count
     435           88 :         = ((size_t)((char *)slot - ((char *)buffer->data))
     436           88 :            / buffer->sizeof_type),
     437              :     };
     438           91 : }
     439              : 
     440              : CCC_Result
     441            3 : CCC_flat_buffer_count_plus(CCC_Flat_buffer *const buffer, size_t const count) {
     442            3 :     if (!buffer) {
     443            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     444              :     }
     445            2 :     size_t const new_count = buffer->count + count;
     446            2 :     if (new_count > buffer->capacity) {
     447            1 :         buffer->count = buffer->capacity;
     448            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     449              :     }
     450            1 :     buffer->count = new_count;
     451            1 :     return CCC_RESULT_OK;
     452            3 : }
     453              : 
     454              : CCC_Result
     455            3 : CCC_flat_buffer_count_minus(CCC_Flat_buffer *const buffer, size_t const count) {
     456            3 :     if (!buffer) {
     457            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     458              :     }
     459            2 :     if (count > buffer->count) {
     460            1 :         buffer->count = 0;
     461            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     462              :     }
     463            1 :     buffer->count -= count;
     464            1 :     return CCC_RESULT_OK;
     465            3 : }
     466              : 
     467              : CCC_Result
     468            3 : CCC_flat_buffer_count_set(CCC_Flat_buffer *const buffer, size_t const count) {
     469            3 :     if (!buffer) {
     470            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     471              :     }
     472            2 :     if (count > buffer->capacity) {
     473            1 :         buffer->count = buffer->capacity;
     474            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     475              :     }
     476            1 :     buffer->count = count;
     477            1 :     return CCC_RESULT_OK;
     478            3 : }
     479              : 
     480              : CCC_Count
     481            3 : CCC_flat_buffer_count_bytes(CCC_Flat_buffer const *buffer) {
     482            3 :     if (!buffer) {
     483            1 :         return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
     484              :     }
     485            2 :     return (CCC_Count){.count = buffer->count * buffer->sizeof_type};
     486            3 : }
     487              : 
     488              : CCC_Count
     489            2 : CCC_flat_buffer_capacity_bytes(CCC_Flat_buffer const *buffer) {
     490            2 :     if (!buffer) {
     491            1 :         return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
     492              :     }
     493            2 :     return (CCC_Count){
     494            1 :         .count = buffer->capacity * buffer->sizeof_type,
     495              :     };
     496            2 : }
     497              : 
     498              : CCC_Result
     499           18 : CCC_flat_buffer_copy(
     500              :     CCC_Flat_buffer *const destination,
     501              :     CCC_Flat_buffer const *const source,
     502              :     CCC_Allocator const *const allocator
     503              : ) {
     504           18 :     if (!destination || !source || source == destination || !allocator
     505           16 :         || (destination->capacity < source->capacity && !allocator->allocate)) {
     506            6 :         return CCC_RESULT_ARGUMENT_ERROR;
     507              :     }
     508           12 :     if (!source->capacity) {
     509            1 :         return CCC_RESULT_OK;
     510              :     }
     511           11 :     if (destination->capacity < source->capacity) {
     512           16 :         CCC_Result const r = CCC_flat_buffer_allocate(
     513            8 :             destination, source->capacity, allocator
     514              :         );
     515            8 :         if (r != CCC_RESULT_OK) {
     516            1 :             return r;
     517              :         }
     518            7 :         destination->capacity = source->capacity;
     519            8 :     }
     520           10 :     if (!source->data || !destination->data) {
     521            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     522              :     }
     523            9 :     destination->count = source->count;
     524            9 :     (void)memcpy(
     525            9 :         destination->data, source->data, source->capacity * source->sizeof_type
     526              :     );
     527            9 :     return CCC_RESULT_OK;
     528           18 : }
     529              : 
     530              : void *
     531            3 : CCC_flat_buffer_data(CCC_Flat_buffer const *const buffer) {
     532            3 :     return buffer ? buffer->data : NULL;
     533              : }
     534              : 
     535              : /*======================  Static Helpers  ==================================*/
     536              : 
     537              : static inline void *
     538           38 : at(struct CCC_Flat_buffer const *const buffer, size_t const i) {
     539           38 :     return ((char *)buffer->data + (i * buffer->sizeof_type));
     540              : }
     541              : 
     542              : static inline size_t
     543           21 : max(size_t const a, size_t const b) {
     544           21 :     return a > b ? a : b;
     545              : }
        

Generated by: LCOV version 2.5.0-beta