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: 97.0 % 460 446
Test Date: 2026-04-02 00:15:37 Functions: 100.0 % 44 44

            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/buffer.h"
      21              : #include "ccc/configuration.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 *at(struct CCC_Flat_double_ended_queue const *, size_t);
      38              : static size_t increment(struct CCC_Flat_double_ended_queue const *, size_t);
      39              : static size_t decrement(struct CCC_Flat_double_ended_queue const *, size_t);
      40              : static size_t
      41              : distance(struct CCC_Flat_double_ended_queue const *, size_t, size_t);
      42              : static size_t
      43              : rdistance(struct CCC_Flat_double_ended_queue const *, size_t, size_t);
      44              : static CCC_Result push_front_range(
      45              :     struct CCC_Flat_double_ended_queue *,
      46              :     CCC_Buffer const *,
      47              :     CCC_Allocator const *
      48              : );
      49              : static CCC_Result push_back_range(
      50              :     struct CCC_Flat_double_ended_queue *,
      51              :     CCC_Buffer const *,
      52              :     CCC_Allocator const *
      53              : );
      54              : static size_t back_free_slot(struct CCC_Flat_double_ended_queue const *);
      55              : static size_t front_free_slot(size_t, size_t);
      56              : static size_t last_index(struct CCC_Flat_double_ended_queue const *);
      57              : static void *push_range(
      58              :     struct CCC_Flat_double_ended_queue *,
      59              :     void const *,
      60              :     CCC_Buffer const *,
      61              :     CCC_Allocator const *
      62              : );
      63              : static void *
      64              : allocate_front(struct CCC_Flat_double_ended_queue *, CCC_Allocator const *);
      65              : static void *
      66              : allocate_back(struct CCC_Flat_double_ended_queue *, CCC_Allocator const *);
      67              : static size_t min(size_t, size_t);
      68              : static void
      69              : destroy_all(struct CCC_Flat_double_ended_queue const *, CCC_Destructor const *);
      70              : 
      71              : /*==========================     Interface    ===============================*/
      72              : 
      73              : void *
      74          104 : CCC_flat_double_ended_queue_push_back(
      75              :     CCC_Flat_double_ended_queue *const queue,
      76              :     void const *const type,
      77              :     CCC_Allocator const *const allocator
      78              : ) {
      79          104 :     if (!queue || !type || !allocator) {
      80            3 :         return NULL;
      81              :     }
      82          101 :     void *const slot = allocate_back(queue, allocator);
      83          101 :     if (slot && slot != type) {
      84           99 :         (void)memcpy(slot, type, queue->buffer.sizeof_type);
      85           99 :     }
      86          101 :     return slot;
      87          104 : }
      88              : 
      89              : void *
      90           99 : CCC_flat_double_ended_queue_push_front(
      91              :     CCC_Flat_double_ended_queue *const queue,
      92              :     void const *const type,
      93              :     CCC_Allocator const *const allocator
      94              : ) {
      95           99 :     if (!queue || !type || !allocator) {
      96            3 :         return NULL;
      97              :     }
      98           96 :     void *const slot = allocate_front(queue, allocator);
      99           96 :     if (slot && slot != type) {
     100           96 :         (void)memcpy(slot, type, queue->buffer.sizeof_type);
     101           96 :     }
     102           96 :     return slot;
     103           99 : }
     104              : 
     105              : CCC_Result
     106           14 : CCC_flat_double_ended_queue_push_front_range(
     107              :     CCC_Flat_double_ended_queue *const queue,
     108              :     CCC_Buffer const *const range,
     109              :     CCC_Allocator const *const allocator
     110              : ) {
     111           14 :     if (!queue || !range || range->sizeof_type != queue->buffer.sizeof_type
     112           12 :         || !allocator) {
     113            3 :         return CCC_RESULT_ARGUMENT_ERROR;
     114              :     }
     115           11 :     return push_front_range(queue, range, allocator);
     116           14 : }
     117              : 
     118              : CCC_Result
     119           32 : CCC_flat_double_ended_queue_push_back_range(
     120              :     CCC_Flat_double_ended_queue *const queue,
     121              :     CCC_Buffer const *const range,
     122              :     CCC_Allocator const *const allocator
     123              : ) {
     124           32 :     if (!queue || !range || range->sizeof_type != queue->buffer.sizeof_type
     125           30 :         || !allocator) {
     126            3 :         return CCC_RESULT_ARGUMENT_ERROR;
     127              :     }
     128           29 :     return push_back_range(queue, range, allocator);
     129           32 : }
     130              : 
     131              : void *
     132           31 : CCC_flat_double_ended_queue_insert_range(
     133              :     CCC_Flat_double_ended_queue *const queue,
     134              :     void *position,
     135              :     CCC_Buffer const *const range,
     136              :     CCC_Allocator const *const allocator
     137              : ) {
     138           31 :     if (!queue || !range || range->sizeof_type != queue->buffer.sizeof_type
     139           29 :         || !allocator) {
     140            3 :         return NULL;
     141              :     }
     142           28 :     if (!range->count) {
     143            1 :         return position;
     144              :     }
     145           27 :     if (position == CCC_flat_double_ended_queue_begin(queue)) {
     146            6 :         return push_front_range(queue, range, allocator) != CCC_RESULT_OK
     147              :                  ? NULL
     148            6 :                  : at(queue, range->count - 1);
     149              :     }
     150           21 :     if (position == CCC_flat_double_ended_queue_end(queue)) {
     151            6 :         return push_back_range(queue, range, allocator) != CCC_RESULT_OK
     152              :                  ? NULL
     153            6 :                  : at(queue, queue->buffer.count - range->count);
     154              :     }
     155           15 :     return push_range(queue, position, range, allocator);
     156           31 : }
     157              : 
     158              : CCC_Result
     159          171 : CCC_flat_double_ended_queue_pop_front(
     160              :     CCC_Flat_double_ended_queue *const queue
     161              : ) {
     162          171 :     if (!queue || !queue->buffer.count) {
     163            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     164              :     }
     165          170 :     queue->front = increment(queue, queue->front);
     166          170 :     --queue->buffer.count;
     167          170 :     return CCC_RESULT_OK;
     168          171 : }
     169              : 
     170              : CCC_Result
     171          110 : CCC_flat_double_ended_queue_pop_back(CCC_Flat_double_ended_queue *const queue) {
     172          110 :     if (!queue || !queue->buffer.count) {
     173            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     174              :     }
     175          109 :     --queue->buffer.count;
     176          109 :     return CCC_RESULT_OK;
     177          110 : }
     178              : 
     179              : void *
     180          171 : CCC_flat_double_ended_queue_front(
     181              :     CCC_Flat_double_ended_queue const *const queue
     182              : ) {
     183          171 :     if (!queue || !queue->buffer.count) {
     184            1 :         return NULL;
     185              :     }
     186          170 :     return CCC_buffer_at(&queue->buffer, queue->front);
     187          171 : }
     188              : 
     189              : void *
     190          106 : CCC_flat_double_ended_queue_back(
     191              :     CCC_Flat_double_ended_queue const *const queue
     192              : ) {
     193          106 :     if (!queue || !queue->buffer.count) {
     194            1 :         return NULL;
     195              :     }
     196          105 :     return CCC_buffer_at(&queue->buffer, last_index(queue));
     197          106 : }
     198              : 
     199              : CCC_Tribool
     200          343 : CCC_flat_double_ended_queue_is_empty(
     201              :     CCC_Flat_double_ended_queue const *const queue
     202              : ) {
     203          343 :     if (!queue) {
     204            1 :         return CCC_TRIBOOL_ERROR;
     205              :     }
     206          342 :     return !queue->buffer.count;
     207          343 : }
     208              : 
     209              : CCC_Count
     210          607 : CCC_flat_double_ended_queue_count(
     211              :     CCC_Flat_double_ended_queue const *const queue
     212              : ) {
     213          607 :     if (!queue) {
     214            1 :         return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
     215              :     }
     216          606 :     return (CCC_Count){.count = queue->buffer.count};
     217          607 : }
     218              : 
     219              : CCC_Count
     220           13 : CCC_flat_double_ended_queue_capacity(
     221              :     CCC_Flat_double_ended_queue const *const queue
     222              : ) {
     223           13 :     if (!queue) {
     224            1 :         return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
     225              :     }
     226           12 :     return (CCC_Count){.count = queue->buffer.capacity};
     227           13 : }
     228              : 
     229              : void *
     230           17 : CCC_flat_double_ended_queue_at(
     231              :     CCC_Flat_double_ended_queue const *const queue, size_t const i
     232              : ) {
     233           17 :     if (!queue || i >= queue->buffer.capacity) {
     234            2 :         return NULL;
     235              :     }
     236           15 :     return CCC_buffer_at(
     237           15 :         &queue->buffer, (queue->front + i) % queue->buffer.capacity
     238              :     );
     239           17 : }
     240              : 
     241              : void *
     242          128 : CCC_flat_double_ended_queue_begin(
     243              :     CCC_Flat_double_ended_queue const *const queue
     244              : ) {
     245          128 :     if (!queue || !queue->buffer.count) {
     246            2 :         return NULL;
     247              :     }
     248          126 :     return CCC_buffer_at(&queue->buffer, queue->front);
     249          128 : }
     250              : 
     251              : void *
     252           98 : CCC_flat_double_ended_queue_reverse_begin(
     253              :     CCC_Flat_double_ended_queue const *const queue
     254              : ) {
     255           98 :     if (!queue || !queue->buffer.count) {
     256            1 :         return NULL;
     257              :     }
     258           97 :     return CCC_buffer_at(&queue->buffer, last_index(queue));
     259           98 : }
     260              : 
     261              : void *
     262          515 : CCC_flat_double_ended_queue_next(
     263              :     CCC_Flat_double_ended_queue const *const queue,
     264              :     void const *const iterator_pointer
     265              : ) {
     266          515 :     if (!queue || !iterator_pointer) {
     267            2 :         return NULL;
     268              :     }
     269          513 :     size_t const next_i = increment(queue, index_of(queue, iterator_pointer));
     270          513 :     if (next_i == queue->front
     271          513 :         || distance(queue, next_i, queue->front) >= queue->buffer.count) {
     272           99 :         return NULL;
     273              :     }
     274          414 :     return CCC_buffer_at(&queue->buffer, next_i);
     275          515 : }
     276              : 
     277              : void *
     278          501 : CCC_flat_double_ended_queue_reverse_next(
     279              :     CCC_Flat_double_ended_queue const *const queue,
     280              :     void const *const iterator_pointer
     281              : ) {
     282          501 :     if (!queue || !iterator_pointer) {
     283            2 :         return NULL;
     284              :     }
     285          499 :     size_t const cur_i = index_of(queue, iterator_pointer);
     286          499 :     size_t const next_i = decrement(queue, cur_i);
     287          499 :     size_t const reverse_begin = last_index(queue);
     288          499 :     if (next_i == reverse_begin
     289          499 :         || rdistance(queue, next_i, reverse_begin) >= queue->buffer.count) {
     290           97 :         return NULL;
     291              :     }
     292          402 :     return CCC_buffer_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_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_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 :             CCC_buffer_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_buffer_allocate(&queue->buffer, 0, allocator);
     414              :     }
     415            1 :     destroy_all(queue, destructor);
     416            1 :     CCC_Result const r = CCC_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_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           40 :     if (CCC_buffer_index(&queue->buffer, iterator).count != last_index(queue)) {
     450            0 :         return CCC_FALSE;
     451              :     }
     452          194 :     for (; iterator != CCC_flat_double_ended_queue_reverse_end(queue);
     453          154 :          iterator = CCC_flat_double_ended_queue_reverse_next(queue, iterator),
     454          154 :          ++size) {
     455          154 :         if (size >= CCC_flat_double_ended_queue_count(queue).count) {
     456            0 :             return CCC_FALSE;
     457              :         }
     458          154 :     }
     459           40 :     return size == CCC_flat_double_ended_queue_count(queue).count;
     460           43 : }
     461              : 
     462              : /*======================   Private Interface   ==============================*/
     463              : 
     464              : void *
     465            3 : CCC_private_flat_double_ended_queue_allocate_back(
     466              :     struct CCC_Flat_double_ended_queue *const queue,
     467              :     CCC_Allocator const *const allocator
     468              : ) {
     469            3 :     return allocate_back(queue, allocator);
     470              : }
     471              : 
     472              : void *
     473            1 : CCC_private_flat_double_ended_queue_allocate_front(
     474              :     struct CCC_Flat_double_ended_queue *const queue,
     475              :     CCC_Allocator const *const allocator
     476              : ) {
     477            1 :     return allocate_front(queue, allocator);
     478              : }
     479              : 
     480              : /*======================     Static Helpers    ==============================*/
     481              : 
     482              : static void *
     483           97 : allocate_front(
     484              :     struct CCC_Flat_double_ended_queue *const queue,
     485              :     CCC_Allocator const *const allocator
     486              : ) {
     487           97 :     CCC_Tribool const full = maybe_resize(queue, 1, allocator) != CCC_RESULT_OK;
     488              :     /* Should have been able to resize. Bad error. */
     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 = CCC_buffer_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              :     /* Should have been able to resize. Bad error. */
     507          110 :     if ((full && !queue->buffer.capacity) || (allocator->allocate && full)) {
     508          128 :         return NULL;
     509              :     }
     510          102 :     void *const new_slot = CCC_buffer_at(&queue->buffer, back_free_slot(queue));
     511              :     /* If no reallocation policy is given we are a ring buffer. */
     512          102 :     if (full) {
     513            1 :         queue->front = increment(queue, queue->front);
     514            1 :     } else {
     515          101 :         ++queue->buffer.count;
     516              :     }
     517          102 :     return new_slot;
     518          104 : }
     519              : 
     520              : static CCC_Result
     521           43 : push_back_range(
     522              :     struct CCC_Flat_double_ended_queue *const queue,
     523              :     CCC_Buffer const *const range,
     524              :     CCC_Allocator const *const allocator
     525              : ) {
     526           43 :     size_t const sizeof_type = queue->buffer.sizeof_type;
     527           86 :     CCC_Tribool const full
     528           43 :         = maybe_resize(queue, range->count, allocator) != CCC_RESULT_OK;
     529           43 :     size_t const cap = queue->buffer.capacity;
     530           43 :     if ((full && !queue->buffer.capacity) || (allocator->allocate && full)) {
     531            8 :         return CCC_RESULT_ALLOCATOR_ERROR;
     532              :     }
     533              :     /* If a range is too large we can make various simplifications to preserve
     534              :        the final capacity elements. */
     535           35 :     if (range->count >= cap) {
     536           11 :         queue->front = 0;
     537           11 :         (void)memcpy(
     538           11 :             CCC_buffer_at(&queue->buffer, 0),
     539           11 :             CCC_buffer_at(range, range->count - cap),
     540           11 :             sizeof_type * cap
     541              :         );
     542           11 :         queue->buffer.count = cap;
     543           11 :         return CCC_RESULT_OK;
     544              :     }
     545           24 :     size_t const new_size = queue->buffer.count + range->count;
     546           24 :     size_t const back_slot = back_free_slot(queue);
     547           24 :     size_t const chunk = min(range->count, cap - back_slot);
     548           24 :     size_t const remainder_back_slot = (back_slot + chunk) % cap;
     549           24 :     size_t const remainder = (range->count - chunk);
     550           24 :     (void)memcpy(
     551           24 :         CCC_buffer_at(&queue->buffer, back_slot),
     552           24 :         range->data,
     553           24 :         chunk * sizeof_type
     554              :     );
     555           24 :     if (remainder) {
     556            2 :         (void)memcpy(
     557            2 :             CCC_buffer_at(&queue->buffer, remainder_back_slot),
     558            2 :             CCC_buffer_at(range, chunk),
     559            2 :             remainder * sizeof_type
     560              :         );
     561            2 :     }
     562           24 :     if (new_size > cap) {
     563            8 :         queue->front = (queue->front + (new_size - cap)) % cap;
     564            8 :     }
     565           24 :     queue->buffer.count = min(cap, new_size);
     566           24 :     return CCC_RESULT_OK;
     567           35 : }
     568              : 
     569              : static CCC_Result
     570           17 : push_front_range(
     571              :     struct CCC_Flat_double_ended_queue *const queue,
     572              :     CCC_Buffer const *const range,
     573              :     CCC_Allocator const *const allocator
     574              : ) {
     575           17 :     size_t const sizeof_type = queue->buffer.sizeof_type;
     576           34 :     CCC_Tribool const full
     577           17 :         = maybe_resize(queue, range->count, allocator) != CCC_RESULT_OK;
     578           17 :     size_t const cap = queue->buffer.capacity;
     579           17 :     if ((full && !queue->buffer.capacity) || (allocator->allocate && full)) {
     580            0 :         return CCC_RESULT_ALLOCATOR_ERROR;
     581              :     }
     582              :     /* If a range is too large we can make various simplifications to preserve
     583              :        the final capacity elements. */
     584           17 :     if (range->count >= cap) {
     585            5 :         queue->front = 0;
     586            5 :         (void)memcpy(
     587            5 :             CCC_buffer_at(&queue->buffer, 0),
     588            5 :             CCC_buffer_at(range, range->count - cap),
     589            5 :             sizeof_type * cap
     590              :         );
     591            5 :         queue->buffer.count = cap;
     592            5 :         return CCC_RESULT_OK;
     593              :     }
     594           12 :     size_t const space_ahead = front_free_slot(queue->front, cap) + 1;
     595           24 :     size_t const i
     596           12 :         = range->count > space_ahead ? 0 : space_ahead - range->count;
     597           12 :     size_t const chunk = min(range->count, space_ahead);
     598           12 :     size_t const remainder = (range->count - chunk);
     599           12 :     (void)memcpy(
     600           12 :         CCC_buffer_at(&queue->buffer, i),
     601           12 :         CCC_buffer_at(range, range->count - chunk),
     602           12 :         chunk * sizeof_type
     603              :     );
     604           12 :     if (remainder) {
     605            4 :         (void)memcpy(
     606            4 :             CCC_buffer_at(&queue->buffer, cap - remainder),
     607            4 :             range->data,
     608            4 :             remainder * sizeof_type
     609              :         );
     610            4 :     }
     611           12 :     queue->buffer.count = min(cap, queue->buffer.count + range->count);
     612           12 :     queue->front = remainder ? cap - remainder : i;
     613           12 :     return CCC_RESULT_OK;
     614           17 : }
     615              : 
     616              : static void *
     617           15 : push_range(
     618              :     struct CCC_Flat_double_ended_queue *const queue,
     619              :     void const *const position,
     620              :     CCC_Buffer const *const range,
     621              :     CCC_Allocator const *const allocator
     622              : ) {
     623           15 :     size_t const sizeof_type = queue->buffer.sizeof_type;
     624           30 :     CCC_Tribool const full
     625           15 :         = maybe_resize(queue, range->count, allocator) != CCC_RESULT_OK;
     626           15 :     if ((full && !queue->buffer.capacity) || (allocator->allocate && full)) {
     627            0 :         return NULL;
     628              :     }
     629           15 :     size_t const cap = queue->buffer.capacity;
     630           15 :     size_t const new_size = queue->buffer.count + range->count;
     631           15 :     if (range->count >= cap) {
     632            2 :         queue->front = 0;
     633            2 :         void *const ret = CCC_buffer_at(&queue->buffer, 0);
     634            2 :         (void)memcpy(
     635            2 :             ret, CCC_buffer_at(range, range->count - cap), sizeof_type * cap
     636              :         );
     637            2 :         queue->buffer.count = cap;
     638            2 :         return ret;
     639            2 :     }
     640           13 :     size_t const pos_i = index_of(queue, position);
     641           13 :     size_t const back = back_free_slot(queue);
     642           13 :     size_t const to_move = back > pos_i ? back - pos_i : cap - pos_i + back;
     643           13 :     size_t const move_i = (pos_i + range->count) % cap;
     644           13 :     size_t move_chunk = move_i + to_move > cap ? cap - move_i : to_move;
     645           13 :     move_chunk = back < pos_i ? min(cap - pos_i, move_chunk)
     646            8 :                               : min(back - pos_i, move_chunk);
     647           13 :     size_t const move_remain = to_move - move_chunk;
     648           13 :     (void)memmove(
     649           13 :         CCC_buffer_at(&queue->buffer, move_i),
     650           13 :         CCC_buffer_at(&queue->buffer, pos_i),
     651           13 :         move_chunk * sizeof_type
     652              :     );
     653           13 :     if (move_remain) {
     654            6 :         size_t const move_remain_i = (move_i + move_chunk) % cap;
     655            6 :         size_t const remaining_start_i = (pos_i + move_chunk) % cap;
     656            6 :         (void)memmove(
     657            6 :             CCC_buffer_at(&queue->buffer, move_remain_i),
     658            6 :             CCC_buffer_at(&queue->buffer, remaining_start_i),
     659            6 :             move_remain * sizeof_type
     660              :         );
     661            6 :     }
     662           13 :     size_t const elements_chunk = min(range->count, cap - pos_i);
     663           13 :     size_t const elements_remain = range->count - elements_chunk;
     664           13 :     (void)memcpy(
     665           13 :         CCC_buffer_at(&queue->buffer, pos_i),
     666           13 :         range->data,
     667           13 :         elements_chunk * sizeof_type
     668              :     );
     669           13 :     if (elements_remain) {
     670            4 :         size_t const second_chunk_i = (pos_i + elements_chunk) % cap;
     671            4 :         (void)memcpy(
     672            4 :             CCC_buffer_at(&queue->buffer, second_chunk_i),
     673            4 :             CCC_buffer_at(range, elements_chunk),
     674            4 :             elements_remain * sizeof_type
     675              :         );
     676            4 :     }
     677           13 :     if (new_size > cap) {
     678              :         /* Wrapping behavior stops if it would overwrite the start of the
     679              :            range being inserted. This is to preserve as much info about
     680              :            the range as possible. If wrapping occurs the range is the new
     681              :            front. */
     682            8 :         size_t const excess = (new_size - cap);
     683            8 :         size_t const front_to_pos_dist = (pos_i + cap - queue->front) % cap;
     684            8 :         queue->front = (queue->front + min(excess, front_to_pos_dist)) % cap;
     685            8 :     }
     686           13 :     queue->buffer.count = min(cap, new_size);
     687           13 :     return CCC_buffer_at(&queue->buffer, pos_i);
     688           15 : }
     689              : 
     690              : static CCC_Result
     691          269 : maybe_resize(
     692              :     struct CCC_Flat_double_ended_queue *const queue,
     693              :     size_t const to_add,
     694              :     CCC_Allocator const *const allocator
     695              : ) {
     696          269 :     size_t required = queue->buffer.count + to_add;
     697          269 :     if (required < queue->buffer.count) {
     698            0 :         return CCC_RESULT_ARGUMENT_ERROR;
     699              :     }
     700          269 :     if (required <= queue->buffer.capacity) {
     701          225 :         return CCC_RESULT_OK;
     702              :     }
     703           44 :     if (!allocator->allocate) {
     704           39 :         return CCC_RESULT_NO_ALLOCATION_FUNCTION;
     705              :     }
     706            5 :     size_t const sizeof_type = queue->buffer.sizeof_type;
     707            5 :     if (!queue->buffer.capacity && to_add == 1) {
     708            0 :         required = START_CAPACITY;
     709            5 :     } else if (to_add == 1) {
     710            3 :         required = queue->buffer.capacity * 2;
     711            3 :     }
     712           15 :     void *const new_data = allocator->allocate((CCC_Allocator_arguments){
     713              :         .input = NULL,
     714            5 :         .bytes = sizeof_type * required,
     715            5 :         .context = allocator->context,
     716              :     });
     717            5 :     if (!new_data) {
     718            0 :         return CCC_RESULT_ALLOCATOR_ERROR;
     719              :     }
     720            5 :     if (queue->buffer.count) {
     721            6 :         size_t const first_chunk
     722            3 :             = min(queue->buffer.count, queue->buffer.capacity - queue->front);
     723            3 :         (void)memcpy(
     724            3 :             new_data,
     725            3 :             CCC_buffer_at(&queue->buffer, queue->front),
     726            3 :             sizeof_type * first_chunk
     727              :         );
     728            3 :         if (first_chunk < queue->buffer.count) {
     729            6 :             (void)memcpy(
     730            3 :                 (char *)new_data + (sizeof_type * first_chunk),
     731            3 :                 CCC_buffer_begin(&queue->buffer),
     732            3 :                 sizeof_type * (queue->buffer.count - first_chunk)
     733              :             );
     734            3 :         }
     735            3 :     }
     736            5 :     (void)CCC_buffer_allocate(&queue->buffer, 0, allocator);
     737            5 :     queue->buffer.data = new_data;
     738            5 :     queue->front = 0;
     739            5 :     queue->buffer.capacity = required;
     740            5 :     return CCC_RESULT_OK;
     741          269 : }
     742              : 
     743              : static inline void
     744            2 : destroy_all(
     745              :     struct CCC_Flat_double_ended_queue const *const queue,
     746              :     CCC_Destructor const *const destructor
     747              : ) {
     748            0 :     assert(
     749            2 :         queue->buffer.count
     750            2 :         && "queue is not empty otherwise full cannot be distinguished from "
     751              :            "empty"
     752              :     );
     753            2 :     size_t const back = back_free_slot(queue);
     754            2 :     size_t i = queue->front;
     755            2 :     do {
     756           24 :         destructor->destroy((CCC_Arguments){
     757            8 :             .type = CCC_buffer_at(&queue->buffer, i),
     758            8 :             .context = destructor->context,
     759              :         });
     760            8 :         i = increment(queue, i);
     761            8 :     } while (i != back);
     762            2 : }
     763              : 
     764              : /** Returns the distance between the current iterator position and the origin
     765              : position. Distance is calculated in ascending indices, meaning the result is
     766              : the number of forward steps in the Buffer origin would need to take reach
     767              : iterator, possibly accounting for wrapping around the end of the buffer. */
     768              : static inline size_t
     769          463 : distance(
     770              :     struct CCC_Flat_double_ended_queue const *const queue,
     771              :     size_t const iterator,
     772              :     size_t const origin
     773              : ) {
     774          463 :     return iterator > origin ? iterator - origin
     775           94 :                              : (queue->buffer.capacity - origin) + iterator;
     776              : }
     777              : 
     778              : /** Returns the rdistance between the current iterator position and the origin
     779              : position. Rdistance is calculated in descending indices, meaning the result is
     780              : the number of backward steps in the Buffer origin would need to take to reach
     781              : iterator, possibly accounting for wrapping around the beginning of buffer. */
     782              : static inline size_t
     783          449 : rdistance(
     784              :     struct CCC_Flat_double_ended_queue const *const queue,
     785              :     size_t const iterator,
     786              :     size_t const origin
     787              : ) {
     788          449 :     return iterator > origin ? (queue->buffer.capacity - iterator) + origin
     789          323 :                              : origin - iterator;
     790              : }
     791              : 
     792              : static inline size_t
     793         1025 : index_of(
     794              :     struct CCC_Flat_double_ended_queue const *const queue,
     795              :     void const *const position
     796              : ) {
     797         1025 :     assert(position >= CCC_buffer_begin(&queue->buffer));
     798            0 :     assert(
     799         1025 :         (char *)position
     800         2050 :         < (char *)queue->buffer.data
     801         1025 :               + (queue->buffer.capacity * queue->buffer.capacity)
     802              :     );
     803         1025 :     return (
     804         1025 :         (size_t)((char *)position - (char *)CCC_buffer_begin(&queue->buffer))
     805         1025 :         / queue->buffer.sizeof_type
     806              :     );
     807              : }
     808              : 
     809              : static inline void *
     810           12 : at(struct CCC_Flat_double_ended_queue const *const queue, size_t const index) {
     811           12 :     return CCC_buffer_at(
     812           12 :         &queue->buffer, (queue->front + index) % queue->buffer.capacity
     813              :     );
     814              : }
     815              : 
     816              : static inline size_t
     817          692 : increment(
     818              :     struct CCC_Flat_double_ended_queue const *const queue, size_t const index
     819              : ) {
     820          692 :     return index == (queue->buffer.capacity - 1) ? 0 : index + 1;
     821              : }
     822              : 
     823              : static inline size_t
     824          499 : decrement(
     825              :     struct CCC_Flat_double_ended_queue const *const queue, size_t const index
     826              : ) {
     827          499 :     return index ? index - 1 : queue->buffer.capacity - 1;
     828              : }
     829              : 
     830              : static inline size_t
     831          141 : back_free_slot(struct CCC_Flat_double_ended_queue const *const queue) {
     832          141 :     return (queue->front + queue->buffer.count) % queue->buffer.capacity;
     833              : }
     834              : 
     835              : static inline size_t
     836          109 : front_free_slot(size_t const front, size_t const capacity) {
     837          109 :     return front ? front - 1 : capacity - 1;
     838              : }
     839              : 
     840              : /** Returns index of last element in the queue or front if empty. */
     841              : static inline size_t
     842          741 : last_index(struct CCC_Flat_double_ended_queue const *const queue) {
     843          741 :     return queue->buffer.count
     844          741 :              ? (queue->front + queue->buffer.count - 1) % queue->buffer.capacity
     845            0 :              : queue->front;
     846              : }
     847              : 
     848              : static inline size_t
     849          124 : min(size_t const left, size_t const right) {
     850          124 :     return left < right ? left : right;
     851              : }
        

Generated by: LCOV version 2.4.1-beta