LCOV - code coverage report
Current view: top level - source/buffer.c (source / functions) Coverage Total Hit
Test: CCC Test Suite Coverage Report Lines: 99.7 % 308 307
Test Date: 2026-04-02 00:15:37 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/buffer.h"
      19              : #include "ccc/configuration.h"
      20              : #include "ccc/private/private_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_Buffer const *, size_t);
      30              : static size_t max(size_t, size_t);
      31              : 
      32              : /*==========================    Interface    ================================*/
      33              : 
      34              : CCC_Result
      35           63 : CCC_buffer_allocate(
      36              :     CCC_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_buffer_reserve(
      61              :     CCC_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_buffer_clear(
      90              :     CCC_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_buffer_begin(buffer); i != CCC_buffer_end(buffer);
     100            8 :          i = CCC_buffer_next(buffer, i)) {
     101           24 :         destructor->destroy((CCC_Arguments){
     102            8 :             .type = i,
     103            8 :             .context = destructor->context,
     104              :         });
     105            8 :     }
     106            1 :     buffer->count = 0;
     107            1 :     return CCC_RESULT_OK;
     108            4 : }
     109              : 
     110              : CCC_Result
     111           20 : CCC_buffer_clear_and_free(
     112              :     CCC_Buffer *const buffer,
     113              :     CCC_Destructor const *const destructor,
     114              :     CCC_Allocator const *const allocator
     115              : ) {
     116           20 :     if (!buffer || !allocator || !destructor || !allocator->allocate) {
     117            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     118              :     }
     119           19 :     if (destructor->destroy) {
     120            9 :         for (void *i = CCC_buffer_begin(buffer); i != CCC_buffer_end(buffer);
     121            8 :              i = CCC_buffer_next(buffer, i)) {
     122           24 :             destructor->destroy((CCC_Arguments){
     123            8 :                 .type = i,
     124            8 :                 .context = destructor->context,
     125              :             });
     126            8 :         }
     127            1 :     }
     128           57 :     (void)allocator->allocate((CCC_Allocator_arguments){
     129           19 :         .input = buffer->data,
     130              :         .bytes = 0,
     131           19 :         .context = allocator->context,
     132              :     });
     133           19 :     buffer->data = NULL;
     134           19 :     buffer->count = 0;
     135           19 :     buffer->capacity = 0;
     136           19 :     return CCC_RESULT_OK;
     137           20 : }
     138              : 
     139              : void *
     140      2952531 : CCC_buffer_at(CCC_Buffer const *const buffer, size_t const i) {
     141      2952531 :     if (!buffer || i >= buffer->capacity) {
     142            3 :         return NULL;
     143              :     }
     144      2952528 :     return ((char *)buffer->data + (i * buffer->sizeof_type));
     145      2952531 : }
     146              : 
     147              : void *
     148           90 : CCC_buffer_back(CCC_Buffer const *const buffer) {
     149           90 :     return CCC_buffer_at(buffer, buffer->count - 1);
     150              : }
     151              : 
     152              : void *
     153            7 : CCC_buffer_front(CCC_Buffer const *const buffer) {
     154            7 :     return CCC_buffer_at(buffer, 0);
     155              : }
     156              : 
     157              : void *
     158         4877 : CCC_buffer_allocate_back(
     159              :     CCC_Buffer *const buffer, CCC_Allocator const *const allocator
     160              : ) {
     161         4877 :     if (!buffer || !allocator) {
     162            4 :         return NULL;
     163              :     }
     164         4873 :     if (buffer->count == buffer->capacity) {
     165           40 :         CCC_Result const resize_res = CCC_buffer_allocate(
     166           20 :             buffer, max(buffer->capacity * 2, START_CAPACITY), allocator
     167              :         );
     168           20 :         if (resize_res != CCC_RESULT_OK) {
     169            6 :             return NULL;
     170              :         }
     171           20 :     }
     172         9734 :     void *const ret
     173         4867 :         = ((char *)buffer->data + (buffer->sizeof_type * buffer->count));
     174         4867 :     ++buffer->count;
     175         4867 :     return ret;
     176         4877 : }
     177              : 
     178              : void *
     179          149 : CCC_buffer_push_back(
     180              :     CCC_Buffer *const buffer,
     181              :     void const *const data,
     182              :     CCC_Allocator const *const allocator
     183              : ) {
     184          149 :     if (!data) {
     185            1 :         return NULL;
     186              :     }
     187          148 :     void *const slot = CCC_buffer_allocate_back(buffer, allocator);
     188          148 :     if (slot) {
     189          142 :         (void)memcpy(slot, data, buffer->sizeof_type);
     190          142 :     }
     191          148 :     return slot;
     192          149 : }
     193              : 
     194              : CCC_Result
     195        14814 : CCC_buffer_swap(
     196              :     CCC_Buffer const *const buffer,
     197              :     void *const temp,
     198              :     size_t const index,
     199              :     size_t const swap_index
     200              : ) {
     201        14814 :     if (!buffer || !temp || index >= buffer->capacity
     202        14814 :         || swap_index >= buffer->capacity || swap_index == index) {
     203            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     204              :     }
     205        14813 :     (void)memcpy(temp, at(buffer, index), buffer->sizeof_type);
     206        14813 :     (void)memcpy(
     207        14813 :         at(buffer, index), at(buffer, swap_index), buffer->sizeof_type
     208              :     );
     209        14813 :     (void)memcpy(at(buffer, swap_index), temp, buffer->sizeof_type);
     210        14813 :     return CCC_RESULT_OK;
     211        14814 : }
     212              : 
     213              : void *
     214            3 : CCC_buffer_move(
     215              :     CCC_Buffer const *const buffer,
     216              :     size_t const destination,
     217              :     size_t const source
     218              : ) {
     219            3 :     if (!buffer || destination >= buffer->capacity
     220            3 :         || source >= buffer->capacity) {
     221            1 :         return NULL;
     222              :     }
     223            2 :     if (destination == source) {
     224            1 :         return at(buffer, destination);
     225              :     }
     226            1 :     return memcpy(
     227            1 :         at(buffer, destination), at(buffer, source), buffer->sizeof_type
     228              :     );
     229            3 : }
     230              : 
     231              : CCC_Result
     232           10 : CCC_buffer_write(
     233              :     CCC_Buffer const *const buffer, size_t const i, void const *const data
     234              : ) {
     235           10 :     if (!buffer || !buffer->data || !data) {
     236            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     237              :     }
     238            9 :     void *const pos = CCC_buffer_at(buffer, i);
     239            9 :     if (!pos || data == pos) {
     240            2 :         return CCC_RESULT_ARGUMENT_ERROR;
     241              :     }
     242            7 :     (void)memcpy(pos, data, buffer->sizeof_type);
     243            7 :     return CCC_RESULT_OK;
     244           10 : }
     245              : 
     246              : CCC_Result
     247            6 : CCC_buffer_erase(CCC_Buffer *const buffer, size_t const i) {
     248            6 :     if (!buffer || !buffer->count || i >= buffer->count) {
     249            2 :         return CCC_RESULT_ARGUMENT_ERROR;
     250              :     }
     251            4 :     if (1 == buffer->count) {
     252            1 :         buffer->count = 0;
     253            1 :         return CCC_RESULT_OK;
     254              :     }
     255            3 :     if (i == buffer->count - 1) {
     256            1 :         --buffer->count;
     257            1 :         return CCC_RESULT_OK;
     258              :     }
     259            2 :     (void)memmove(
     260            2 :         at(buffer, i),
     261            2 :         at(buffer, i + 1),
     262            2 :         buffer->sizeof_type * (buffer->count - (i + 1))
     263              :     );
     264            2 :     --buffer->count;
     265            2 :     return CCC_RESULT_OK;
     266            6 : }
     267              : 
     268              : void *
     269           11 : CCC_buffer_insert(
     270              :     CCC_Buffer *const buffer,
     271              :     size_t const i,
     272              :     void const *const data,
     273              :     CCC_Allocator const *const allocator
     274              : ) {
     275           11 :     if (!buffer || !buffer->data || i > buffer->count || !allocator) {
     276            4 :         return NULL;
     277              :     }
     278            7 :     if (i == buffer->count) {
     279            1 :         return CCC_buffer_push_back(buffer, data, allocator);
     280              :     }
     281            6 :     if (buffer->count == buffer->capacity) {
     282            2 :         CCC_Result const r = CCC_buffer_allocate(
     283            1 :             buffer, max(buffer->count * 2, START_CAPACITY), allocator
     284              :         );
     285            1 :         if (r != CCC_RESULT_OK) {
     286            1 :             return NULL;
     287              :         }
     288            1 :     }
     289            5 :     (void)memmove(
     290            5 :         at(buffer, i + 1),
     291            5 :         at(buffer, i),
     292            5 :         buffer->sizeof_type * (buffer->count - i)
     293              :     );
     294            5 :     ++buffer->count;
     295            5 :     return memcpy(at(buffer, i), data, buffer->sizeof_type);
     296           11 : }
     297              : 
     298              : CCC_Result
     299           66 : CCC_buffer_pop_back_n(CCC_Buffer *const buffer, size_t count) {
     300           66 :     if (!buffer || count > buffer->count) {
     301            4 :         return CCC_RESULT_ARGUMENT_ERROR;
     302              :     }
     303           62 :     buffer->count -= count;
     304           62 :     return CCC_RESULT_OK;
     305           66 : }
     306              : 
     307              : CCC_Result
     308           64 : CCC_buffer_pop_back(CCC_Buffer *const buffer) {
     309           64 :     return CCC_buffer_pop_back_n(buffer, 1);
     310              : }
     311              : 
     312              : CCC_Count
     313         1078 : CCC_buffer_count(CCC_Buffer const *const buffer) {
     314         1078 :     if (!buffer) {
     315            1 :         return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
     316              :     }
     317         1077 :     return (CCC_Count){.count = buffer->count};
     318         1078 : }
     319              : 
     320              : CCC_Count
     321           29 : CCC_buffer_capacity(CCC_Buffer const *const buffer) {
     322           29 :     if (!buffer) {
     323            1 :         return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
     324              :     }
     325           28 :     return (CCC_Count){.count = buffer->capacity};
     326           29 : }
     327              : 
     328              : CCC_Count
     329          128 : CCC_buffer_sizeof_type(CCC_Buffer const *const buffer) {
     330          128 :     if (!buffer) {
     331            1 :         return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
     332              :     }
     333          127 :     return (CCC_Count){.count = buffer->sizeof_type};
     334          128 : }
     335              : 
     336              : CCC_Tribool
     337         2038 : CCC_buffer_is_empty(CCC_Buffer const *const buffer) {
     338         2038 :     if (!buffer) {
     339            1 :         return CCC_TRIBOOL_ERROR;
     340              :     }
     341         2037 :     return !buffer->count;
     342         2038 : }
     343              : 
     344              : CCC_Tribool
     345           29 : CCC_buffer_is_full(CCC_Buffer const *const buffer) {
     346           29 :     if (!buffer) {
     347            1 :         return CCC_TRIBOOL_ERROR;
     348              :     }
     349           28 :     if (!buffer->capacity) {
     350            1 :         return CCC_FALSE;
     351              :     }
     352           27 :     return buffer->count == buffer->capacity ? CCC_TRUE : CCC_FALSE;
     353           29 : }
     354              : 
     355              : void *
     356         2156 : CCC_buffer_begin(CCC_Buffer const *const buffer) {
     357         2156 :     return buffer ? buffer->data : NULL;
     358              : }
     359              : 
     360              : void *
     361           64 : CCC_buffer_reverse_begin(CCC_Buffer const *const buffer) {
     362           64 :     if (!buffer || !buffer->data) {
     363            2 :         return NULL;
     364              :     }
     365              :     /* OK if count is 0. Negative offset puts at reverse_end anyway. */
     366          124 :     return (unsigned char *)buffer->data
     367           62 :          + ((buffer->count - 1) * buffer->sizeof_type);
     368           64 : }
     369              : 
     370              : void *
     371         1458 : CCC_buffer_next(CCC_Buffer const *const buffer, void const *const iterator) {
     372         1458 :     if (!buffer || !buffer->capacity || !iterator) {
     373            1 :         return NULL;
     374              :     }
     375         1457 :     if ((char *)iterator
     376         1457 :         >= (char *)buffer->data + ((buffer->count - 1) * buffer->sizeof_type)) {
     377           77 :         return CCC_buffer_end(buffer);
     378              :     }
     379         1380 :     return (unsigned char *)iterator + buffer->sizeof_type;
     380         1458 : }
     381              : 
     382              : void *
     383          385 : CCC_buffer_reverse_next(
     384              :     CCC_Buffer const *const buffer, void const *const iterator
     385              : ) {
     386          385 :     if (!buffer || !buffer->data || !iterator) {
     387            1 :         return NULL;
     388              :     }
     389          384 :     if (iterator <= CCC_buffer_reverse_end(buffer)) {
     390            1 :         return CCC_buffer_reverse_end(buffer);
     391              :     }
     392          383 :     return (char *)iterator - buffer->sizeof_type;
     393          385 : }
     394              : 
     395              : /** We accept that end may be the address past Buffer capacity. */
     396              : void *
     397         1250 : CCC_buffer_end(CCC_Buffer const *const buffer) {
     398         1250 :     if (!buffer || !buffer->data) {
     399            2 :         return NULL;
     400              :     }
     401         2496 :     return (unsigned char *)buffer->data
     402         1248 :          + (buffer->count * buffer->sizeof_type);
     403         1250 : }
     404              : 
     405              : /** We accept that reverse_end is out of bounds and the address before start.
     406              : Even if the array base was somehow 0 and wrapping occurred upon subtraction the
     407              : iterator would eventually reach this same address through reverse_next and be
     408              : compared to it in the main user loop. */
     409              : void *
     410          739 : CCC_buffer_reverse_end(CCC_Buffer const *const buffer) {
     411          739 :     if (!buffer || !buffer->data) {
     412            1 :         return NULL;
     413              :     }
     414          738 :     return (unsigned char *)buffer->data - buffer->sizeof_type;
     415          739 : }
     416              : 
     417              : CCC_Count
     418           91 : CCC_buffer_index(CCC_Buffer const *const buffer, void const *const slot) {
     419           91 :     if (!buffer || !buffer->data || !slot || slot < buffer->data
     420           89 :         || (char *)slot
     421          176 :                >= ((char *)buffer->data
     422           88 :                    + (buffer->capacity * buffer->sizeof_type))) {
     423            3 :         return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
     424              :     }
     425            0 :     assert(
     426           88 :         slot >= buffer->data && "positive pointer difference is caught at entry"
     427              :     );
     428          176 :     return (CCC_Count){
     429              :         .count
     430           88 :         = ((size_t)((char *)slot - ((char *)buffer->data))
     431           88 :            / buffer->sizeof_type),
     432              :     };
     433           91 : }
     434              : 
     435              : CCC_Result
     436            3 : CCC_buffer_count_plus(CCC_Buffer *const buffer, size_t const count) {
     437            3 :     if (!buffer) {
     438            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     439              :     }
     440            2 :     size_t const new_count = buffer->count + count;
     441            2 :     if (new_count > buffer->capacity) {
     442            1 :         buffer->count = buffer->capacity;
     443            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     444              :     }
     445            1 :     buffer->count = new_count;
     446            1 :     return CCC_RESULT_OK;
     447            3 : }
     448              : 
     449              : CCC_Result
     450            3 : CCC_buffer_count_minus(CCC_Buffer *const buffer, size_t const count) {
     451            3 :     if (!buffer) {
     452            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     453              :     }
     454            2 :     if (count > buffer->count) {
     455            1 :         buffer->count = 0;
     456            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     457              :     }
     458            1 :     buffer->count -= count;
     459            1 :     return CCC_RESULT_OK;
     460            3 : }
     461              : 
     462              : CCC_Result
     463            4 : CCC_buffer_count_set(CCC_Buffer *const buffer, size_t const count) {
     464            4 :     if (!buffer) {
     465            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     466              :     }
     467            3 :     if (count > buffer->capacity) {
     468            1 :         buffer->count = buffer->capacity;
     469            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     470              :     }
     471            2 :     buffer->count = count;
     472            2 :     return CCC_RESULT_OK;
     473            4 : }
     474              : 
     475              : CCC_Count
     476            3 : CCC_buffer_count_bytes(CCC_Buffer const *buffer) {
     477            3 :     if (!buffer) {
     478            1 :         return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
     479              :     }
     480            2 :     return (CCC_Count){.count = buffer->count * buffer->sizeof_type};
     481            3 : }
     482              : 
     483              : CCC_Count
     484            2 : CCC_buffer_capacity_bytes(CCC_Buffer const *buffer) {
     485            2 :     if (!buffer) {
     486            1 :         return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
     487              :     }
     488            2 :     return (CCC_Count){
     489            1 :         .count = buffer->capacity * buffer->sizeof_type,
     490              :     };
     491            2 : }
     492              : 
     493              : CCC_Result
     494           18 : CCC_buffer_copy(
     495              :     CCC_Buffer *const destination,
     496              :     CCC_Buffer const *const source,
     497              :     CCC_Allocator const *const allocator
     498              : ) {
     499           18 :     if (!destination || !source || source == destination || !allocator
     500           16 :         || (destination->capacity < source->capacity && !allocator->allocate)) {
     501            6 :         return CCC_RESULT_ARGUMENT_ERROR;
     502              :     }
     503           12 :     if (!source->capacity) {
     504            1 :         return CCC_RESULT_OK;
     505              :     }
     506           11 :     if (destination->capacity < source->capacity) {
     507           16 :         CCC_Result const r
     508            8 :             = CCC_buffer_allocate(destination, source->capacity, allocator);
     509            8 :         if (r != CCC_RESULT_OK) {
     510            1 :             return r;
     511              :         }
     512            7 :         destination->capacity = source->capacity;
     513            8 :     }
     514           10 :     if (!source->data || !destination->data) {
     515            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     516              :     }
     517            9 :     destination->count = source->count;
     518            9 :     (void)memcpy(
     519            9 :         destination->data, source->data, source->capacity * source->sizeof_type
     520              :     );
     521            9 :     return CCC_RESULT_OK;
     522           18 : }
     523              : 
     524              : void *
     525            3 : CCC_buffer_data(CCC_Buffer const *const buffer) {
     526            3 :     return buffer ? buffer->data : NULL;
     527              : }
     528              : 
     529              : /*======================  Static Helpers  ==================================*/
     530              : 
     531              : static inline void *
     532        59274 : at(struct CCC_Buffer const *const buffer, size_t const i) {
     533        59274 :     return ((char *)buffer->data + (i * buffer->sizeof_type));
     534              : }
     535              : 
     536              : static inline size_t
     537           21 : max(size_t const a, size_t const b) {
     538           21 :     return a > b ? a : b;
     539              : }
        

Generated by: LCOV version 2.4.1-beta