LCOV - code coverage report
Current view: top level - source/flat_double_ended_queue.c (source / functions) Coverage Total Hit
Test: CCC Test Suite Coverage Report Lines: 96.8 % 465 450
Test Date: 2026-05-12 15:05:06 Functions: 100.0 % 45 45

            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 <limits.h>
      16              : #include <stddef.h>
      17              : #include <stdint.h>
      18              : 
      19              : /** CCC provided headers. */
      20              : #include "ccc/configuration.h"
      21              : #include "ccc/flat_buffer.h"
      22              : #include "ccc/flat_double_ended_queue.h"
      23              : #include "ccc/private/private_flat_double_ended_queue.h"
      24              : #include "ccc/types.h"
      25              : 
      26              : enum : size_t {
      27              :     START_CAPACITY = 8,
      28              : };
      29              : 
      30              : /*==========================    Prototypes    ===============================*/
      31              : 
      32              : static CCC_Result maybe_resize(
      33              :     struct CCC_Flat_double_ended_queue *, size_t, CCC_Allocator const *
      34              : );
      35              : static size_t
      36              : index_of(struct CCC_Flat_double_ended_queue const *, void const *);
      37              : static void *wrapping_at(struct CCC_Flat_double_ended_queue const *, size_t);
      38              : static void *raw_at(CCC_Flat_buffer const *, size_t);
      39              : static size_t increment(struct CCC_Flat_double_ended_queue const *, size_t);
      40              : static size_t decrement(struct CCC_Flat_double_ended_queue const *, size_t);
      41              : static size_t
      42              : distance(struct CCC_Flat_double_ended_queue const *, size_t, size_t);
      43              : static size_t
      44              : reverse_distance(struct CCC_Flat_double_ended_queue const *, size_t, size_t);
      45              : static void *push_front_range(
      46              :     struct CCC_Flat_double_ended_queue *,
      47              :     CCC_Flat_buffer const *,
      48              :     CCC_Allocator const *
      49              : );
      50              : static void *push_back_range(
      51              :     struct CCC_Flat_double_ended_queue *,
      52              :     CCC_Flat_buffer const *,
      53              :     CCC_Allocator const *
      54              : );
      55              : static size_t back_free_slot(struct CCC_Flat_double_ended_queue const *);
      56              : static size_t front_free_slot(size_t, size_t);
      57              : static size_t last_index(struct CCC_Flat_double_ended_queue const *);
      58              : static void *push_range(
      59              :     struct CCC_Flat_double_ended_queue *,
      60              :     void const *,
      61              :     CCC_Flat_buffer const *,
      62              :     CCC_Allocator const *
      63              : );
      64              : static void *
      65              : allocate_front(struct CCC_Flat_double_ended_queue *, CCC_Allocator const *);
      66              : static void *
      67              : allocate_back(struct CCC_Flat_double_ended_queue *, CCC_Allocator const *);
      68              : static size_t min(size_t, size_t);
      69              : static void
      70              : destroy_all(struct CCC_Flat_double_ended_queue const *, CCC_Destructor const *);
      71              : 
      72              : /*==========================     Interface    ===============================*/
      73              : 
      74              : void *
      75          104 : CCC_flat_double_ended_queue_push_back(
      76              :     CCC_Flat_double_ended_queue *const queue,
      77              :     void const *const type,
      78              :     CCC_Allocator const *const allocator
      79              : ) {
      80          104 :     if (!queue || !type || !allocator) {
      81            3 :         return NULL;
      82              :     }
      83          101 :     void *const slot = allocate_back(queue, allocator);
      84          101 :     if (slot && slot != type) {
      85           99 :         (void)memcpy(slot, type, queue->buffer.sizeof_type);
      86           99 :     }
      87          101 :     return slot;
      88          104 : }
      89              : 
      90              : void *
      91           99 : CCC_flat_double_ended_queue_push_front(
      92              :     CCC_Flat_double_ended_queue *const queue,
      93              :     void const *const type,
      94              :     CCC_Allocator const *const allocator
      95              : ) {
      96           99 :     if (!queue || !type || !allocator) {
      97            3 :         return NULL;
      98              :     }
      99           96 :     void *const slot = allocate_front(queue, allocator);
     100           96 :     if (slot && slot != type) {
     101           96 :         (void)memcpy(slot, type, queue->buffer.sizeof_type);
     102           96 :     }
     103           96 :     return slot;
     104           99 : }
     105              : 
     106              : CCC_Result
     107           14 : CCC_flat_double_ended_queue_push_front_range(
     108              :     CCC_Flat_double_ended_queue *const queue,
     109              :     CCC_Flat_buffer const *const range,
     110              :     CCC_Allocator const *const allocator
     111              : ) {
     112           14 :     if (!queue || !range || range->sizeof_type != queue->buffer.sizeof_type
     113           12 :         || !allocator) {
     114            3 :         return CCC_RESULT_ARGUMENT_ERROR;
     115              :     }
     116           11 :     return push_front_range(queue, range, allocator)
     117              :              ? CCC_RESULT_OK
     118              :              : CCC_RESULT_ALLOCATOR_ERROR;
     119           14 : }
     120              : 
     121              : CCC_Result
     122           32 : CCC_flat_double_ended_queue_push_back_range(
     123              :     CCC_Flat_double_ended_queue *const queue,
     124              :     CCC_Flat_buffer const *const range,
     125              :     CCC_Allocator const *const allocator
     126              : ) {
     127           32 :     if (!queue || !range || range->sizeof_type != queue->buffer.sizeof_type
     128           30 :         || !allocator) {
     129            3 :         return CCC_RESULT_ARGUMENT_ERROR;
     130              :     }
     131           29 :     return push_back_range(queue, range, allocator)
     132              :              ? CCC_RESULT_OK
     133              :              : CCC_RESULT_ALLOCATOR_ERROR;
     134           32 : }
     135              : 
     136              : void *
     137           31 : CCC_flat_double_ended_queue_insert_range(
     138              :     CCC_Flat_double_ended_queue *const queue,
     139              :     void *position,
     140              :     CCC_Flat_buffer const *const range,
     141              :     CCC_Allocator const *const allocator
     142              : ) {
     143           31 :     if (!queue || !range || range->sizeof_type != queue->buffer.sizeof_type
     144           29 :         || !allocator) {
     145            3 :         return NULL;
     146              :     }
     147           28 :     if (!range->count) {
     148            1 :         return position;
     149              :     }
     150           27 :     if (position == CCC_flat_double_ended_queue_begin(queue)) {
     151            6 :         return push_front_range(queue, range, allocator);
     152              :     }
     153           21 :     if (position == CCC_flat_double_ended_queue_end(queue)) {
     154            6 :         return push_back_range(queue, range, allocator);
     155              :     }
     156           15 :     return push_range(queue, position, range, allocator);
     157           31 : }
     158              : 
     159              : CCC_Result
     160          171 : CCC_flat_double_ended_queue_pop_front(
     161              :     CCC_Flat_double_ended_queue *const queue
     162              : ) {
     163          171 :     if (!queue || !queue->buffer.count) {
     164            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     165              :     }
     166          170 :     queue->front = increment(queue, queue->front);
     167          170 :     --queue->buffer.count;
     168          170 :     return CCC_RESULT_OK;
     169          171 : }
     170              : 
     171              : CCC_Result
     172          110 : CCC_flat_double_ended_queue_pop_back(CCC_Flat_double_ended_queue *const queue) {
     173          110 :     if (!queue || !queue->buffer.count) {
     174            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     175              :     }
     176          109 :     --queue->buffer.count;
     177          109 :     return CCC_RESULT_OK;
     178          110 : }
     179              : 
     180              : void *
     181          171 : CCC_flat_double_ended_queue_front(
     182              :     CCC_Flat_double_ended_queue const *const queue
     183              : ) {
     184          171 :     if (!queue || !queue->buffer.count) {
     185            1 :         return NULL;
     186              :     }
     187          170 :     return raw_at(&queue->buffer, queue->front);
     188          171 : }
     189              : 
     190              : void *
     191          106 : CCC_flat_double_ended_queue_back(
     192              :     CCC_Flat_double_ended_queue const *const queue
     193              : ) {
     194          106 :     if (!queue || !queue->buffer.count) {
     195            1 :         return NULL;
     196              :     }
     197          105 :     return raw_at(&queue->buffer, last_index(queue));
     198          106 : }
     199              : 
     200              : CCC_Tribool
     201          343 : CCC_flat_double_ended_queue_is_empty(
     202              :     CCC_Flat_double_ended_queue const *const queue
     203              : ) {
     204          343 :     if (!queue) {
     205            1 :         return CCC_TRIBOOL_ERROR;
     206              :     }
     207          342 :     return !queue->buffer.count;
     208          343 : }
     209              : 
     210              : CCC_Count
     211          607 : CCC_flat_double_ended_queue_count(
     212              :     CCC_Flat_double_ended_queue const *const queue
     213              : ) {
     214          607 :     if (!queue) {
     215            1 :         return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
     216              :     }
     217          606 :     return (CCC_Count){.count = queue->buffer.count};
     218          607 : }
     219              : 
     220              : CCC_Count
     221           13 : CCC_flat_double_ended_queue_capacity(
     222              :     CCC_Flat_double_ended_queue const *const queue
     223              : ) {
     224           13 :     if (!queue) {
     225            1 :         return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
     226              :     }
     227           12 :     return (CCC_Count){.count = queue->buffer.capacity};
     228           13 : }
     229              : 
     230              : void *
     231           17 : CCC_flat_double_ended_queue_at(
     232              :     CCC_Flat_double_ended_queue const *const queue, size_t const i
     233              : ) {
     234           17 :     if (!queue || i >= queue->buffer.capacity) {
     235            2 :         return NULL;
     236              :     }
     237           15 :     return wrapping_at(queue, i);
     238           17 : }
     239              : 
     240              : void *
     241          128 : CCC_flat_double_ended_queue_begin(
     242              :     CCC_Flat_double_ended_queue const *const queue
     243              : ) {
     244          128 :     if (!queue || !queue->buffer.count) {
     245            2 :         return NULL;
     246              :     }
     247          126 :     return raw_at(&queue->buffer, queue->front);
     248          128 : }
     249              : 
     250              : void *
     251           98 : CCC_flat_double_ended_queue_reverse_begin(
     252              :     CCC_Flat_double_ended_queue const *const queue
     253              : ) {
     254           98 :     if (!queue || !queue->buffer.count) {
     255            1 :         return NULL;
     256              :     }
     257           97 :     return raw_at(&queue->buffer, last_index(queue));
     258           98 : }
     259              : 
     260              : void *
     261          515 : CCC_flat_double_ended_queue_next(
     262              :     CCC_Flat_double_ended_queue const *const queue,
     263              :     void const *const iterator_pointer
     264              : ) {
     265          515 :     if (!queue || !iterator_pointer) {
     266            2 :         return NULL;
     267              :     }
     268          513 :     size_t const next_i = increment(queue, index_of(queue, iterator_pointer));
     269          513 :     if (next_i == queue->front
     270          513 :         || distance(queue, next_i, queue->front) >= queue->buffer.count) {
     271           99 :         return NULL;
     272              :     }
     273          414 :     return raw_at(&queue->buffer, next_i);
     274          515 : }
     275              : 
     276              : void *
     277          501 : CCC_flat_double_ended_queue_reverse_next(
     278              :     CCC_Flat_double_ended_queue const *const queue,
     279              :     void const *const iterator_pointer
     280              : ) {
     281          501 :     if (!queue || !iterator_pointer) {
     282            2 :         return NULL;
     283              :     }
     284          499 :     size_t const cur_i = index_of(queue, iterator_pointer);
     285          499 :     size_t const next_i = decrement(queue, cur_i);
     286          499 :     size_t const reverse_begin = last_index(queue);
     287          499 :     if (next_i == reverse_begin
     288          499 :         || reverse_distance(queue, next_i, reverse_begin)
     289          449 :                >= queue->buffer.count) {
     290           97 :         return NULL;
     291              :     }
     292          402 :     return raw_at(&queue->buffer, next_i);
     293          501 : }
     294              : 
     295              : void *
     296          641 : CCC_flat_double_ended_queue_end(CCC_Flat_double_ended_queue const *const) {
     297          641 :     return NULL;
     298              : }
     299              : 
     300              : void *
     301          597 : CCC_flat_double_ended_queue_reverse_end(
     302              :     CCC_Flat_double_ended_queue const *const
     303              : ) {
     304          597 :     return NULL;
     305              : }
     306              : 
     307              : void *
     308            1 : CCC_flat_double_ended_queue_data(
     309              :     CCC_Flat_double_ended_queue const *const queue
     310              : ) {
     311            1 :     return queue ? CCC_flat_buffer_begin(&queue->buffer) : NULL;
     312              : }
     313              : 
     314              : CCC_Result
     315            8 : CCC_flat_double_ended_queue_copy(
     316              :     CCC_Flat_double_ended_queue *const destination,
     317              :     CCC_Flat_double_ended_queue const *const source,
     318              :     CCC_Allocator const *const allocator
     319              : ) {
     320            8 :     if (!destination || !source || !allocator || source == destination
     321            7 :         || (destination->buffer.capacity < source->buffer.capacity
     322            7 :             && !allocator->allocate)) {
     323            2 :         return CCC_RESULT_ARGUMENT_ERROR;
     324              :     }
     325              :     /* Copying from an empty source is odd but supported. */
     326            6 :     if (!source->buffer.capacity) {
     327            1 :         destination->front = destination->buffer.count = 0;
     328            1 :         return CCC_RESULT_OK;
     329              :     }
     330            5 :     if (destination->buffer.capacity < source->buffer.capacity) {
     331            6 :         CCC_Result resize_res = CCC_flat_buffer_allocate(
     332            3 :             &destination->buffer, source->buffer.capacity, allocator
     333              :         );
     334            3 :         if (resize_res != CCC_RESULT_OK) {
     335            1 :             return resize_res;
     336              :         }
     337            2 :         destination->buffer.capacity = source->buffer.capacity;
     338            3 :     }
     339            4 :     if (!destination->buffer.data || !source->buffer.data) {
     340            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     341              :     }
     342            3 :     destination->buffer.count = source->buffer.count;
     343            3 :     if (destination->buffer.capacity > source->buffer.capacity) {
     344            4 :         size_t const first_chunk = min(
     345            2 :             source->buffer.count, source->buffer.capacity - source->front
     346              :         );
     347            2 :         (void)memcpy(
     348            2 :             destination->buffer.data,
     349            2 :             raw_at(&source->buffer, source->front),
     350            2 :             source->buffer.sizeof_type * first_chunk
     351              :         );
     352            2 :         if (first_chunk < source->buffer.count) {
     353            2 :             (void)memcpy(
     354            1 :                 (char *)destination->buffer.data
     355            1 :                     + (source->buffer.sizeof_type * first_chunk),
     356            1 :                 source->buffer.data,
     357            1 :                 source->buffer.sizeof_type
     358            1 :                     * (source->buffer.count - first_chunk)
     359              :             );
     360            1 :         }
     361            2 :         destination->front = 0;
     362            2 :         return CCC_RESULT_OK;
     363            2 :     }
     364            1 :     destination->front = source->front;
     365            1 :     (void)memcpy(
     366            1 :         destination->buffer.data,
     367            1 :         source->buffer.data,
     368            1 :         source->buffer.capacity * source->buffer.sizeof_type
     369              :     );
     370            1 :     return CCC_RESULT_OK;
     371            8 : }
     372              : 
     373              : CCC_Result
     374            3 : CCC_flat_double_ended_queue_reserve(
     375              :     CCC_Flat_double_ended_queue *const queue,
     376              :     size_t const to_add,
     377              :     CCC_Allocator const *const allocator
     378              : ) {
     379            3 :     if (!queue || !allocator || !allocator->allocate || !to_add) {
     380            2 :         return CCC_RESULT_ARGUMENT_ERROR;
     381              :     }
     382            1 :     return maybe_resize(queue, to_add, allocator);
     383            3 : }
     384              : 
     385              : CCC_Result
     386            4 : CCC_flat_double_ended_queue_clear(
     387              :     CCC_Flat_double_ended_queue *const queue,
     388              :     CCC_Destructor const *const destructor
     389              : ) {
     390            4 :     if (!queue || !destructor) {
     391            2 :         return CCC_RESULT_ARGUMENT_ERROR;
     392              :     }
     393            2 :     if (!destructor->destroy || !queue->buffer.count) {
     394            1 :         queue->front = 0;
     395            1 :         queue->buffer.count = 0;
     396            1 :         return CCC_RESULT_OK;
     397              :     }
     398            1 :     destroy_all(queue, destructor);
     399            1 :     return CCC_RESULT_OK;
     400            4 : }
     401              : 
     402              : CCC_Result
     403           15 : CCC_flat_double_ended_queue_clear_and_free(
     404              :     CCC_Flat_double_ended_queue *const queue,
     405              :     CCC_Destructor const *const destructor,
     406              :     CCC_Allocator const *const allocator
     407              : ) {
     408           15 :     if (!queue || !destructor || !allocator || !allocator->allocate) {
     409            5 :         return CCC_RESULT_ARGUMENT_ERROR;
     410              :     }
     411           10 :     if (!destructor->destroy || !queue->buffer.count) {
     412            9 :         queue->buffer.count = queue->front = 0;
     413            9 :         return CCC_flat_buffer_allocate(&queue->buffer, 0, allocator);
     414              :     }
     415            1 :     destroy_all(queue, destructor);
     416            1 :     CCC_Result const r = CCC_flat_buffer_allocate(&queue->buffer, 0, allocator);
     417            1 :     if (r == CCC_RESULT_OK) {
     418            1 :         queue->buffer.count = queue->front = 0;
     419            1 :     }
     420            1 :     return r;
     421           15 : }
     422              : 
     423              : CCC_Tribool
     424           43 : CCC_flat_double_ended_queue_validate(
     425              :     CCC_Flat_double_ended_queue const *const queue
     426              : ) {
     427           43 :     if (!queue) {
     428            0 :         return CCC_TRIBOOL_ERROR;
     429              :     }
     430           43 :     if (CCC_flat_double_ended_queue_is_empty(queue)) {
     431            3 :         return CCC_TRUE;
     432              :     }
     433           40 :     void *iterator = CCC_flat_double_ended_queue_begin(queue);
     434           40 :     if (CCC_flat_buffer_index(&queue->buffer, iterator).count != queue->front) {
     435            0 :         return CCC_FALSE;
     436              :     }
     437           40 :     size_t size = 0;
     438          194 :     for (; iterator != CCC_flat_double_ended_queue_end(queue);
     439          154 :          iterator = CCC_flat_double_ended_queue_next(queue, iterator), ++size) {
     440          154 :         if (size >= CCC_flat_double_ended_queue_count(queue).count) {
     441            0 :             return CCC_FALSE;
     442              :         }
     443          154 :     }
     444           40 :     if (size != CCC_flat_double_ended_queue_count(queue).count) {
     445            0 :         return CCC_FALSE;
     446              :     }
     447           40 :     size = 0;
     448           40 :     iterator = CCC_flat_double_ended_queue_reverse_begin(queue);
     449           80 :     if (CCC_flat_buffer_index(&queue->buffer, iterator).count
     450           40 :         != last_index(queue)) {
     451            0 :         return CCC_FALSE;
     452              :     }
     453          194 :     for (; iterator != CCC_flat_double_ended_queue_reverse_end(queue);
     454          154 :          iterator = CCC_flat_double_ended_queue_reverse_next(queue, iterator),
     455          154 :          ++size) {
     456          154 :         if (size >= CCC_flat_double_ended_queue_count(queue).count) {
     457            0 :             return CCC_FALSE;
     458              :         }
     459          154 :     }
     460           40 :     return size == CCC_flat_double_ended_queue_count(queue).count;
     461           43 : }
     462              : 
     463              : /*======================   Private Interface   ==============================*/
     464              : 
     465              : void *
     466            3 : CCC_private_flat_double_ended_queue_allocate_back(
     467              :     struct CCC_Flat_double_ended_queue *const queue,
     468              :     CCC_Allocator const *const allocator
     469              : ) {
     470            3 :     return allocate_back(queue, allocator);
     471              : }
     472              : 
     473              : void *
     474            1 : CCC_private_flat_double_ended_queue_allocate_front(
     475              :     struct CCC_Flat_double_ended_queue *const queue,
     476              :     CCC_Allocator const *const allocator
     477              : ) {
     478            1 :     return allocate_front(queue, allocator);
     479              : }
     480              : 
     481              : /*======================     Static Helpers    ==============================*/
     482              : 
     483              : static void *
     484           97 : allocate_front(
     485              :     struct CCC_Flat_double_ended_queue *const queue,
     486              :     CCC_Allocator const *const allocator
     487              : ) {
     488           97 :     CCC_Tribool const full = maybe_resize(queue, 1, allocator) != CCC_RESULT_OK;
     489           97 :     if ((full && !queue->buffer.capacity) || (allocator->allocate && full)) {
     490          128 :         return NULL;
     491              :     }
     492           97 :     queue->front = front_free_slot(queue->front, queue->buffer.capacity);
     493           97 :     void *const new_slot = raw_at(&queue->buffer, queue->front);
     494           97 :     if (!full) {
     495           97 :         ++queue->buffer.count;
     496           97 :     }
     497           97 :     return new_slot;
     498           97 : }
     499              : 
     500              : static void *
     501          110 : allocate_back(
     502              :     struct CCC_Flat_double_ended_queue *const queue,
     503              :     CCC_Allocator const *const allocator
     504              : ) {
     505          110 :     CCC_Tribool const full = maybe_resize(queue, 1, allocator) != CCC_RESULT_OK;
     506          110 :     if ((full && !queue->buffer.capacity) || (allocator->allocate && full)) {
     507          128 :         return NULL;
     508              :     }
     509          102 :     void *const new_slot = raw_at(&queue->buffer, back_free_slot(queue));
     510              :     /* If no reallocation policy is given we are a ring buffer. */
     511          102 :     if (full) {
     512            1 :         queue->front = increment(queue, queue->front);
     513            1 :     } else {
     514          101 :         ++queue->buffer.count;
     515              :     }
     516          102 :     return new_slot;
     517          104 : }
     518              : 
     519              : static void *
     520           43 : push_back_range(
     521              :     struct CCC_Flat_double_ended_queue *const queue,
     522              :     CCC_Flat_buffer const *const range,
     523              :     CCC_Allocator const *const allocator
     524              : ) {
     525           43 :     size_t const sizeof_type = queue->buffer.sizeof_type;
     526           86 :     CCC_Tribool const full
     527           43 :         = maybe_resize(queue, range->count, allocator) != CCC_RESULT_OK;
     528           43 :     size_t const cap = queue->buffer.capacity;
     529           43 :     if ((full && !queue->buffer.capacity) || (allocator->allocate && full)) {
     530            8 :         return NULL;
     531              :     }
     532           35 :     if (range->count >= cap) {
     533           11 :         queue->front = 0;
     534           22 :         void *const return_this = memcpy(
     535           11 :             raw_at(&queue->buffer, 0),
     536           11 :             raw_at(range, range->count - cap),
     537           11 :             sizeof_type * cap
     538              :         );
     539           11 :         queue->buffer.count = cap;
     540           11 :         return return_this;
     541           11 :     }
     542           24 :     size_t const new_size = queue->buffer.count + range->count;
     543           24 :     size_t const back_slot = back_free_slot(queue);
     544           24 :     size_t const chunk = min(range->count, cap - back_slot);
     545           24 :     size_t const remainder_back_slot = (back_slot + chunk) % cap;
     546           24 :     size_t const remainder = (range->count - chunk);
     547           48 :     void *const return_this = memcpy(
     548           24 :         raw_at(&queue->buffer, back_slot), range->data, chunk * sizeof_type
     549              :     );
     550           24 :     if (remainder) {
     551            2 :         (void)memcpy(
     552            2 :             raw_at(&queue->buffer, remainder_back_slot),
     553            2 :             raw_at(range, chunk),
     554            2 :             remainder * sizeof_type
     555              :         );
     556            2 :     }
     557           24 :     if (new_size > cap) {
     558            8 :         queue->front = (queue->front + (new_size - cap)) % cap;
     559            8 :     }
     560           24 :     queue->buffer.count = min(cap, new_size);
     561           24 :     return return_this;
     562           35 : }
     563              : 
     564              : static void *
     565           17 : push_front_range(
     566              :     struct CCC_Flat_double_ended_queue *const queue,
     567              :     CCC_Flat_buffer const *const range,
     568              :     CCC_Allocator const *const allocator
     569              : ) {
     570           17 :     size_t const sizeof_type = queue->buffer.sizeof_type;
     571           34 :     CCC_Tribool const full
     572           17 :         = maybe_resize(queue, range->count, allocator) != CCC_RESULT_OK;
     573           17 :     size_t const cap = queue->buffer.capacity;
     574           17 :     if ((full && !queue->buffer.capacity) || (allocator->allocate && full)) {
     575            0 :         return NULL;
     576              :     }
     577           17 :     if (range->count >= cap) {
     578            5 :         queue->front = 0;
     579           10 :         void *const return_this = memcpy(
     580            5 :             raw_at(&queue->buffer, 0),
     581            5 :             raw_at(range, range->count - cap),
     582            5 :             sizeof_type * cap
     583              :         );
     584            5 :         queue->buffer.count = cap;
     585            5 :         return return_this;
     586            5 :     }
     587           12 :     size_t const space_ahead = front_free_slot(queue->front, cap) + 1;
     588           24 :     size_t const i
     589           12 :         = range->count > space_ahead ? 0 : space_ahead - range->count;
     590           12 :     size_t const chunk = min(range->count, space_ahead);
     591           12 :     size_t const remainder = (range->count - chunk);
     592           24 :     void *const return_this = memcpy(
     593           12 :         raw_at(&queue->buffer, i),
     594           12 :         raw_at(range, range->count - chunk),
     595           12 :         chunk * sizeof_type
     596              :     );
     597           12 :     if (remainder) {
     598            4 :         (void)memcpy(
     599            4 :             raw_at(&queue->buffer, cap - remainder),
     600            4 :             range->data,
     601            4 :             remainder * sizeof_type
     602              :         );
     603            4 :     }
     604           12 :     queue->buffer.count = min(cap, queue->buffer.count + range->count);
     605           12 :     queue->front = remainder ? cap - remainder : i;
     606           12 :     return return_this;
     607           17 : }
     608              : 
     609              : static void *
     610           15 : push_range(
     611              :     struct CCC_Flat_double_ended_queue *const queue,
     612              :     void const *const position,
     613              :     CCC_Flat_buffer const *const range,
     614              :     CCC_Allocator const *const allocator
     615              : ) {
     616           15 :     size_t const sizeof_type = queue->buffer.sizeof_type;
     617           30 :     CCC_Tribool const full
     618           15 :         = maybe_resize(queue, range->count, allocator) != CCC_RESULT_OK;
     619           15 :     if ((full && !queue->buffer.capacity) || (allocator->allocate && full)) {
     620            0 :         return NULL;
     621              :     }
     622           15 :     size_t const cap = queue->buffer.capacity;
     623           15 :     size_t const new_size = queue->buffer.count + range->count;
     624           15 :     if (range->count >= cap) {
     625            2 :         queue->front = 0;
     626            4 :         void *const return_this = memcpy(
     627            2 :             raw_at(&queue->buffer, 0),
     628            2 :             raw_at(range, range->count - cap),
     629            2 :             sizeof_type * cap
     630              :         );
     631            2 :         queue->buffer.count = cap;
     632            2 :         return return_this;
     633            2 :     }
     634           13 :     size_t const pos_i = index_of(queue, position);
     635           13 :     size_t const back = back_free_slot(queue);
     636           13 :     size_t const to_move = back > pos_i ? back - pos_i : cap - pos_i + back;
     637           13 :     size_t const move_i = (pos_i + range->count) % cap;
     638           13 :     size_t move_chunk = move_i + to_move > cap ? cap - move_i : to_move;
     639           13 :     move_chunk = back < pos_i ? min(cap - pos_i, move_chunk)
     640            8 :                               : min(back - pos_i, move_chunk);
     641           13 :     size_t const move_remain = to_move - move_chunk;
     642           13 :     (void)memmove(
     643           13 :         raw_at(&queue->buffer, move_i),
     644           13 :         raw_at(&queue->buffer, pos_i),
     645           13 :         move_chunk * sizeof_type
     646              :     );
     647           13 :     if (move_remain) {
     648            6 :         size_t const move_remain_i = (move_i + move_chunk) % cap;
     649            6 :         size_t const remaining_start_i = (pos_i + move_chunk) % cap;
     650            6 :         (void)memmove(
     651            6 :             raw_at(&queue->buffer, move_remain_i),
     652            6 :             raw_at(&queue->buffer, remaining_start_i),
     653            6 :             move_remain * sizeof_type
     654              :         );
     655            6 :     }
     656           13 :     size_t const elements_chunk = min(range->count, cap - pos_i);
     657           13 :     size_t const elements_remain = range->count - elements_chunk;
     658           26 :     void *const return_this = memcpy(
     659           13 :         raw_at(&queue->buffer, pos_i), range->data, elements_chunk * sizeof_type
     660              :     );
     661           13 :     if (elements_remain) {
     662            4 :         size_t const second_chunk_i = (pos_i + elements_chunk) % cap;
     663            4 :         (void)memcpy(
     664            4 :             raw_at(&queue->buffer, second_chunk_i),
     665            4 :             raw_at(range, elements_chunk),
     666            4 :             elements_remain * sizeof_type
     667              :         );
     668            4 :     }
     669           13 :     if (new_size > cap) {
     670              :         /* Wrapping behavior stops if it would overwrite the start of the
     671              :            range being inserted. This is to preserve as much info about
     672              :            the range as possible. If wrapping occurs the range is the new
     673              :            front. */
     674            8 :         size_t const excess = (new_size - cap);
     675            8 :         size_t const front_to_pos_dist = (pos_i + cap - queue->front) % cap;
     676            8 :         queue->front = (queue->front + min(excess, front_to_pos_dist)) % cap;
     677            8 :     }
     678           13 :     queue->buffer.count = min(cap, new_size);
     679           13 :     return return_this;
     680           15 : }
     681              : 
     682              : static CCC_Result
     683          269 : maybe_resize(
     684              :     struct CCC_Flat_double_ended_queue *const queue,
     685              :     size_t const to_add,
     686              :     CCC_Allocator const *const allocator
     687              : ) {
     688          269 :     size_t required = queue->buffer.count + to_add;
     689          269 :     if (required < queue->buffer.count) {
     690            0 :         return CCC_RESULT_ARGUMENT_ERROR;
     691              :     }
     692          269 :     if (required <= queue->buffer.capacity) {
     693          225 :         return CCC_RESULT_OK;
     694              :     }
     695           44 :     if (!allocator->allocate) {
     696           39 :         return CCC_RESULT_NO_ALLOCATION_FUNCTION;
     697              :     }
     698            5 :     size_t const sizeof_type = queue->buffer.sizeof_type;
     699            5 :     if (!queue->buffer.capacity && to_add == 1) {
     700            0 :         required = START_CAPACITY;
     701            5 :     } else if (to_add == 1) {
     702            3 :         required = queue->buffer.capacity * 2;
     703            3 :     }
     704           15 :     void *const new_data = allocator->allocate((CCC_Allocator_arguments){
     705              :         .input = NULL,
     706            5 :         .bytes = sizeof_type * required,
     707            5 :         .context = allocator->context,
     708              :     });
     709            5 :     if (!new_data) {
     710            0 :         return CCC_RESULT_ALLOCATOR_ERROR;
     711              :     }
     712            5 :     if (queue->buffer.count) {
     713            6 :         size_t const first_chunk
     714            3 :             = min(queue->buffer.count, queue->buffer.capacity - queue->front);
     715            3 :         (void)memcpy(
     716            3 :             new_data,
     717            3 :             raw_at(&queue->buffer, queue->front),
     718            3 :             sizeof_type * first_chunk
     719              :         );
     720            3 :         if (first_chunk < queue->buffer.count) {
     721            6 :             (void)memcpy(
     722            3 :                 (char *)new_data + (sizeof_type * first_chunk),
     723            3 :                 CCC_flat_buffer_begin(&queue->buffer),
     724            3 :                 sizeof_type * (queue->buffer.count - first_chunk)
     725              :             );
     726            3 :         }
     727            3 :     }
     728            5 :     (void)CCC_flat_buffer_allocate(&queue->buffer, 0, allocator);
     729            5 :     queue->buffer.data = new_data;
     730            5 :     queue->front = 0;
     731            5 :     queue->buffer.capacity = required;
     732            5 :     return CCC_RESULT_OK;
     733          269 : }
     734              : 
     735              : static inline void
     736            2 : destroy_all(
     737              :     struct CCC_Flat_double_ended_queue const *const queue,
     738              :     CCC_Destructor const *const destructor
     739              : ) {
     740            0 :     assert(
     741            2 :         queue->buffer.count
     742            2 :         && "queue is not empty otherwise full cannot be distinguished from "
     743              :            "empty"
     744              :     );
     745            2 :     size_t const back = back_free_slot(queue);
     746            2 :     size_t i = queue->front;
     747            2 :     do {
     748           24 :         destructor->destroy((CCC_Arguments){
     749            8 :             .type = raw_at(&queue->buffer, i),
     750            8 :             .context = destructor->context,
     751              :         });
     752            8 :         i = increment(queue, i);
     753            8 :     } while (i != back);
     754            2 : }
     755              : 
     756              : /** Returns the distance between the current iterator position and the origin
     757              : position. Distance is calculated in ascending indices, meaning the result is
     758              : the number of forward steps in the Flat_buffer origin would need to take reach
     759              : iterator, possibly accounting for wrapping around the end of the buffer. */
     760              : static inline size_t
     761          463 : distance(
     762              :     struct CCC_Flat_double_ended_queue const *const queue,
     763              :     size_t const iterator,
     764              :     size_t const origin
     765              : ) {
     766          463 :     return iterator > origin ? iterator - origin
     767           94 :                              : (queue->buffer.capacity - origin) + iterator;
     768              : }
     769              : 
     770              : /** Returns the rdistance between the current iterator position and the origin
     771              : position. Rdistance is calculated in descending indices, meaning the result is
     772              : the number of backward steps in the Flat_buffer origin would need to take to
     773              : reach iterator, possibly accounting for wrapping around beginning of buffer. */
     774              : static inline size_t
     775          449 : reverse_distance(
     776              :     struct CCC_Flat_double_ended_queue const *const queue,
     777              :     size_t const iterator,
     778              :     size_t const origin
     779              : ) {
     780          449 :     return iterator > origin ? (queue->buffer.capacity - iterator) + origin
     781          323 :                              : origin - iterator;
     782              : }
     783              : 
     784              : static inline size_t
     785         1025 : index_of(
     786              :     struct CCC_Flat_double_ended_queue const *const queue,
     787              :     void const *const position
     788              : ) {
     789         1025 :     assert(position >= CCC_flat_buffer_begin(&queue->buffer));
     790            0 :     assert(
     791         1025 :         (char *)position
     792         2050 :         < (char *)queue->buffer.data
     793         1025 :               + (queue->buffer.capacity * queue->buffer.capacity)
     794              :     );
     795         1025 :     return (
     796         1025 :         (size_t)((char *)position
     797         1025 :                  - (char *)CCC_flat_buffer_begin(&queue->buffer))
     798         1025 :         / queue->buffer.sizeof_type
     799              :     );
     800              : }
     801              : 
     802              : /** Delivers the element at the requested index relative to the front of the
     803              : double ended queue. This accounts for the wrapping that can occur of elements
     804              : if the front of the double ended queue is not 0. */
     805              : static inline void *
     806           15 : wrapping_at(
     807              :     CCC_Flat_double_ended_queue const *const queue, size_t const index
     808              : ) {
     809            0 :     assert(
     810           15 :         index < queue->buffer.capacity && "wrap access to buffer is in bounds"
     811              :     );
     812           30 :     return (char *)queue->buffer.data
     813           30 :          + (((queue->front + index) % queue->buffer.capacity)
     814           15 :             * queue->buffer.sizeof_type);
     815              : }
     816              : 
     817              : /** Delivers the slot at the requested index zero based. Assumes the index is
     818              : within capacity.  */
     819              : static inline void *
     820         1677 : raw_at(CCC_Flat_buffer const *const buffer, size_t const index) {
     821         1677 :     assert(index < buffer->capacity && "raw access to buffer is in bounds");
     822         1677 :     return (char *)buffer->data + (buffer->sizeof_type * index);
     823              : }
     824              : 
     825              : static inline size_t
     826          692 : increment(
     827              :     struct CCC_Flat_double_ended_queue const *const queue, size_t const index
     828              : ) {
     829          692 :     return index == (queue->buffer.capacity - 1) ? 0 : index + 1;
     830              : }
     831              : 
     832              : static inline size_t
     833          499 : decrement(
     834              :     struct CCC_Flat_double_ended_queue const *const queue, size_t const index
     835              : ) {
     836          499 :     return index ? index - 1 : queue->buffer.capacity - 1;
     837              : }
     838              : 
     839              : static inline size_t
     840          141 : back_free_slot(struct CCC_Flat_double_ended_queue const *const queue) {
     841          141 :     return (queue->front + queue->buffer.count) % queue->buffer.capacity;
     842              : }
     843              : 
     844              : static inline size_t
     845          109 : front_free_slot(size_t const front, size_t const capacity) {
     846          109 :     return front ? front - 1 : capacity - 1;
     847              : }
     848              : 
     849              : /** Returns index of last element in the queue or front if empty. */
     850              : static inline size_t
     851          741 : last_index(struct CCC_Flat_double_ended_queue const *const queue) {
     852          741 :     return queue->buffer.count
     853          741 :              ? (queue->front + queue->buffer.count - 1) % queue->buffer.capacity
     854            0 :              : queue->front;
     855              : }
     856              : 
     857              : static inline size_t
     858          124 : min(size_t const left, size_t const right) {
     859          124 :     return left < right ? left : right;
     860              : }
        

Generated by: LCOV version 2.5.0-beta