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

Generated by: LCOV version 2.4-beta