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

Generated by: LCOV version 2.4-beta