LCOV - code coverage report
Current view: top level - source/flat_priority_queue.c (source / functions) Coverage Total Hit
Test: CCC Test Suite Coverage Report Lines: 99.2 % 379 376
Test Date: 2026-06-29 16:04:01 Functions: 100.0 % 33 33

            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              : /** A standard flat priority queue binary heap implementation. This is
      15              : 0-indexed so that there is no wasted space. The heap is fairly standard.
      16              : However, due to CCC's use of callbacks for comparison it is valuable to limit
      17              : the number of calls to this comparison callback with Bottom up heapify and
      18              : heapsort implementations when possible.
      19              : 
      20              : [1] The following paper was used to implement bottom-up heapsort.
      21              : "BOTTOM-UP-HEAPSORT, a new variant of HEAPSORT beating, on an average, QUICKSORT
      22              : (if n is not very small)" by Ingo Wegener, Theoretical Computer Science 118
      23              : (1993) 81-98. */
      24              : /** C23 provided headers. */
      25              : #include <limits.h>
      26              : #include <stddef.h>
      27              : #include <stdint.h>
      28              : 
      29              : /** CCC provided headers. */
      30              : #include "ccc/configuration.h" /* IWYU pragma: keep */
      31              : #include "ccc/flat_buffer.h"
      32              : #include "ccc/flat_priority_queue.h"
      33              : #include "ccc/private/private_flat_priority_queue.h"
      34              : #include "ccc/sort.h"
      35              : #include "ccc/types.h"
      36              : 
      37              : enum : size_t {
      38              :     START_CAP = 8,
      39              : };
      40              : 
      41              : /*=====================      Prototypes      ================================*/
      42              : 
      43              : static size_t index_of(struct CCC_Flat_priority_queue const *, void const *);
      44              : static CCC_Tribool
      45              : wins(void const *, void const *, CCC_Order, CCC_Comparator const *);
      46              : static size_t bubble_up(
      47              :     CCC_Flat_buffer const *, size_t, void *, CCC_Order, CCC_Comparator const *
      48              : );
      49              : static size_t
      50              : update_fixup(struct CCC_Flat_priority_queue const *, void *, void *);
      51              : static void
      52              : heapify(CCC_Flat_buffer const *, void *, CCC_Order, CCC_Comparator const *);
      53              : static size_t bottom_up_reheap(
      54              :     CCC_Flat_buffer const *,
      55              :     size_t,
      56              :     size_t,
      57              :     void *,
      58              :     CCC_Order,
      59              :     CCC_Comparator const *
      60              : );
      61              : static void
      62              : destroy_each(struct CCC_Flat_priority_queue *, CCC_Destructor const *);
      63              : static void swap(void *, size_t, void *, void *);
      64              : static void *at(CCC_Flat_buffer const *buffer, size_t i);
      65              : static unsigned count_leading_zeros_size_t(size_t n);
      66              : 
      67              : /*=====================       Interface      ================================*/
      68              : 
      69              : CCC_Result
      70            3 : CCC_flat_priority_queue_copy_heapify(
      71              :     CCC_Flat_priority_queue *const priority_queue,
      72              :     CCC_Flat_buffer const *const buffer,
      73              :     void *const temp,
      74              :     CCC_Allocator const *const allocator
      75              : ) {
      76            3 :     if (!priority_queue || !temp || !allocator) {
      77            1 :         return CCC_RESULT_ARGUMENT_ERROR;
      78              :     }
      79            4 :     CCC_Result const copy_result
      80            2 :         = CCC_flat_buffer_copy(&priority_queue->buffer, buffer, allocator);
      81            2 :     if (copy_result != CCC_RESULT_OK) {
      82            1 :         return copy_result;
      83              :     }
      84            1 :     heapify(
      85            1 :         &priority_queue->buffer,
      86            1 :         temp,
      87            1 :         priority_queue->order,
      88            1 :         &priority_queue->comparator
      89              :     );
      90            1 :     return CCC_RESULT_OK;
      91            3 : }
      92              : 
      93              : CCC_Flat_priority_queue
      94            2 : CCC_flat_priority_queue_in_place_heapify(
      95              :     CCC_Flat_buffer *const buffer,
      96              :     void *const temp,
      97              :     CCC_Order const order,
      98              :     CCC_Comparator const *const comparator
      99              : ) {
     100            2 :     if (!buffer || !temp || !comparator || !comparator->compare
     101            2 :         || (order != CCC_ORDER_GREATER && order != CCC_ORDER_LESSER)) {
     102            1 :         return (CCC_Flat_priority_queue){
     103              :             .order = CCC_ORDER_ERROR,
     104              :         };
     105              :     }
     106            3 :     CCC_Flat_priority_queue priority_queue = {
     107            1 :         .buffer = *buffer,
     108            1 :         .order = order,
     109            1 :         .comparator = *comparator,
     110              :     };
     111            1 :     heapify(
     112            1 :         &priority_queue.buffer,
     113            1 :         temp,
     114            1 :         priority_queue.order,
     115            1 :         &priority_queue.comparator
     116              :     );
     117            1 :     *buffer = (CCC_Flat_buffer){};
     118            1 :     return priority_queue;
     119            2 : }
     120              : 
     121              : void *
     122         1223 : CCC_flat_priority_queue_push(
     123              :     CCC_Flat_priority_queue *const priority_queue,
     124              :     void const *const type,
     125              :     void *const temp,
     126              :     CCC_Allocator const *const allocator
     127              : ) {
     128         1223 :     if (!priority_queue || !type || !temp || !allocator) {
     129            1 :         return NULL;
     130              :     }
     131         2444 :     void *const new
     132         1222 :         = CCC_flat_buffer_allocate_back(&priority_queue->buffer, allocator);
     133         1222 :     if (!new) {
     134            2 :         return NULL;
     135              :     }
     136         1220 :     if (new != type) {
     137         1219 :         (void)memcpy(new, type, priority_queue->buffer.sizeof_type);
     138         1219 :     }
     139         1220 :     assert(temp);
     140         2440 :     size_t const i = bubble_up(
     141         1220 :         &priority_queue->buffer,
     142         1220 :         priority_queue->buffer.count - 1,
     143         1220 :         temp,
     144         1220 :         priority_queue->order,
     145         1220 :         &priority_queue->comparator
     146              :     );
     147         1220 :     assert(i < priority_queue->buffer.count);
     148         1220 :     return at(&priority_queue->buffer, i);
     149         1223 : }
     150              : 
     151              : CCC_Result
     152          925 : CCC_flat_priority_queue_pop(
     153              :     CCC_Flat_priority_queue *const priority_queue, void *const temp
     154              : ) {
     155          925 :     if (!priority_queue || !temp || !priority_queue->buffer.count) {
     156            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     157              :     }
     158          924 :     --priority_queue->buffer.count;
     159          924 :     if (!priority_queue->buffer.count) {
     160           13 :         return CCC_RESULT_OK;
     161              :     }
     162          911 :     swap(
     163          911 :         temp,
     164          911 :         priority_queue->buffer.sizeof_type,
     165          911 :         at(&priority_queue->buffer, 0),
     166          911 :         at(&priority_queue->buffer, priority_queue->buffer.count)
     167              :     );
     168          911 :     bottom_up_reheap(
     169          911 :         &priority_queue->buffer,
     170          911 :         priority_queue->buffer.count,
     171              :         0,
     172          911 :         temp,
     173          911 :         priority_queue->order,
     174          911 :         &priority_queue->comparator
     175              :     );
     176          911 :     return CCC_RESULT_OK;
     177          925 : }
     178              : 
     179              : CCC_Result
     180          530 : CCC_flat_priority_queue_erase(
     181              :     CCC_Flat_priority_queue *const priority_queue,
     182              :     void *const type,
     183              :     void *const temp
     184              : ) {
     185          530 :     if (!priority_queue || !type || !temp || !priority_queue->buffer.count) {
     186            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     187              :     }
     188          529 :     size_t const i = index_of(priority_queue, type);
     189          529 :     --priority_queue->buffer.count;
     190          529 :     if (i == priority_queue->buffer.count) {
     191           13 :         return CCC_RESULT_OK;
     192              :     }
     193          516 :     swap(
     194          516 :         temp,
     195          516 :         priority_queue->buffer.sizeof_type,
     196          516 :         at(&priority_queue->buffer, i),
     197          516 :         at(&priority_queue->buffer, priority_queue->buffer.count)
     198              :     );
     199         1032 :     CCC_Order const order_res
     200         2064 :         = priority_queue->comparator.compare((CCC_Comparator_arguments){
     201          516 :             .type_left = at(&priority_queue->buffer, i),
     202              :             .type_right
     203          516 :             = at(&priority_queue->buffer, priority_queue->buffer.count),
     204          516 :             .context = priority_queue->comparator.context,
     205              :         });
     206          516 :     if (order_res == priority_queue->order) {
     207          147 :         (void)bubble_up(
     208          147 :             &priority_queue->buffer,
     209          147 :             i,
     210          147 :             temp,
     211          147 :             priority_queue->order,
     212          147 :             &priority_queue->comparator
     213              :         );
     214          516 :     } else if (order_res != CCC_ORDER_EQUAL) {
     215          367 :         bottom_up_reheap(
     216          367 :             &priority_queue->buffer,
     217          367 :             priority_queue->buffer.count,
     218          367 :             i,
     219          367 :             temp,
     220          367 :             priority_queue->order,
     221          367 :             &priority_queue->comparator
     222              :         );
     223          367 :     }
     224          516 :     return CCC_RESULT_OK;
     225          530 : }
     226              : 
     227              : void *
     228          701 : CCC_flat_priority_queue_update(
     229              :     CCC_Flat_priority_queue const *const priority_queue,
     230              :     void *const type,
     231              :     void *const temp,
     232              :     CCC_Modifier const *const modifier
     233              : ) {
     234          701 :     if (!priority_queue || !type || !temp || !modifier || !modifier->modify
     235          699 :         || !priority_queue->buffer.count) {
     236            3 :         return NULL;
     237              :     }
     238         2094 :     modifier->modify((CCC_Arguments){
     239          698 :         .type = type,
     240          698 :         .context = modifier->context,
     241              :     });
     242          698 :     return at(
     243          698 :         &priority_queue->buffer, update_fixup(priority_queue, type, temp)
     244              :     );
     245          701 : }
     246              : 
     247              : /* There are no efficiency benefits in knowing an increase will occur. */
     248              : void *
     249          203 : CCC_flat_priority_queue_increase(
     250              :     CCC_Flat_priority_queue const *const priority_queue,
     251              :     void *const type,
     252              :     void *const temp,
     253              :     CCC_Modifier const *const modifier
     254              : ) {
     255          203 :     return CCC_flat_priority_queue_update(priority_queue, type, temp, modifier);
     256              : }
     257              : 
     258              : /* There are no efficiency benefits in knowing an decrease will occur. */
     259              : void *
     260          252 : CCC_flat_priority_queue_decrease(
     261              :     CCC_Flat_priority_queue const *const priority_queue,
     262              :     void *const type,
     263              :     void *const temp,
     264              :     CCC_Modifier const *const modifier
     265              : ) {
     266          252 :     return CCC_flat_priority_queue_update(priority_queue, type, temp, modifier);
     267              : }
     268              : 
     269              : void *
     270          432 : CCC_flat_priority_queue_front(
     271              :     CCC_Flat_priority_queue const *const priority_queue
     272              : ) {
     273          432 :     if (!priority_queue || !priority_queue->buffer.count) {
     274            1 :         return NULL;
     275              :     }
     276          431 :     return at(&priority_queue->buffer, 0);
     277          432 : }
     278              : 
     279              : CCC_Tribool
     280         1250 : CCC_flat_priority_queue_is_empty(
     281              :     CCC_Flat_priority_queue const *const priority_queue
     282              : ) {
     283         1250 :     if (!priority_queue) {
     284            1 :         return CCC_TRIBOOL_ERROR;
     285              :     }
     286         1249 :     return CCC_flat_buffer_is_empty(&priority_queue->buffer);
     287         1250 : }
     288              : 
     289              : CCC_Count
     290         1008 : CCC_flat_priority_queue_count(
     291              :     CCC_Flat_priority_queue const *const priority_queue
     292              : ) {
     293         1008 :     if (!priority_queue) {
     294            1 :         return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
     295              :     }
     296         1007 :     return CCC_flat_buffer_count(&priority_queue->buffer);
     297         1008 : }
     298              : 
     299              : CCC_Count
     300            9 : CCC_flat_priority_queue_capacity(
     301              :     CCC_Flat_priority_queue const *const priority_queue
     302              : ) {
     303            9 :     if (!priority_queue) {
     304            1 :         return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
     305              :     }
     306            8 :     return CCC_flat_buffer_capacity(&priority_queue->buffer);
     307            9 : }
     308              : 
     309              : void *
     310            1 : CCC_flat_priority_queue_data(
     311              :     CCC_Flat_priority_queue const *const priority_queue
     312              : ) {
     313            1 :     return priority_queue ? CCC_flat_buffer_begin(&priority_queue->buffer)
     314              :                           : NULL;
     315              : }
     316              : 
     317              : CCC_Order
     318            1 : CCC_flat_priority_queue_order(
     319              :     CCC_Flat_priority_queue const *const priority_queue
     320              : ) {
     321            1 :     return priority_queue ? priority_queue->order : CCC_ORDER_ERROR;
     322              : }
     323              : 
     324              : CCC_Result
     325            3 : CCC_flat_priority_queue_reserve(
     326              :     CCC_Flat_priority_queue *const priority_queue,
     327              :     size_t const to_add,
     328              :     CCC_Allocator const *const allocator
     329              : ) {
     330            3 :     if (!priority_queue || !allocator) {
     331            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     332              :     }
     333            2 :     return CCC_flat_buffer_reserve(&priority_queue->buffer, to_add, allocator);
     334            3 : }
     335              : 
     336              : CCC_Result
     337            7 : CCC_flat_priority_queue_copy(
     338              :     CCC_Flat_priority_queue *const destination,
     339              :     CCC_Flat_priority_queue const *const source,
     340              :     CCC_Allocator const *const allocator
     341              : ) {
     342            7 :     if (!destination || !source || source == destination || !allocator
     343            7 :         || (destination->buffer.capacity < source->buffer.capacity
     344            7 :             && !allocator->allocate)) {
     345            2 :         return CCC_RESULT_ARGUMENT_ERROR;
     346              :     }
     347            5 :     destination->order = source->order;
     348            5 :     destination->comparator = source->comparator;
     349            5 :     if (destination->buffer.capacity < source->buffer.capacity) {
     350            4 :         CCC_Result const r = CCC_flat_buffer_allocate(
     351            2 :             &destination->buffer, source->buffer.capacity, allocator
     352              :         );
     353            2 :         if (r != CCC_RESULT_OK) {
     354            1 :             return r;
     355              :         }
     356            1 :         destination->buffer.capacity = source->buffer.capacity;
     357            2 :     }
     358            4 :     destination->buffer.count = source->buffer.count;
     359              :     /* It is ok to only copy count elements because we know that all elements
     360              :        in a binary heap are contiguous from [0, C), where C is count. */
     361            4 :     if (source->buffer.count) {
     362            3 :         if (!source->buffer.data || !destination->buffer.data) {
     363            1 :             return CCC_RESULT_ARGUMENT_ERROR;
     364              :         }
     365            2 :         (void)memcpy(
     366            2 :             destination->buffer.data,
     367            2 :             source->buffer.data,
     368            2 :             source->buffer.count * source->buffer.sizeof_type
     369              :         );
     370            2 :     }
     371            3 :     return CCC_RESULT_OK;
     372            7 : }
     373              : 
     374              : CCC_Result
     375            4 : CCC_flat_priority_queue_clear(
     376              :     CCC_Flat_priority_queue *const priority_queue,
     377              :     CCC_Destructor const *const destructor
     378              : ) {
     379            4 :     if (!priority_queue || !destructor) {
     380            3 :         return CCC_RESULT_ARGUMENT_ERROR;
     381              :     }
     382            1 :     if (destructor->destroy) {
     383            1 :         destroy_each(priority_queue, destructor);
     384            1 :     }
     385            1 :     priority_queue->buffer.count = 0;
     386            1 :     return CCC_RESULT_OK;
     387            4 : }
     388              : 
     389              : CCC_Result
     390           13 : CCC_flat_priority_queue_clear_and_free(
     391              :     CCC_Flat_priority_queue *const priority_queue,
     392              :     CCC_Destructor const *const destructor,
     393              :     CCC_Allocator const *const allocator
     394              : ) {
     395           13 :     if (!priority_queue || !destructor || !allocator) {
     396            2 :         return CCC_RESULT_ARGUMENT_ERROR;
     397              :     }
     398           11 :     if (destructor->destroy) {
     399            1 :         destroy_each(priority_queue, destructor);
     400            1 :     }
     401           11 :     return CCC_flat_buffer_allocate(&priority_queue->buffer, 0, allocator);
     402           13 : }
     403              : 
     404              : CCC_Tribool
     405         7076 : CCC_flat_priority_queue_validate(
     406              :     CCC_Flat_priority_queue const *const priority_queue
     407              : ) {
     408         7076 :     if (!priority_queue) {
     409            0 :         return CCC_TRIBOOL_ERROR;
     410              :     }
     411         7076 :     size_t const count = priority_queue->buffer.count;
     412         7076 :     if (count <= 1) {
     413           33 :         return CCC_TRUE;
     414              :     }
     415       992558 :     for (size_t i = 0,
     416         7043 :                 left = (i * 2) + 1,
     417         7043 :                 right = (i * 2) + 2,
     418         7043 :                 end = (count - 2) / 2;
     419       971429 :          i <= end;
     420       964386 :          ++i, left = (i * 2) + 1, right = (i * 2) + 2) {
     421       964386 :         void const *const this_pointer = at(&priority_queue->buffer, i);
     422              :         /* Putting the child in the comparison function first evaluates
     423              :            the child's three way comparison in relation to the parent. If
     424              :            the child beats the parent in total ordering (min/max) something
     425              :            has gone wrong. */
     426       964386 :         if (left < count
     427       964386 :             && wins(
     428       964386 :                 at(&priority_queue->buffer, left),
     429       964386 :                 this_pointer,
     430       964386 :                 priority_queue->order,
     431       964386 :                 &priority_queue->comparator
     432              :             )) {
     433            0 :             return CCC_FALSE;
     434              :         }
     435       964386 :         if (right < count
     436       964386 :             && wins(
     437       960168 :                 at(&priority_queue->buffer, right),
     438       960168 :                 this_pointer,
     439       960168 :                 priority_queue->order,
     440       960168 :                 &priority_queue->comparator
     441              :             )) {
     442            0 :             return CCC_FALSE;
     443              :         }
     444       964386 :     }
     445         7043 :     return CCC_TRUE;
     446         7076 : }
     447              : 
     448              : /*===================     Interface in sort.h   =============================*/
     449              : 
     450              : /** Bottom-Up-Heapsort adapted from "BOTTOM-UP-HEAPSORT, a new variant of
     451              : HEAPSORT beating, on an average, QUICKSORT (if n is not very small)" by Ingo
     452              : Wegener, Theoretical Computer Science 118 (1993) 81-98.
     453              : 
     454              : This implementation is valuable to the C Container Collection because we rely
     455              : on comparison callback functions over generic data. Therefore, we want to limit
     456              : the number of calls to this callback function. A bottom up heapify
     457              : significantly cuts down on comparisons by only comparing our element of interest
     458              : to other elements starting at the leaves of the tree. Because most elements in
     459              : a heap are leaves, or near leaves, the likelihood of finding a heap ordered
     460              : position near the bottom is extremely likely. This means we only require a few
     461              : comparisons on average. */
     462              : CCC_Result
     463           16 : CCC_sort_heapsort(
     464              :     CCC_Flat_buffer const *const buffer,
     465              :     void *const temp,
     466              :     CCC_Order order,
     467              :     CCC_Comparator const *const comparator
     468              : ) {
     469           16 :     if (!buffer || !temp || !comparator || !comparator->compare
     470           14 :         || (order != CCC_ORDER_GREATER && order != CCC_ORDER_LESSER)) {
     471            3 :         return CCC_RESULT_ARGUMENT_ERROR;
     472              :     }
     473              :     /* For sorting the user expects the buffer to be in the order they specify.
     474              :        Just like they would expect their input order to the priority queue to
     475              :        place the least or greatest element closest to the root. However,
     476              :        heap sort fills a buffer from back to front, so flip it. */
     477           13 :     order == CCC_ORDER_GREATER ? (order = CCC_ORDER_LESSER)
     478            8 :                                : (order = CCC_ORDER_GREATER);
     479           13 :     if (buffer->count > 1) {
     480           11 :         heapify(buffer, temp, order, comparator);
     481           11 :         size_t count = buffer->count;
     482           11 :         void *const root = at(buffer, 0);
     483          432 :         while (--count) {
     484          421 :             swap(temp, buffer->sizeof_type, root, at(buffer, count));
     485          421 :             bottom_up_reheap(buffer, count, 0, temp, order, comparator);
     486              :         }
     487           11 :     }
     488           13 :     return CCC_RESULT_OK;
     489           16 : }
     490              : 
     491              : /*===================     Private Interface     =============================*/
     492              : 
     493              : size_t
     494         3496 : CCC_private_flat_priority_queue_bubble_up(
     495              :     struct CCC_Flat_priority_queue const *const priority_queue,
     496              :     void *const temp,
     497              :     size_t index
     498              : ) {
     499         3496 :     return bubble_up(
     500         3496 :         &priority_queue->buffer,
     501         3496 :         index,
     502         3496 :         temp,
     503         3496 :         priority_queue->order,
     504         3496 :         &priority_queue->comparator
     505              :     );
     506              : }
     507              : 
     508              : void *
     509          677 : CCC_private_flat_priority_queue_update_fixup(
     510              :     struct CCC_Flat_priority_queue const *const priority_queue,
     511              :     void *const type,
     512              :     void *const temp
     513              : ) {
     514          677 :     return at(
     515          677 :         &priority_queue->buffer, update_fixup(priority_queue, type, temp)
     516              :     );
     517              : }
     518              : 
     519              : void
     520            2 : CCC_private_flat_priority_queue_heap_order(
     521              :     struct CCC_Flat_priority_queue const *const priority_queue, void *const temp
     522              : ) {
     523            2 :     heapify(
     524            2 :         &priority_queue->buffer,
     525            2 :         temp,
     526            2 :         priority_queue->order,
     527            2 :         &priority_queue->comparator
     528              :     );
     529            2 : }
     530              : 
     531              : /*====================     Static Helpers     ===============================*/
     532              : 
     533              : /* Orders the heap in O(N) time. Assumes n > 0 and n <= capacity. */
     534              : static inline void
     535           15 : heapify(
     536              :     CCC_Flat_buffer const *const buffer,
     537              :     void *temp,
     538              :     CCC_Order const order,
     539              :     CCC_Comparator const *const comparator
     540              : ) {
     541           15 :     size_t i = buffer->count / 2;
     542          384 :     while (i--) {
     543          369 :         bottom_up_reheap(buffer, buffer->count, i, temp, order, comparator);
     544              :     }
     545           15 : }
     546              : 
     547              : /** The Bottom-Up-Reheap procedures from the research paper but all in one
     548              : function. No need to break out into tiny functions because they are only used
     549              : here and this makes the logic easy to track in one short function. Such an
     550              : operation also replaces the traditional bubble-down of a standard heap. The
     551              : bubble-up operation can still be helpful in certain cases and is therefore kept
     552              : as a separate function.
     553              : 
     554              : This function also returns the final resting position of the root element for
     555              : this reheap operation. This is the location that the data previously at the root
     556              : index has been swapped to such that heap order is maintained. This is helpful if
     557              : the root element has been swapped to its special position on the special path
     558              : and we want to report that back for operations such as update, increase, and
     559              : decrease. */
     560              : static size_t
     561         3130 : bottom_up_reheap(
     562              :     CCC_Flat_buffer const *const buffer,
     563              :     size_t const count,
     564              :     size_t const root,
     565              :     void *const temp,
     566              :     CCC_Order const order,
     567              :     CCC_Comparator const *const comparator
     568              : ) {
     569         3130 :     size_t leaf = root;
     570              :     {
     571              :         /* Procedure leaf-search(count, root) */
     572         3130 :         size_t left = (2 * leaf) + 1;
     573        11341 :         while (left + 1 < count) {
     574         8211 :             size_t const right = left + 1;
     575         8211 :             if (wins(at(buffer, left), at(buffer, right), order, comparator)) {
     576         4009 :                 leaf = left;
     577         4009 :             } else {
     578         4202 :                 leaf = right;
     579              :             }
     580         8211 :             left = (2 * leaf) + 1;
     581         8211 :         }
     582         3130 :         if (left < count) {
     583          117 :             leaf = left;
     584          117 :         }
     585         3130 :     }
     586              :     {
     587              :         /* Procedure bottom-up-search(root, leaf). This is where we hope to
     588              :            save on comparison callbacks in the best case. In the heapsort case,
     589              :            we are constantly swapping the last element to the root and then
     590              :            performing this operation so it is extremely likely that the root
     591              :            element will find another home close to the leaf layer. In an
     592              :            arbitrary bottom up reheap operation the likelihood is still good due
     593              :            to 1/2 of all nodes being leaves. */
     594         3130 :         void const *const node = at(buffer, root);
     595         4036 :         while (leaf > root && wins(node, at(buffer, leaf), order, comparator)) {
     596          906 :             leaf = (leaf - 1) / 2;
     597              :         }
     598         3130 :     }
     599              :     {
     600              :         /* Procedure interchange-1(root, leaf). We can reduce the calls to
     601              :            memcpy by avoiding the traditional swap with our temp position. We
     602              :            can figure out the ancestry of the special path leaf position we have
     603              :            found using bitwise checks. This cuts the calls to memcpy from `3 *
     604              :            height` to `height + 2` which is significant for data sizes that can
     605              :            vary significantly in this type of generic container. */
     606         3130 :         (void)memcpy(temp, at(buffer, root), buffer->sizeof_type);
     607         6260 :         size_t tree_levels = count_leading_zeros_size_t(root + 1)
     608         3130 :                            - count_leading_zeros_size_t(leaf + 1);
     609        10552 :         while (tree_levels--) {
     610        14844 :             size_t const vacant_ancestor_index
     611         7422 :                 = ((leaf + 1) >> (tree_levels + 1)) - 1;
     612        14844 :             size_t const occupied_ancestor_child_index
     613         7422 :                 = ((leaf + 1) >> tree_levels) - 1;
     614         7422 :             memcpy(
     615         7422 :                 at(buffer, vacant_ancestor_index),
     616         7422 :                 at(buffer, occupied_ancestor_child_index),
     617         7422 :                 buffer->sizeof_type
     618              :             );
     619         7422 :         }
     620         3130 :         (void)memcpy(at(buffer, leaf), temp, buffer->sizeof_type);
     621         3130 :     }
     622         6260 :     return leaf;
     623         3130 : }
     624              : 
     625              : /** Returns the sorted position of the index element that may be out of heap
     626              : order. This element may move closer to the root index of 0. */
     627              : static inline size_t
     628         5170 : bubble_up(
     629              :     CCC_Flat_buffer const *const buffer,
     630              :     size_t index,
     631              :     void *const temp,
     632              :     CCC_Order const order,
     633              :     CCC_Comparator const *const comparator
     634              : ) {
     635         5170 :     if (!index) {
     636           27 :         return 0;
     637              :     }
     638         5143 :     size_t node = index;
     639              :     /* We can make the same optimization in terms of comparisons and calls
     640              :        to memcpy that we make in the bottom-up reheap function. This is better
     641              :        than calling swap making the calls to memcpy equivalent to the height of
     642              :        the path plus the two additional calls to save the element and write it
     643              :        to its new home in the tree. */
     644        10286 :     void const *const bubble
     645         5143 :         = memcpy(temp, at(buffer, index), buffer->sizeof_type);
     646        11041 :     while (node) {
     647        10957 :         size_t const parent_index = (node - 1) / 2;
     648        10957 :         void const *const parent_node = at(buffer, parent_index);
     649        10957 :         if (!wins(bubble, parent_node, order, comparator)) {
     650         5059 :             break;
     651              :         }
     652         5898 :         (void)memcpy(at(buffer, node), parent_node, buffer->sizeof_type);
     653         5898 :         node = parent_index;
     654        10957 :     }
     655         5143 :     if (node != index) {
     656         3112 :         (void)memcpy(at(buffer, node), bubble, buffer->sizeof_type);
     657         3112 :     }
     658         5143 :     return node;
     659         5170 : }
     660              : 
     661              : /* Fixes the position of element e after its key value has been changed. */
     662              : static size_t
     663         1375 : update_fixup(
     664              :     struct CCC_Flat_priority_queue const *const priority_queue,
     665              :     void *const type,
     666              :     void *const temp
     667              : ) {
     668         1375 :     size_t const index = index_of(priority_queue, type);
     669         1375 :     if (!index) {
     670            2 :         return bottom_up_reheap(
     671            2 :             &priority_queue->buffer,
     672            2 :             priority_queue->buffer.count,
     673              :             0,
     674            2 :             temp,
     675            2 :             priority_queue->order,
     676            2 :             &priority_queue->comparator
     677              :         );
     678              :     }
     679         2746 :     CCC_Order const parent_order
     680         5492 :         = priority_queue->comparator.compare((CCC_Comparator_arguments){
     681         1373 :             .type_left = at(&priority_queue->buffer, index),
     682         1373 :             .type_right = at(&priority_queue->buffer, (index - 1) / 2),
     683         1373 :             .context = priority_queue->comparator.context,
     684              :         });
     685         1373 :     if (parent_order == priority_queue->order) {
     686          307 :         return bubble_up(
     687          307 :             &priority_queue->buffer,
     688          307 :             index,
     689          307 :             temp,
     690          307 :             priority_queue->order,
     691          307 :             &priority_queue->comparator
     692              :         );
     693              :     }
     694         1066 :     if (parent_order != CCC_ORDER_EQUAL) {
     695         1060 :         return bottom_up_reheap(
     696         1060 :             &priority_queue->buffer,
     697         1060 :             priority_queue->buffer.count,
     698         1060 :             index,
     699         1060 :             temp,
     700         1060 :             priority_queue->order,
     701         1060 :             &priority_queue->comparator
     702              :         );
     703              :     }
     704            6 :     return index;
     705         1375 : }
     706              : 
     707              : /** Returns true if the winner, the first argument, wins the comparison.
     708              : Winning in a three-way comparison means satisfying the total order of the
     709              : priority queue. So, the comparison resulting in elements being equal means this
     710              : function returns false. If the winner is in the wrong order, thus losing the
     711              : total order comparison, the function also returns false. The function only
     712              : returns true when the three-way comparison of the winner to the loser results in
     713              : the winner matching the heap order specified upon container initialization. In
     714              : terms of the heap tree structure this means the winner should be closer to the
     715              : root. */
     716              : static inline CCC_Tribool
     717      1946744 : wins(
     718              :     void const *const winner,
     719              :     void const *const loser,
     720              :     CCC_Order const order,
     721              :     CCC_Comparator const *const comparator
     722              : ) {
     723      9733720 :     return comparator->compare((CCC_Comparator_arguments){
     724      1946744 :                .type_left = winner,
     725      1946744 :                .type_right = loser,
     726      1946744 :                .context = comparator->context,
     727              :            })
     728      1946744 :         == order;
     729              : }
     730              : 
     731              : /* Flat priority queue code that uses indices of the underlying Flat_buffer
     732              :    should always be within the Flat_buffer range. It should never exceed the
     733              :    current size and start at or after the Flat_buffer base. Only checked in
     734              :    debug. */
     735              : static inline size_t
     736         1904 : index_of(
     737              :     struct CCC_Flat_priority_queue const *const priority_queue,
     738              :     void const *const slot
     739              : ) {
     740         1904 :     assert(slot >= priority_queue->buffer.data);
     741         3808 :     size_t const i
     742         1904 :         = (size_t)((char *)slot - (char *)priority_queue->buffer.data)
     743         1904 :         / priority_queue->buffer.sizeof_type;
     744         1904 :     assert(i < priority_queue->buffer.count);
     745         3808 :     return i;
     746         1904 : }
     747              : 
     748              : /** Swaps data in a and b according to buffer element size. Assumes a, b, and
     749              : temp are non-null. */
     750              : static inline void
     751         1848 : swap(void *const temp, size_t const sizeof_type, void *const a, void *const b) {
     752         1848 :     assert(temp);
     753         1848 :     assert(a);
     754         1848 :     assert(b);
     755         1848 :     (void)memcpy(temp, a, sizeof_type);
     756         1848 :     (void)memcpy(a, b, sizeof_type);
     757         1848 :     (void)memcpy(b, temp, sizeof_type);
     758         1848 : }
     759              : 
     760              : /** Provides data at index. Assumes buffer is non-null and i is within
     761              : capacity. */
     762              : static inline void *
     763      2967850 : at(CCC_Flat_buffer const *const buffer, size_t const i) {
     764      2967850 :     assert(buffer);
     765      2967850 :     assert(i < buffer->capacity);
     766      2967850 :     return (char *)buffer->data + (i * buffer->sizeof_type);
     767              : }
     768              : 
     769              : static inline void
     770            2 : destroy_each(
     771              :     struct CCC_Flat_priority_queue *const priority_queue,
     772              :     CCC_Destructor const *const destructor
     773              : ) {
     774            2 :     size_t const count = priority_queue->buffer.count;
     775           34 :     for (size_t i = 0; i < count; ++i) {
     776           96 :         destructor->destroy((CCC_Arguments){
     777           32 :             .type = at(&priority_queue->buffer, i),
     778           32 :             .context = destructor->context,
     779              :         });
     780           32 :     }
     781            2 : }
     782              : 
     783              : #if defined(__has_builtin) && __has_builtin(__builtin_clzl)
     784              : 
     785              : static inline unsigned
     786         6260 : count_leading_zeros_size_t(size_t const n) {
     787              :     static_assert(
     788              :         sizeof(size_t) == sizeof(unsigned long),
     789              :         "Ensure the available builtin works for the platform defined "
     790              :         "size of a size_t."
     791              :     );
     792         6260 :     return n ? (unsigned)__builtin_clzl(n) : sizeof(size_t) * CHAR_BIT;
     793              : }
     794              : 
     795              : #else /* !defined(__has_builtin) || !__has_builtin(__builtin_clzl) */
     796              : 
     797              : static inline unsigned
     798              : count_leading_zeros_size_t(size_t n) {
     799              :     enum : size_t {
     800              :         /** @internal Most significant bit of size_t for bit counting. */
     801              :         SIZE_T_MSB = (size_t)1 << ((sizeof(size_t) * CHAR_BIT) - 1),
     802              :     };
     803              :     if (!n) {
     804              :         return sizeof(size_t) * CHAR_BIT;
     805              :     }
     806              :     unsigned cnt = 0;
     807              :     for (; !(n & SIZE_T_MSB); ++cnt, n <<= 1U) {}
     808              :     return cnt;
     809              : }
     810              : 
     811              : #endif /* defined(__has_builtin) && __has_builtin(__builtin_clzl) */
        

Generated by: LCOV version 2.4-beta