LCOV - code coverage report
Current view: top level - source/array_adaptive_map.c (source / functions) Coverage Total Hit
Test: CCC Test Suite Coverage Report Lines: 97.9 % 620 607
Test Date: 2026-05-12 15:05:06 Functions: 100.0 % 74 74

            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              : 
      15              : This file implements a splay tree that does not support duplicates.
      16              : The code to support a splay tree that does not allow duplicates is much simpler
      17              : than the code to support a multimap implementation. This implementation is
      18              : based on the following source.
      19              : 
      20              :     1. Daniel Sleator, Carnegie Mellon University. Sleator's implementation of a
      21              :        topdown splay tree was instrumental in starting things off, but required
      22              :        extensive modification. I had to update parent and child tracking, and
      23              :        unite the left and right cases for fun. See the code for a generalizable
      24              :        strategy to eliminate symmetric left and right cases for any binary tree
      25              :        code. https://www.link.cs.cmu.edu/splay/
      26              : 
      27              : Because this is a self-optimizing data structure it may benefit from many
      28              : constant time queries for frequently accessed elements. It is also in a Struct
      29              : of Arrays layout to improve memory alignment and reduce wasted space. While
      30              : it is recommended that the user reserve space for the needed nodes ahead of
      31              : time, the amortized O(log(N)) run times of a Splay Tree remain the same in
      32              : the dynamic resizing case. */
      33              : /** C23 provided headers. */
      34              : #include <stdalign.h>
      35              : #include <stddef.h>
      36              : 
      37              : /** CCC provided headers. */
      38              : #include "ccc/array_adaptive_map.h"
      39              : #include "ccc/configuration.h"
      40              : #include "ccc/private/private_array_adaptive_map.h"
      41              : #include "ccc/types.h"
      42              : 
      43              : /*========================   Data Alignment Test   ==========================*/
      44              : 
      45              : /** @internal A macro version of the runtime alignment operations we perform
      46              : for calculating bytes. This way we can use in static assert. The user data type
      47              : may not be the same alignment as the nodes and therefore the nodes array must
      48              : start at next aligned byte. */
      49              : #define roundup(bytes_to_round, alignment)                                     \
      50              :     (((bytes_to_round) + (alignment) - 1) & ~((alignment) - 1))
      51              : 
      52              : enum : size_t {
      53              :     /* @internal Test capacity. */
      54              :     TCAP = 3,
      55              : };
      56              : /** @internal This is a static fixed size map exclusive to this translation unit
      57              : used to ensure assumptions about data layout are correct. The following static
      58              : asserts must be true in order to support the Struct of Array style layout we
      59              : use for the data and nodes. It is important that in our user code when we set
      60              : the positions of the node pointer relative to the data pointer the positions are
      61              : correct regardless of backing storage as a fixed map or heap allocation.
      62              : 
      63              : Use an int because that will force the nodes array to be wary of
      64              : where to start. The nodes are 8 byte aligned but an int is 4. This means the
      65              : nodes need to start after a 4 byte buffer of padding at end of data array. */
      66              : static __auto_type const static_data_nodes_layout_test
      67              :     = CCC_array_adaptive_map_storage_for((int const[TCAP]){});
      68              : /** Some assumptions in the code assume that nodes array is last so ensure that
      69              : is the case here. Also good to assume user data comes first. */
      70              : static_assert(
      71              :     (char const *)static_data_nodes_layout_test.data
      72              :         < (char const *)static_data_nodes_layout_test.nodes,
      73              :     "The order of the arrays in a Struct of Arrays map is data, then "
      74              :     "nodes."
      75              : );
      76              : /** We don't care about the alignment or padding after the nodes array because
      77              : we never need to set or move any pointers to that position. The alignment is
      78              : important for the nodes pointer to be set to the correct aligned position and
      79              : so that we allocate enough bytes for our single allocation if the map is dynamic
      80              : and not a fixed type. */
      81              : static_assert(
      82              :     (char const *)&static_data_nodes_layout_test.nodes[TCAP]
      83              :             - (char const *)&static_data_nodes_layout_test.data[0]
      84              :         == roundup(
      85              :                (sizeof(*static_data_nodes_layout_test.data) * TCAP),
      86              :                alignof(*static_data_nodes_layout_test.nodes)
      87              :            ) + (sizeof(*static_data_nodes_layout_test.nodes) * TCAP),
      88              :     "The pointer difference in bytes between end of the nodes array and start "
      89              :     "of user data array must be the same as the total bytes we assume to be "
      90              :     "stored in that range. Alignment of user data must be considered."
      91              : );
      92              : static_assert(
      93              :     (char const *)&static_data_nodes_layout_test.data
      94              :             + roundup(
      95              :                 (sizeof(*static_data_nodes_layout_test.data) * TCAP),
      96              :                 alignof(*static_data_nodes_layout_test.nodes)
      97              :             )
      98              :         == (char const *)&static_data_nodes_layout_test.nodes,
      99              :     "The start of the nodes array must begin at the next aligned "
     100              :     "byte given alignment of a node."
     101              : );
     102              : 
     103              : /*==========================  Type Declarations   ===========================*/
     104              : 
     105              : /** @internal */
     106              : enum {
     107              :     LR = 2,
     108              : };
     109              : 
     110              : /** @internal */
     111              : enum Branch {
     112              :     L = 0,
     113              :     R,
     114              : };
     115              : 
     116              : #define INORDER R
     117              : #define INORDER_REVERSE L
     118              : 
     119              : enum {
     120              :     /** 0th slot is sentinel. Count will be 2 when inserting new root. */
     121              :     INSERT_ROOT_NODE_COUNT = 2,
     122              : };
     123              : 
     124              : /*==============================  Prototypes   ==============================*/
     125              : 
     126              : static size_t splay(struct CCC_Array_adaptive_map *, size_t, void const *);
     127              : static struct CCC_Array_adaptive_map_node *
     128              : node_at(struct CCC_Array_adaptive_map const *, size_t);
     129              : static void *data_at(struct CCC_Array_adaptive_map const *, size_t);
     130              : static struct CCC_Array_adaptive_map_handle
     131              : handle(struct CCC_Array_adaptive_map *, void const *);
     132              : static size_t erase(struct CCC_Array_adaptive_map *, void const *);
     133              : static size_t maybe_allocate_insert(
     134              :     struct CCC_Array_adaptive_map *, void const *, CCC_Allocator const *
     135              : );
     136              : static CCC_Result
     137              : resize(struct CCC_Array_adaptive_map *, size_t, CCC_Allocator const *);
     138              : static void
     139              : resize_struct_of_arrays(struct CCC_Array_adaptive_map const *, void *, size_t);
     140              : static size_t data_bytes(size_t, size_t);
     141              : static size_t nodes_bytes(size_t);
     142              : static struct CCC_Array_adaptive_map_node *
     143              : nodes_base_address(size_t, void const *, size_t);
     144              : static size_t find(struct CCC_Array_adaptive_map *, void const *);
     145              : static void
     146              : connect_new_root(struct CCC_Array_adaptive_map *, size_t, CCC_Order);
     147              : static void insert(struct CCC_Array_adaptive_map *, size_t n);
     148              : static void *key_in_slot(struct CCC_Array_adaptive_map const *, void const *);
     149              : static size_t
     150              : allocate_slot(struct CCC_Array_adaptive_map *, CCC_Allocator const *);
     151              : static size_t total_bytes(size_t, size_t);
     152              : static CCC_Handle_range equal_range(
     153              :     struct CCC_Array_adaptive_map *, void const *, void const *, enum Branch
     154              : );
     155              : static void *key_at(struct CCC_Array_adaptive_map const *, size_t);
     156              : static CCC_Order
     157              : order_nodes(struct CCC_Array_adaptive_map const *, void const *, size_t);
     158              : static size_t remove_from_tree(struct CCC_Array_adaptive_map *, size_t);
     159              : static size_t
     160              : min_max_from(struct CCC_Array_adaptive_map const *, size_t, enum Branch);
     161              : static size_t next(struct CCC_Array_adaptive_map const *, size_t, enum Branch);
     162              : static size_t
     163              : branch_index(struct CCC_Array_adaptive_map const *, size_t, enum Branch);
     164              : static size_t parent_index(struct CCC_Array_adaptive_map const *, size_t);
     165              : static size_t *
     166              : branch_pointer(struct CCC_Array_adaptive_map const *, size_t, enum Branch);
     167              : static size_t *parent_pointer(struct CCC_Array_adaptive_map const *, size_t);
     168              : static CCC_Tribool validate(struct CCC_Array_adaptive_map const *);
     169              : static void init_node(struct CCC_Array_adaptive_map const *, size_t);
     170              : static void swap(void *, void *, void *, size_t);
     171              : static void link(struct CCC_Array_adaptive_map *, size_t, enum Branch, size_t);
     172              : static size_t max(size_t, size_t);
     173              : static void
     174              : delete_nodes(struct CCC_Array_adaptive_map const *, CCC_Destructor const *);
     175              : 
     176              : /*==============================  Interface    ==============================*/
     177              : 
     178              : void *
     179        16768 : CCC_array_adaptive_map_at(
     180              :     CCC_Array_adaptive_map const *const map, CCC_Handle_index const index
     181              : ) {
     182        16768 :     if (!map || !index) {
     183           13 :         return NULL;
     184              :     }
     185        16755 :     return data_at(map, index);
     186        16768 : }
     187              : 
     188              : CCC_Tribool
     189           66 : CCC_array_adaptive_map_contains(
     190              :     CCC_Array_adaptive_map *const map, void const *const key
     191              : ) {
     192           66 :     if (!map || !key) {
     193            2 :         return CCC_TRIBOOL_ERROR;
     194              :     }
     195           64 :     map->root = splay(map, map->root, key);
     196           64 :     return order_nodes(map, key, map->root) == CCC_ORDER_EQUAL;
     197           66 : }
     198              : 
     199              : CCC_Handle_index
     200         2017 : CCC_array_adaptive_map_get_key_value(
     201              :     CCC_Array_adaptive_map *const map, void const *const key
     202              : ) {
     203         2017 :     if (!map || !key) {
     204            2 :         return 0;
     205              :     }
     206         2015 :     return find(map, key);
     207         2017 : }
     208              : 
     209              : CCC_Array_adaptive_map_handle
     210        13044 : CCC_array_adaptive_map_handle(
     211              :     CCC_Array_adaptive_map *const map, void const *const key
     212              : ) {
     213        13044 :     if (!map || !key) {
     214            2 :         return (CCC_Array_adaptive_map_handle){
     215              :             .status = CCC_ENTRY_ARGUMENT_ERROR,
     216              :         };
     217              :     }
     218        13042 :     return handle(map, key);
     219        13044 : }
     220              : 
     221              : CCC_Handle_index
     222         8381 : CCC_array_adaptive_map_insert_handle(
     223              :     CCC_Array_adaptive_map_handle const *const handle,
     224              :     void const *const key_val_type,
     225              :     CCC_Allocator const *const allocator
     226              : ) {
     227         8381 :     if (!handle || !key_val_type || !allocator) {
     228            3 :         return 0;
     229              :     }
     230         8378 :     if (handle->status == CCC_ENTRY_OCCUPIED) {
     231         3105 :         void *const ret = data_at(handle->map, handle->index);
     232         3105 :         if (key_val_type != ret) {
     233         3105 :             (void)memcpy(ret, key_val_type, handle->map->sizeof_type);
     234         3105 :         }
     235         3105 :         return handle->index;
     236         3105 :     }
     237         5273 :     return maybe_allocate_insert(handle->map, key_val_type, allocator);
     238         8381 : }
     239              : 
     240              : CCC_Array_adaptive_map_handle *
     241          112 : CCC_array_adaptive_map_and_modify(
     242              :     CCC_Array_adaptive_map_handle *const handle,
     243              :     CCC_Modifier const *const modifier
     244              : ) {
     245          112 :     if (!handle || !modifier) {
     246            2 :         return NULL;
     247              :     }
     248          110 :     if (modifier->modify && handle->status & CCC_ENTRY_OCCUPIED) {
     249          168 :         modifier->modify((CCC_Arguments){
     250           56 :             .type = data_at(handle->map, handle->index),
     251           56 :             .context = modifier->context,
     252              :         });
     253           56 :     }
     254          110 :     return handle;
     255          112 : }
     256              : 
     257              : CCC_Handle_index
     258          262 : CCC_array_adaptive_map_or_insert(
     259              :     CCC_Array_adaptive_map_handle const *const handle,
     260              :     void const *const key_val_type,
     261              :     CCC_Allocator const *const allocator
     262              : ) {
     263          262 :     if (!handle || !key_val_type || !allocator) {
     264            3 :         return 0;
     265              :     }
     266          259 :     if (handle->status & CCC_ENTRY_OCCUPIED) {
     267          153 :         return handle->index;
     268              :     }
     269          106 :     return maybe_allocate_insert(handle->map, key_val_type, allocator);
     270          262 : }
     271              : 
     272              : CCC_Handle
     273         1565 : CCC_array_adaptive_map_swap_handle(
     274              :     CCC_Array_adaptive_map *const map,
     275              :     void *const type_output,
     276              :     CCC_Allocator const *const allocator
     277              : ) {
     278         1565 :     if (!map || !type_output || !allocator) {
     279            3 :         return (CCC_Handle){.status = CCC_ENTRY_ARGUMENT_ERROR};
     280              :     }
     281         1562 :     size_t const found = find(map, key_in_slot(map, type_output));
     282         1562 :     if (found) {
     283          107 :         assert(map->root);
     284          107 :         void *const ret = data_at(map, map->root);
     285          107 :         void *const temp = data_at(map, 0);
     286          107 :         swap(temp, type_output, ret, map->sizeof_type);
     287          214 :         return (CCC_Handle){
     288          107 :             .index = found,
     289              :             .status = CCC_ENTRY_OCCUPIED,
     290              :         };
     291          107 :     }
     292         1455 :     size_t const inserted = maybe_allocate_insert(map, type_output, allocator);
     293         1455 :     if (!inserted) {
     294            1 :         return (CCC_Handle){
     295              :             .index = 0,
     296              :             .status = CCC_ENTRY_INSERT_ERROR,
     297              :         };
     298              :     }
     299         2908 :     return (CCC_Handle){
     300         1454 :         .index = inserted,
     301              :         .status = CCC_ENTRY_VACANT,
     302              :     };
     303         1565 : }
     304              : 
     305              : CCC_Handle
     306         1224 : CCC_array_adaptive_map_try_insert(
     307              :     CCC_Array_adaptive_map *const map,
     308              :     void const *const key_val_type,
     309              :     CCC_Allocator const *const allocator
     310              : ) {
     311         1224 :     if (!map || !key_val_type || !allocator) {
     312            3 :         return (CCC_Handle){.status = CCC_ENTRY_ARGUMENT_ERROR};
     313              :     }
     314         1221 :     size_t const found = find(map, key_in_slot(map, key_val_type));
     315         1221 :     if (found) {
     316          415 :         assert(map->root);
     317          830 :         return (CCC_Handle){
     318          415 :             .index = found,
     319              :             .status = CCC_ENTRY_OCCUPIED,
     320              :         };
     321              :     }
     322          806 :     size_t const inserted = maybe_allocate_insert(map, key_val_type, allocator);
     323          806 :     if (!inserted) {
     324            1 :         return (CCC_Handle){
     325              :             .index = 0,
     326              :             .status = CCC_ENTRY_INSERT_ERROR,
     327              :         };
     328              :     }
     329         1610 :     return (CCC_Handle){
     330          805 :         .index = inserted,
     331              :         .status = CCC_ENTRY_VACANT,
     332              :     };
     333         1224 : }
     334              : 
     335              : CCC_Handle
     336         3027 : CCC_array_adaptive_map_insert_or_assign(
     337              :     CCC_Array_adaptive_map *const map,
     338              :     void const *const key_val_type,
     339              :     CCC_Allocator const *const allocator
     340              : ) {
     341         3027 :     if (!map || !key_val_type || !allocator) {
     342            3 :         return (CCC_Handle){.status = CCC_ENTRY_ARGUMENT_ERROR};
     343              :     }
     344         3024 :     size_t const found = find(map, key_in_slot(map, key_val_type));
     345         3024 :     if (found) {
     346          363 :         assert(map->root);
     347          363 :         void *const f_base = data_at(map, found);
     348          363 :         if (key_val_type != f_base) {
     349          363 :             memcpy(f_base, key_val_type, map->sizeof_type);
     350          363 :         }
     351          726 :         return (CCC_Handle){
     352          363 :             .index = found,
     353              :             .status = CCC_ENTRY_OCCUPIED,
     354              :         };
     355          363 :     }
     356         2661 :     size_t const inserted = maybe_allocate_insert(map, key_val_type, allocator);
     357         2661 :     if (!inserted) {
     358            3 :         return (CCC_Handle){
     359              :             .index = 0,
     360              :             .status = CCC_ENTRY_INSERT_ERROR,
     361              :         };
     362              :     }
     363         5316 :     return (CCC_Handle){
     364         2658 :         .index = inserted,
     365              :         .status = CCC_ENTRY_VACANT,
     366              :     };
     367         3027 : }
     368              : 
     369              : CCC_Handle
     370         2291 : CCC_array_adaptive_map_remove_key_value(
     371              :     CCC_Array_adaptive_map *const map, void *const type_output
     372              : ) {
     373         2291 :     if (!map || !type_output) {
     374            2 :         return (CCC_Handle){.status = CCC_ENTRY_ARGUMENT_ERROR};
     375              :     }
     376         2289 :     size_t const removed = erase(map, key_in_slot(map, type_output));
     377         2289 :     if (!removed) {
     378            3 :         return (CCC_Handle){
     379              :             .index = 0,
     380              :             .status = CCC_ENTRY_VACANT,
     381              :         };
     382              :     }
     383         2286 :     assert(removed);
     384         2286 :     void const *const r = data_at(map, removed);
     385         2286 :     if (type_output != r) {
     386         2286 :         (void)memcpy(type_output, r, map->sizeof_type);
     387         2286 :     }
     388         2286 :     return (CCC_Handle){
     389              :         .index = 0,
     390              :         .status = CCC_ENTRY_OCCUPIED,
     391              :     };
     392         2291 : }
     393              : 
     394              : CCC_Handle
     395           55 : CCC_array_adaptive_map_remove_handle(
     396              :     CCC_Array_adaptive_map_handle *const handle
     397              : ) {
     398           55 :     if (!handle) {
     399            1 :         return (CCC_Handle){.status = CCC_ENTRY_ARGUMENT_ERROR};
     400              :     }
     401           54 :     if (handle->status == CCC_ENTRY_OCCUPIED) {
     402           88 :         size_t const erased
     403           44 :             = erase(handle->map, key_at(handle->map, handle->index));
     404           44 :         assert(erased);
     405           88 :         return (CCC_Handle){
     406           44 :             .index = erased,
     407              :             .status = CCC_ENTRY_OCCUPIED,
     408              :         };
     409           44 :     }
     410           10 :     return (CCC_Handle){
     411              :         .index = 0,
     412              :         .status = CCC_ENTRY_VACANT,
     413              :     };
     414           55 : }
     415              : 
     416              : CCC_Handle_index
     417           16 : CCC_array_adaptive_map_unwrap(
     418              :     CCC_Array_adaptive_map_handle const *const handle
     419              : ) {
     420           16 :     if (!handle) {
     421            1 :         return 0;
     422              :     }
     423           15 :     return handle->status == CCC_ENTRY_OCCUPIED ? handle->index : 0;
     424           16 : }
     425              : 
     426              : CCC_Tribool
     427            3 : CCC_array_adaptive_map_insert_error(
     428              :     CCC_Array_adaptive_map_handle const *const handle
     429              : ) {
     430            3 :     if (!handle) {
     431            2 :         return CCC_TRIBOOL_ERROR;
     432              :     }
     433            1 :     return (handle->status & CCC_ENTRY_INSERT_ERROR) != 0;
     434            3 : }
     435              : 
     436              : CCC_Tribool
     437           84 : CCC_array_adaptive_map_occupied(
     438              :     CCC_Array_adaptive_map_handle const *const handle
     439              : ) {
     440           84 :     if (!handle) {
     441            1 :         return CCC_TRIBOOL_ERROR;
     442              :     }
     443           83 :     return (handle->status & CCC_ENTRY_OCCUPIED) != 0;
     444           84 : }
     445              : 
     446              : CCC_Handle_status
     447            2 : CCC_array_adaptive_map_handle_status(
     448              :     CCC_Array_adaptive_map_handle const *const handle
     449              : ) {
     450            2 :     return handle ? handle->status : CCC_ENTRY_ARGUMENT_ERROR;
     451              : }
     452              : 
     453              : CCC_Tribool
     454         2362 : CCC_array_adaptive_map_is_empty(CCC_Array_adaptive_map const *const map) {
     455         2362 :     if (!map) {
     456            1 :         return CCC_TRIBOOL_ERROR;
     457              :     }
     458         2361 :     return !CCC_array_adaptive_map_count(map).count;
     459         2362 : }
     460              : 
     461              : CCC_Count
     462         2515 : CCC_array_adaptive_map_count(CCC_Array_adaptive_map const *const map) {
     463         2515 :     if (!map) {
     464            1 :         return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
     465              :     }
     466         5028 :     return (CCC_Count){
     467         2514 :         .count = map->count ? map->count - 1 : 0,
     468              :     };
     469         2515 : }
     470              : 
     471              : CCC_Count
     472           10 : CCC_array_adaptive_map_capacity(CCC_Array_adaptive_map const *const map) {
     473           10 :     if (!map) {
     474            1 :         return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
     475              :     }
     476            9 :     return (CCC_Count){.count = map->capacity};
     477           10 : }
     478              : 
     479              : CCC_Handle_index
     480           16 : CCC_array_adaptive_map_begin(CCC_Array_adaptive_map const *const map) {
     481           16 :     if (!map || !map->capacity) {
     482            3 :         return 0;
     483              :     }
     484           13 :     size_t const n = min_max_from(map, map->root, L);
     485           13 :     return n;
     486           16 : }
     487              : 
     488              : CCC_Handle_index
     489            3 : CCC_array_adaptive_map_reverse_begin(CCC_Array_adaptive_map const *const map) {
     490            3 :     if (!map || !map->capacity) {
     491            1 :         return 0;
     492              :     }
     493            2 :     size_t const n = min_max_from(map, map->root, R);
     494            2 :     return n;
     495            3 : }
     496              : 
     497              : CCC_Handle_index
     498         3004 : CCC_array_adaptive_map_next(
     499              :     CCC_Array_adaptive_map const *const map, CCC_Handle_index const iterator
     500              : ) {
     501         3004 :     if (!map || !map->capacity) {
     502            1 :         return 0;
     503              :     }
     504         3003 :     size_t const n = next(map, iterator, INORDER);
     505         3003 :     return n;
     506         3004 : }
     507              : 
     508              : CCC_Handle_index
     509         1293 : CCC_array_adaptive_map_reverse_next(
     510              :     CCC_Array_adaptive_map const *const map, CCC_Handle_index const iterator
     511              : ) {
     512         1293 :     if (!map || !iterator || !map->capacity) {
     513            1 :         return 0;
     514              :     }
     515         1292 :     size_t const n = next(map, iterator, INORDER_REVERSE);
     516         1292 :     return n;
     517         1293 : }
     518              : 
     519              : CCC_Handle_index
     520         4274 : CCC_array_adaptive_map_end(CCC_Array_adaptive_map const *const) {
     521         4274 :     return 0;
     522              : }
     523              : 
     524              : CCC_Handle_index
     525            4 : CCC_array_adaptive_map_reverse_end(CCC_Array_adaptive_map const *const) {
     526            4 :     return 0;
     527              : }
     528              : 
     529              : CCC_Handle_range
     530            8 : CCC_array_adaptive_map_equal_range(
     531              :     CCC_Array_adaptive_map *const map,
     532              :     void const *const begin_key,
     533              :     void const *const end_key
     534              : ) {
     535            8 :     if (!map || !begin_key || !end_key) {
     536            3 :         return (CCC_Handle_range){};
     537              :     }
     538            5 :     return equal_range(map, begin_key, end_key, INORDER);
     539            8 : }
     540              : 
     541              : CCC_Handle_range_reverse
     542            8 : CCC_array_adaptive_map_equal_range_reverse(
     543              :     CCC_Array_adaptive_map *const map,
     544              :     void const *const reverse_begin_key,
     545              :     void const *const reverse_end_key
     546              : )
     547              : 
     548              : {
     549            8 :     if (!map || !reverse_begin_key || !reverse_end_key) {
     550            3 :         return (CCC_Handle_range_reverse){};
     551              :     }
     552            5 :     CCC_Handle_range const range
     553            5 :         = equal_range(map, reverse_begin_key, reverse_end_key, INORDER_REVERSE);
     554           15 :     return (CCC_Handle_range_reverse){
     555            5 :         .reverse_begin = range.begin,
     556            5 :         .reverse_end = range.end,
     557              :     };
     558            8 : }
     559              : 
     560              : CCC_Result
     561           15 : CCC_array_adaptive_map_reserve(
     562              :     CCC_Array_adaptive_map *const map,
     563              :     size_t const to_add,
     564              :     CCC_Allocator const *const allocator
     565              : ) {
     566           15 :     if (!map || !to_add || !allocator || !allocator->allocate) {
     567            3 :         return CCC_RESULT_ARGUMENT_ERROR;
     568              :     }
     569           12 :     size_t const needed = map->count + to_add + (map->count == 0);
     570           12 :     if (needed <= map->capacity) {
     571            1 :         return CCC_RESULT_OK;
     572              :     }
     573           11 :     size_t const old_count = map->count;
     574           11 :     size_t old_cap = map->capacity;
     575           11 :     CCC_Result const r = resize(map, needed, allocator);
     576           11 :     if (r != CCC_RESULT_OK) {
     577            1 :         return r;
     578              :     }
     579           10 :     if (!old_cap) {
     580           10 :         map->count = 1;
     581           10 :     }
     582           10 :     old_cap = old_count ? old_cap : 0;
     583           10 :     size_t const new_cap = map->capacity;
     584           10 :     size_t prev = 0;
     585           10 :     size_t i = new_cap;
     586         1445 :     while (i--) {
     587         1445 :         if (i <= old_cap) {
     588           10 :             break;
     589              :         }
     590         1435 :         node_at(map, i)->next_free = prev;
     591         1435 :         prev = i;
     592              :     }
     593           10 :     if (!map->free_list) {
     594           10 :         map->free_list = prev;
     595           10 :     }
     596           10 :     return CCC_RESULT_OK;
     597           15 : }
     598              : 
     599              : CCC_Result
     600            7 : CCC_array_adaptive_map_copy(
     601              :     CCC_Array_adaptive_map *const destination,
     602              :     CCC_Array_adaptive_map const *const source,
     603              :     CCC_Allocator const *const allocator
     604              : ) {
     605            7 :     if (!destination || !source || !allocator || source == destination
     606            7 :         || (destination->capacity < source->capacity && !allocator->allocate)) {
     607            2 :         return CCC_RESULT_ARGUMENT_ERROR;
     608              :     }
     609            5 :     if (!source->capacity) {
     610            1 :         return CCC_RESULT_OK;
     611              :     }
     612            4 :     if (destination->capacity < source->capacity) {
     613            3 :         CCC_Result const r = resize(destination, source->capacity, allocator);
     614            3 :         if (r != CCC_RESULT_OK) {
     615            1 :             return r;
     616              :         }
     617            3 :     } else {
     618              :         /* Might not be necessary but not worth finding out. Do every time. */
     619            1 :         destination->nodes = nodes_base_address(
     620            1 :             destination->sizeof_type, destination->data, destination->capacity
     621              :         );
     622              :     }
     623            3 :     if (!destination->data || !source->data) {
     624            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     625              :     }
     626            2 :     resize_struct_of_arrays(source, destination->data, destination->capacity);
     627            2 :     destination->free_list = source->free_list;
     628            2 :     destination->root = source->root;
     629            2 :     destination->count = source->count;
     630            2 :     destination->comparator = source->comparator;
     631            2 :     destination->sizeof_type = source->sizeof_type;
     632            2 :     destination->key_offset = source->key_offset;
     633            2 :     return CCC_RESULT_OK;
     634            7 : }
     635              : 
     636              : CCC_Result
     637            2 : CCC_array_adaptive_map_clear(
     638              :     CCC_Array_adaptive_map *const map, CCC_Destructor const *const destructor
     639              : ) {
     640            2 :     if (!map || !destructor) {
     641            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     642              :     }
     643            1 :     if (destructor->destroy) {
     644            1 :         delete_nodes(map, destructor);
     645            1 :     }
     646            1 :     map->count = 1;
     647            1 :     map->root = 0;
     648            1 :     return CCC_RESULT_OK;
     649            2 : }
     650              : 
     651              : CCC_Result
     652           18 : CCC_array_adaptive_map_clear_and_free(
     653              :     CCC_Array_adaptive_map *const map,
     654              :     CCC_Destructor const *const destructor,
     655              :     CCC_Allocator const *const allocator
     656              : ) {
     657           18 :     if (!map || !destructor || !allocator || !allocator->allocate) {
     658            4 :         return CCC_RESULT_ARGUMENT_ERROR;
     659              :     }
     660           14 :     if (destructor->destroy) {
     661            1 :         delete_nodes(map, destructor);
     662            1 :     }
     663           14 :     map->root = 0;
     664           14 :     map->count = 0;
     665           14 :     map->capacity = 0;
     666           42 :     (void)allocator->allocate((CCC_Allocator_arguments){
     667           14 :         .input = map->data,
     668              :         .bytes = 0,
     669           14 :         .context = allocator->context,
     670              :     });
     671           14 :     map->data = NULL;
     672           14 :     map->nodes = NULL;
     673           14 :     return CCC_RESULT_OK;
     674           18 : }
     675              : 
     676              : CCC_Tribool
     677         9875 : CCC_array_adaptive_map_validate(CCC_Array_adaptive_map const *const map) {
     678         9875 :     if (!map) {
     679            1 :         return CCC_TRIBOOL_ERROR;
     680              :     }
     681         9874 :     return validate(map);
     682         9875 : }
     683              : 
     684              : /*===========================   Private Interface ===========================*/
     685              : 
     686              : void
     687          144 : CCC_private_array_adaptive_map_insert(
     688              :     struct CCC_Array_adaptive_map *const map, size_t const elem_i
     689              : ) {
     690          144 :     insert(map, elem_i);
     691          144 : }
     692              : 
     693              : struct CCC_Array_adaptive_map_handle
     694           48 : CCC_private_array_adaptive_map_handle(
     695              :     struct CCC_Array_adaptive_map *const map, void const *const key
     696              : ) {
     697           48 :     return handle(map, key);
     698           48 : }
     699              : 
     700              : void *
     701           36 : CCC_private_array_adaptive_map_key_at(
     702              :     struct CCC_Array_adaptive_map const *const map, size_t const slot
     703              : ) {
     704           36 :     return key_at(map, slot);
     705              : }
     706              : 
     707              : void *
     708         2207 : CCC_private_array_adaptive_map_data_at(
     709              :     struct CCC_Array_adaptive_map const *const map, size_t const slot
     710              : ) {
     711         2207 :     return data_at(map, slot);
     712              : }
     713              : 
     714              : size_t
     715          146 : CCC_private_array_adaptive_map_allocate_slot(
     716              :     struct CCC_Array_adaptive_map *const map,
     717              :     CCC_Allocator const *const allocator
     718              : ) {
     719          146 :     return allocate_slot(map, allocator);
     720              : }
     721              : 
     722              : /*===========================   Static Helpers    ===========================*/
     723              : 
     724              : static CCC_Handle_range
     725           10 : equal_range(
     726              :     struct CCC_Array_adaptive_map *const t,
     727              :     void const *const begin_key,
     728              :     void const *const end_key,
     729              :     enum Branch const traversal
     730              : ) {
     731           10 :     if (CCC_array_adaptive_map_is_empty(t)) {
     732            2 :         return (CCC_Handle_range){};
     733              :     }
     734              :     /* As with most BST code the cases are perfectly symmetrical. If we
     735              :        are seeking an increasing or decreasing range we need to make sure
     736              :        we follow the [inclusive, exclusive) range rule. This means double
     737              :        checking we don't need to progress to the next greatest or next
     738              :        lesser element depending on the direction we are traversing. */
     739            8 :     CCC_Order const les_or_grt[2] = {CCC_ORDER_LESSER, CCC_ORDER_GREATER};
     740            8 :     size_t b = splay(t, t->root, begin_key);
     741            8 :     if (order_nodes(t, begin_key, b) == les_or_grt[traversal]) {
     742            2 :         b = next(t, b, traversal);
     743            2 :     }
     744            8 :     size_t e = splay(t, t->root, end_key);
     745            8 :     if (order_nodes(t, end_key, e) != les_or_grt[!traversal]) {
     746            5 :         e = next(t, e, traversal);
     747            5 :     }
     748           24 :     return (CCC_Handle_range){
     749            8 :         .begin = b,
     750            8 :         .end = e,
     751              :     };
     752           10 : }
     753              : 
     754              : static struct CCC_Array_adaptive_map_handle
     755        13090 : handle(struct CCC_Array_adaptive_map *const map, void const *const key) {
     756        13090 :     size_t const found = find(map, key);
     757        13090 :     if (found) {
     758        22542 :         return (struct CCC_Array_adaptive_map_handle){
     759         7514 :             .map = map,
     760         7514 :             .index = found,
     761              :             .status = CCC_ENTRY_OCCUPIED,
     762              :         };
     763              :     }
     764        11152 :     return (struct CCC_Array_adaptive_map_handle){
     765         5576 :         .map = map,
     766              :         .index = 0,
     767              :         .status = CCC_ENTRY_VACANT,
     768              :     };
     769        13090 : }
     770              : 
     771              : static size_t
     772        10301 : maybe_allocate_insert(
     773              :     struct CCC_Array_adaptive_map *const map,
     774              :     void const *const user_type,
     775              :     CCC_Allocator const *const allocator
     776              : ) {
     777        10301 :     size_t const node = allocate_slot(map, allocator);
     778        10301 :     if (!node) {
     779            8 :         return 0;
     780              :     }
     781        10293 :     (void)memcpy(data_at(map, node), user_type, map->sizeof_type);
     782        10293 :     insert(map, node);
     783        10293 :     return node;
     784        10301 : }
     785              : 
     786              : static size_t
     787        10447 : allocate_slot(
     788              :     struct CCC_Array_adaptive_map *const map,
     789              :     CCC_Allocator const *const allocator
     790              : ) {
     791              :     /* The end sentinel node will always be at 0. This also means once
     792              :        initialized the internal size for implementer is always at least 1. */
     793        10447 :     size_t const old_count = map->count;
     794        10447 :     size_t old_cap = map->capacity;
     795        10447 :     if (!old_count || old_count == old_cap) {
     796           93 :         assert(!map->free_list);
     797           93 :         if (old_count == old_cap) {
     798           49 :             if (resize(map, max(old_cap * 2, 8), allocator) != CCC_RESULT_OK) {
     799           10 :                 return 0;
     800              :             }
     801           39 :         } else {
     802           44 :             map->nodes = nodes_base_address(
     803           44 :                 map->sizeof_type, map->data, map->capacity
     804              :             );
     805              :         }
     806           83 :         old_cap = old_count ? old_cap : 1;
     807           83 :         size_t const new_cap = map->capacity;
     808           83 :         size_t prev = 0;
     809        16916 :         for (size_t i = new_cap - 1; i >= old_cap; prev = i, --i) {
     810        16833 :             node_at(map, i)->next_free = prev;
     811        16833 :         }
     812           83 :         map->free_list = prev;
     813           83 :         map->count = max(old_count, 1);
     814           83 :     }
     815        10437 :     assert(map->free_list);
     816        10437 :     ++map->count;
     817        10437 :     size_t const slot = map->free_list;
     818        10437 :     map->free_list = node_at(map, slot)->next_free;
     819        10437 :     return slot;
     820        10447 : }
     821              : 
     822              : static CCC_Result
     823           63 : resize(
     824              :     struct CCC_Array_adaptive_map *const map,
     825              :     size_t const new_capacity,
     826              :     CCC_Allocator const *const allocator
     827              : ) {
     828           63 :     if (!allocator->allocate) {
     829            9 :         return CCC_RESULT_NO_ALLOCATION_FUNCTION;
     830              :     }
     831          162 :     void *const new_data = allocator->allocate((CCC_Allocator_arguments){
     832              :         .input = NULL,
     833           54 :         .bytes = total_bytes(map->sizeof_type, new_capacity),
     834           54 :         .context = allocator->context,
     835              :     });
     836           54 :     if (!new_data) {
     837            3 :         return CCC_RESULT_ALLOCATOR_ERROR;
     838              :     }
     839           51 :     resize_struct_of_arrays(map, new_data, new_capacity);
     840           51 :     map->nodes = nodes_base_address(map->sizeof_type, new_data, new_capacity);
     841          153 :     allocator->allocate((CCC_Allocator_arguments){
     842           51 :         .input = map->data,
     843              :         .bytes = 0,
     844           51 :         .context = allocator->context,
     845              :     });
     846           51 :     map->data = new_data;
     847           51 :     map->capacity = new_capacity;
     848           51 :     return CCC_RESULT_OK;
     849           63 : }
     850              : 
     851              : static void
     852        10437 : insert(struct CCC_Array_adaptive_map *const map, size_t const n) {
     853        10437 :     init_node(map, n);
     854        10437 :     if (map->count == INSERT_ROOT_NODE_COUNT) {
     855           59 :         map->root = n;
     856           59 :         return;
     857              :     }
     858        10378 :     void const *const key = key_at(map, n);
     859        10378 :     map->root = splay(map, map->root, key);
     860        10378 :     CCC_Order const root_order = order_nodes(map, key, map->root);
     861        10378 :     if (CCC_ORDER_EQUAL == root_order) {
     862            0 :         return;
     863              :     }
     864        10378 :     connect_new_root(map, n, root_order);
     865        20815 : }
     866              : 
     867              : static void
     868        10378 : connect_new_root(
     869              :     struct CCC_Array_adaptive_map *const map,
     870              :     size_t const new_root,
     871              :     CCC_Order const order_result
     872              : ) {
     873        10378 :     enum Branch const dir = CCC_ORDER_GREATER == order_result;
     874        10378 :     link(map, new_root, dir, branch_index(map, map->root, dir));
     875        10378 :     link(map, new_root, !dir, map->root);
     876        10378 :     *branch_pointer(map, map->root, dir) = 0;
     877        10378 :     map->root = new_root;
     878        10378 :     *parent_pointer(map, map->root) = 0;
     879        10378 : }
     880              : 
     881              : static size_t
     882         2333 : erase(struct CCC_Array_adaptive_map *const map, void const *const key) {
     883         2333 :     if (CCC_array_adaptive_map_is_empty(map)) {
     884            1 :         return 0;
     885              :     }
     886         2332 :     size_t const ret = splay(map, map->root, key);
     887         2332 :     CCC_Order const found = order_nodes(map, key, ret);
     888         2332 :     if (found != CCC_ORDER_EQUAL) {
     889            2 :         return 0;
     890              :     }
     891         2330 :     return remove_from_tree(map, ret);
     892         2333 : }
     893              : 
     894              : static size_t
     895         2330 : remove_from_tree(struct CCC_Array_adaptive_map *const map, size_t const ret) {
     896         2330 :     if (!branch_index(map, ret, L)) {
     897          375 :         map->root = branch_index(map, ret, R);
     898          375 :         *parent_pointer(map, map->root) = 0;
     899          375 :     } else {
     900         1955 :         map->root = splay(map, branch_index(map, ret, L), key_at(map, ret));
     901         1955 :         link(map, map->root, R, branch_index(map, ret, R));
     902              :     }
     903         2330 :     node_at(map, ret)->next_free = map->free_list;
     904         2330 :     map->free_list = ret;
     905         2330 :     --map->count;
     906         2330 :     return ret;
     907              : }
     908              : 
     909              : static size_t
     910        20912 : find(struct CCC_Array_adaptive_map *const map, void const *const key) {
     911        20912 :     if (!map->root) {
     912           76 :         return 0;
     913              :     }
     914        20836 :     map->root = splay(map, map->root, key);
     915        20836 :     return order_nodes(map, key, map->root) == CCC_ORDER_EQUAL ? map->root : 0;
     916        20912 : }
     917              : 
     918              : /** Adopts D. Sleator technique for splaying. Notable to this method is the
     919              : general improvement to the tree that occurs because we always splay the key
     920              : to the root, OR the next closest value to the key to the root. This has
     921              : interesting performance implications for real data sets.
     922              : 
     923              : This implementation has been modified to unite the left and right symmetries
     924              : and manage the parent pointers. Parent pointers are not usual for splay trees
     925              : but are necessary for a clean iteration API. */
     926              : static size_t
     927        35581 : splay(
     928              :     struct CCC_Array_adaptive_map *const map, size_t root, void const *const key
     929              : ) {
     930        35581 :     assert(root);
     931              :     /* Splaying brings the key element up to the root. The zigzag fixes of
     932              :        splaying repair the tree and we remember the roots of these changes in
     933              :        this helper tree. At the end, make the root pick up these modified left
     934              :        and right helpers. The nil node should NULL initialized to start. */
     935        35581 :     struct CCC_Array_adaptive_map_node *const nil = node_at(map, 0);
     936        35581 :     nil->branch[L] = nil->branch[R] = nil->parent = 0;
     937        35581 :     size_t left_right_subtrees[LR] = {0, 0};
     938       157703 :     for (;;) {
     939       157703 :         CCC_Order const root_order = order_nodes(map, key, root);
     940       157703 :         enum Branch const order_link = CCC_ORDER_GREATER == root_order;
     941       157703 :         size_t const child = branch_index(map, root, order_link);
     942       157703 :         if (CCC_ORDER_EQUAL == root_order || !child) {
     943        26912 :             break;
     944              :         }
     945       261582 :         CCC_Order const child_order
     946       130791 :             = order_nodes(map, key, branch_index(map, root, order_link));
     947       130791 :         enum Branch const child_order_link = CCC_ORDER_GREATER == child_order;
     948              :         /* A straight line has formed from root->child->grandchild. An
     949              :            opportunity to splay and heal the tree arises. */
     950       130791 :         if (CCC_ORDER_EQUAL != child_order && order_link == child_order_link) {
     951        83358 :             link(map, root, order_link, branch_index(map, child, !order_link));
     952        83358 :             link(map, child, !order_link, root);
     953        83358 :             root = child;
     954        83358 :             if (!branch_index(map, root, order_link)) {
     955         8669 :                 break;
     956              :             }
     957        74689 :         }
     958       122122 :         link(map, left_right_subtrees[!order_link], order_link, root);
     959       122122 :         left_right_subtrees[!order_link] = root;
     960       122122 :         root = branch_index(map, root, order_link);
     961       157703 :     }
     962        35581 :     link(map, left_right_subtrees[L], R, branch_index(map, root, L));
     963        35581 :     link(map, left_right_subtrees[R], L, branch_index(map, root, R));
     964        35581 :     link(map, root, L, nil->branch[R]);
     965        35581 :     link(map, root, R, nil->branch[L]);
     966        35581 :     map->root = root;
     967        35581 :     *parent_pointer(map, map->root) = 0;
     968        71162 :     return root;
     969        35581 : }
     970              : 
     971              : /** Links the parent node to node starting at subtree root via direction dir.
     972              : updates the parent of the child being picked up by the new parent as well. */
     973              : static inline void
     974       453873 : link(
     975              :     struct CCC_Array_adaptive_map *const map,
     976              :     size_t const parent,
     977              :     enum Branch const dir,
     978              :     size_t const subtree
     979              : ) {
     980       453873 :     *branch_pointer(map, parent, dir) = subtree;
     981       453873 :     *parent_pointer(map, subtree) = parent;
     982       453873 : }
     983              : 
     984              : static size_t
     985           15 : min_max_from(
     986              :     struct CCC_Array_adaptive_map const *const map,
     987              :     size_t start,
     988              :     enum Branch const dir
     989              : ) {
     990           15 :     if (!start) {
     991            1 :         return 0;
     992              :     }
     993           82 :     for (; branch_index(map, start, dir);
     994           68 :          start = branch_index(map, start, dir)) {}
     995           14 :     return start;
     996           15 : }
     997              : 
     998              : static size_t
     999         4302 : next(
    1000              :     struct CCC_Array_adaptive_map const *const map,
    1001              :     size_t n,
    1002              :     enum Branch const traversal
    1003              : ) {
    1004         4302 :     if (!n) {
    1005            0 :         return 0;
    1006              :     }
    1007         4302 :     assert(!parent_index(map, map->root));
    1008         4302 :     if (branch_index(map, n, traversal)) {
    1009         4611 :         for (n = branch_index(map, n, traversal);
    1010         4611 :              branch_index(map, n, !traversal);
    1011         2457 :              n = branch_index(map, n, !traversal)) {}
    1012         2154 :         return n;
    1013              :     }
    1014         2148 :     size_t p = parent_index(map, n);
    1015         3907 :     for (; p && branch_index(map, p, !traversal) != n;
    1016         1759 :          n = p, p = parent_index(map, p)) {}
    1017         2148 :     return p;
    1018         4302 : }
    1019              : 
    1020              : /** Deletes all nodes in the tree by calling destructor function on them in
    1021              : linear time and constant space. This function modifies nodes as it deletes the
    1022              : tree elements. Assumes the destructor function is non-null.
    1023              : 
    1024              : This function does not update any count or capacity fields of the map, it
    1025              : simply calls the destructor on each node and removes the nodes references to
    1026              : other tree elements. */
    1027              : static void
    1028            2 : delete_nodes(
    1029              :     struct CCC_Array_adaptive_map const *const map,
    1030              :     CCC_Destructor const *const destructor
    1031              : ) {
    1032            2 :     size_t node = map->root;
    1033           31 :     while (node) {
    1034           29 :         struct CCC_Array_adaptive_map_node *const e = node_at(map, node);
    1035           29 :         if (e->branch[L]) {
    1036           14 :             size_t const left = e->branch[L];
    1037           14 :             e->branch[L] = node_at(map, left)->branch[R];
    1038           14 :             node_at(map, left)->branch[R] = node;
    1039           14 :             node = left;
    1040              :             continue;
    1041           14 :         }
    1042           15 :         size_t const next = e->branch[R];
    1043           15 :         e->branch[L] = e->branch[R] = 0;
    1044           15 :         e->parent = 0;
    1045           45 :         destructor->destroy((CCC_Arguments){
    1046           15 :             .type = data_at(map, node),
    1047           15 :             .context = destructor->context,
    1048              :         });
    1049           15 :         node = next;
    1050           29 :     }
    1051            2 : }
    1052              : 
    1053              : static inline CCC_Order
    1054      6796013 : order_nodes(
    1055              :     struct CCC_Array_adaptive_map const *const map,
    1056              :     void const *const key,
    1057              :     size_t const node
    1058              : ) {
    1059     27184052 :     return map->comparator.compare((CCC_Key_comparator_arguments){
    1060      6796013 :         .key_left = key,
    1061      6796013 :         .type_right = data_at(map, node),
    1062      6796013 :         .context = map->comparator.context,
    1063              :     });
    1064              : }
    1065              : 
    1066              : static inline void
    1067        10437 : init_node(struct CCC_Array_adaptive_map const *const map, size_t const node) {
    1068        10437 :     struct CCC_Array_adaptive_map_node *const e = node_at(map, node);
    1069        10437 :     e->branch[L] = e->branch[R] = e->parent = 0;
    1070        10437 : }
    1071              : 
    1072              : /** Calculates the number of bytes needed for user data INCLUDING any bytes we
    1073              : need to add to the end of the array such that the following nodes array starts
    1074              : on an aligned byte boundary given the alignment requirements of a node. This
    1075              : means the value returned from this function may or may not be slightly larger
    1076              : then the raw size of just user elements if rounding up must occur. */
    1077              : static inline size_t
    1078          258 : data_bytes(size_t const sizeof_type, size_t const capacity) {
    1079          516 :     return ((sizeof_type * capacity)
    1080          258 :             + alignof(*(struct CCC_Array_adaptive_map){}.nodes) - 1)
    1081          258 :          & ~(alignof(*(struct CCC_Array_adaptive_map){}.nodes) - 1);
    1082              : }
    1083              : 
    1084              : /** Calculates the number of bytes needed for the nodes array without any
    1085              : consideration for end padding as no arrays follow. */
    1086              : static inline size_t
    1087           90 : nodes_bytes(size_t const capacity) {
    1088           90 :     return sizeof(*(struct CCC_Array_adaptive_map){}.nodes) * capacity;
    1089              : }
    1090              : 
    1091              : /** Calculates the number of bytes needed for all arrays in the Struct of Arrays
    1092              : map design INCLUDING any extra padding bytes that need to be added between the
    1093              : data and node arrays and the node and parity arrays. Padding might be needed if
    1094              : the alignment of the type in next array that follows a preceding array is
    1095              : different from the preceding array. In that case it is the preceding array's
    1096              : responsibility to add padding bytes to its end such that the next array begins
    1097              : on an aligned byte boundary for its own type. This means that the bytes returned
    1098              : by this function may be greater than summing the (sizeof(type) * capacity) for
    1099              : each array in the conceptual struct. */
    1100              : static inline size_t
    1101           54 : total_bytes(size_t sizeof_type, size_t const capacity) {
    1102           54 :     return data_bytes(sizeof_type, capacity) + nodes_bytes(capacity);
    1103              : }
    1104              : 
    1105              : /** Returns the base of the node array relative to the data base pointer. This
    1106              : positions is guaranteed to be the first aligned byte given the alignment of the
    1107              : node type after the data array. The data array has added any necessary padding
    1108              : after it to ensure that the base of the node array is aligned for its type. */
    1109              : static inline struct CCC_Array_adaptive_map_node *
    1110          168 : nodes_base_address(
    1111              :     size_t const sizeof_type, void const *const data, size_t const capacity
    1112              : ) {
    1113          336 :     return (struct CCC_Array_adaptive_map_node *)((char *)data
    1114          168 :                                                   + data_bytes(
    1115          168 :                                                       sizeof_type, capacity
    1116              :                                                   ));
    1117              : }
    1118              : 
    1119              : /** Copies over the Struct of Arrays contained within the one contiguous
    1120              : allocation of the map to the new memory provided. Assumes the new_data pointer
    1121              : points to the base of an allocation that has been allocated with sufficient
    1122              : bytes to support the user data, nodes, and parity arrays for the provided new
    1123              : capacity. */
    1124              : static inline void
    1125           53 : resize_struct_of_arrays(
    1126              :     struct CCC_Array_adaptive_map const *const source,
    1127              :     void *const destination_data_base,
    1128              :     size_t const destination_capacity
    1129              : ) {
    1130           53 :     if (!source->data) {
    1131           17 :         return;
    1132              :     }
    1133           36 :     assert(destination_capacity >= source->capacity);
    1134           36 :     size_t const sizeof_type = source->sizeof_type;
    1135              :     /* Each section of the allocation "grows" when we re-size so one copy would
    1136              :        not work. Instead each component is copied over allowing each to grow. */
    1137           36 :     (void)memcpy(
    1138           36 :         destination_data_base,
    1139           36 :         source->data,
    1140           36 :         data_bytes(sizeof_type, source->capacity)
    1141              :     );
    1142           36 :     (void)memcpy(
    1143           36 :         nodes_base_address(
    1144           36 :             sizeof_type, destination_data_base, destination_capacity
    1145              :         ),
    1146           36 :         nodes_base_address(sizeof_type, source->data, source->capacity),
    1147           36 :         nodes_bytes(source->capacity)
    1148              :     );
    1149           89 : }
    1150              : 
    1151              : static inline void
    1152          107 : swap(void *const temp, void *const a, void *const b, size_t const sizeof_type) {
    1153          107 :     if (a == b) {
    1154            0 :         return;
    1155              :     }
    1156          107 :     (void)memcpy(temp, a, sizeof_type);
    1157          107 :     (void)memcpy(a, b, sizeof_type);
    1158          107 :     (void)memcpy(b, temp, sizeof_type);
    1159          214 : }
    1160              : 
    1161              : static inline struct CCC_Array_adaptive_map_node *
    1162     30645743 : node_at(struct CCC_Array_adaptive_map const *const map, size_t const i) {
    1163     30645743 :     return &map->nodes[i];
    1164              : }
    1165              : 
    1166              : static inline void *
    1167     13317613 : data_at(struct CCC_Array_adaptive_map const *const map, size_t const i) {
    1168     13317613 :     return (char *)map->data + (i * map->sizeof_type);
    1169              : }
    1170              : 
    1171              : static inline size_t
    1172     21687640 : branch_index(
    1173              :     struct CCC_Array_adaptive_map const *const map,
    1174              :     size_t const parent,
    1175              :     enum Branch const dir
    1176              : ) {
    1177     21687640 :     return node_at(map, parent)->branch[dir];
    1178              : }
    1179              : 
    1180              : static inline size_t
    1181      3508974 : parent_index(
    1182              :     struct CCC_Array_adaptive_map const *const map, size_t const child
    1183              : ) {
    1184      3508974 :     return node_at(map, child)->parent;
    1185              : }
    1186              : 
    1187              : static inline size_t *
    1188       464251 : branch_pointer(
    1189              :     struct CCC_Array_adaptive_map const *const map,
    1190              :     size_t const node,
    1191              :     enum Branch const branch
    1192              : ) {
    1193       464251 :     return &node_at(map, node)->branch[branch];
    1194              : }
    1195              : 
    1196              : static inline size_t *
    1197       500207 : parent_pointer(
    1198              :     struct CCC_Array_adaptive_map const *const map, size_t const node
    1199              : ) {
    1200       500207 :     return &node_at(map, node)->parent;
    1201              : }
    1202              : 
    1203              : static inline void *
    1204      6486306 : key_at(struct CCC_Array_adaptive_map const *const map, size_t const i) {
    1205      6486306 :     return (char *)data_at(map, i) + map->key_offset;
    1206              : }
    1207              : 
    1208              : static void *
    1209         8096 : key_in_slot(
    1210              :     struct CCC_Array_adaptive_map const *map, void const *const user_struct
    1211              : ) {
    1212         8096 :     return (char *)user_struct + map->key_offset;
    1213              : }
    1214              : 
    1215              : static inline size_t
    1216          132 : max(size_t const a, size_t const b) {
    1217          132 :     return a > b ? a : b;
    1218              : }
    1219              : 
    1220              : /*===========================   Validation   ===============================*/
    1221              : 
    1222              : /* NOLINTBEGIN(*misc-no-recursion) */
    1223              : 
    1224              : /** @internal */
    1225              : struct Tree_range {
    1226              :     size_t low;
    1227              :     size_t root;
    1228              :     size_t high;
    1229              : };
    1230              : 
    1231              : static size_t
    1232      7011396 : recursive_count(
    1233              :     struct CCC_Array_adaptive_map const *const map, size_t const r
    1234              : ) {
    1235      7011396 :     if (!r) {
    1236      3510631 :         return 0;
    1237              :     }
    1238      7001530 :     return 1 + recursive_count(map, branch_index(map, r, R))
    1239      3500765 :          + recursive_count(map, branch_index(map, r, L));
    1240      7011396 : }
    1241              : 
    1242              : static CCC_Tribool
    1243      7011396 : are_subtrees_valid(
    1244              :     struct CCC_Array_adaptive_map const *map, struct Tree_range const r
    1245              : ) {
    1246      7011396 :     if (!r.root) {
    1247      3510631 :         return CCC_TRUE;
    1248              :     }
    1249      3500765 :     if (r.low
    1250      3500765 :         && order_nodes(map, key_at(map, r.low), r.root) != CCC_ORDER_LESSER) {
    1251            0 :         return CCC_FALSE;
    1252              :     }
    1253      3500765 :     if (r.high
    1254      3500765 :         && order_nodes(map, key_at(map, r.high), r.root) != CCC_ORDER_GREATER) {
    1255            0 :         return CCC_FALSE;
    1256              :     }
    1257      7001530 :     return are_subtrees_valid(
    1258      3500765 :                map,
    1259     14003060 :                (struct Tree_range){
    1260      3500765 :                    .low = r.low,
    1261      3500765 :                    .root = branch_index(map, r.root, L),
    1262      3500765 :                    .high = r.root,
    1263              :                }
    1264              :            )
    1265      3500765 :         && are_subtrees_valid(
    1266      3500765 :                map,
    1267     14003060 :                (struct Tree_range){
    1268      3500765 :                    .low = r.root,
    1269      3500765 :                    .root = branch_index(map, r.root, R),
    1270      3500765 :                    .high = r.high,
    1271              :                }
    1272              :         );
    1273      7011396 : }
    1274              : 
    1275              : static CCC_Tribool
    1276      7011396 : is_storing_parent(
    1277              :     struct CCC_Array_adaptive_map const *const map,
    1278              :     size_t const p,
    1279              :     size_t const root
    1280              : ) {
    1281      7011396 :     if (!root) {
    1282      3510631 :         return CCC_TRUE;
    1283              :     }
    1284      3500765 :     if (parent_index(map, root) != p) {
    1285            0 :         return CCC_FALSE;
    1286              :     }
    1287      7001530 :     return is_storing_parent(map, root, branch_index(map, root, L))
    1288      3500765 :         && is_storing_parent(map, root, branch_index(map, root, R));
    1289      7011396 : }
    1290              : 
    1291              : static CCC_Tribool
    1292         9866 : is_free_list_valid(struct CCC_Array_adaptive_map const *const map) {
    1293         9866 :     if (!map->count) {
    1294            0 :         return CCC_TRUE;
    1295              :     }
    1296         9866 :     size_t cur_free_index = map->free_list;
    1297         9866 :     size_t list_count = 0;
    1298      4417427 :     while (cur_free_index && list_count < map->capacity) {
    1299      4407561 :         cur_free_index = node_at(map, cur_free_index)->next_free;
    1300      4407561 :         ++list_count;
    1301              :     }
    1302         9866 :     if (cur_free_index) {
    1303            0 :         return CCC_FALSE;
    1304              :     }
    1305         9866 :     if (list_count + map->count != map->capacity) {
    1306            0 :         return CCC_FALSE;
    1307              :     }
    1308         9866 :     return CCC_TRUE;
    1309         9866 : }
    1310              : 
    1311              : static CCC_Tribool
    1312         9874 : validate(struct CCC_Array_adaptive_map const *const map) {
    1313         9874 :     if (!map->count) {
    1314            8 :         return CCC_TRUE;
    1315              :     }
    1316         9866 :     if (!are_subtrees_valid(map, (struct Tree_range){.root = map->root})) {
    1317            0 :         return CCC_FALSE;
    1318              :     }
    1319         9866 :     size_t const size = recursive_count(map, map->root);
    1320         9866 :     if (size && size != map->count - 1) {
    1321            0 :         return CCC_FALSE;
    1322              :     }
    1323         9866 :     if (!is_storing_parent(map, 0, map->root)) {
    1324            0 :         return CCC_FALSE;
    1325              :     }
    1326         9866 :     if (!is_free_list_valid(map)) {
    1327            0 :         return CCC_FALSE;
    1328              :     }
    1329         9866 :     return CCC_TRUE;
    1330         9874 : }
    1331              : 
    1332              : /* NOLINTEND(*misc-no-recursion) */
        

Generated by: LCOV version 2.5.0-beta