LCOV - code coverage report
Current view: top level - source/doubly_linked_list.c (source / functions) Coverage Total Hit
Test: CCC Test Suite Coverage Report Lines: 98.9 % 468 463
Test Date: 2026-06-29 16:04:01 Functions: 100.0 % 41 41

            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              : /* Citation:
      15              : [1] See the sort methods for citations and change lists regarding the pintOS
      16              : educational operating system natural merge sort algorithm used for linked lists.
      17              : Code in the pintOS source is at  `source/lib/kernel.list.c`, but this may change
      18              : if they refactor. */
      19              : /** C23 provided headers. */
      20              : #include <stddef.h>
      21              : 
      22              : /** CCC provided headers. */
      23              : #include "ccc/configuration.h" /* IWYU pragma: keep */
      24              : #include "ccc/doubly_linked_list.h"
      25              : #include "ccc/private/private_doubly_linked_list.h"
      26              : #include "ccc/sort.h"
      27              : #include "ccc/types.h"
      28              : 
      29              : /*===========================   Prototypes    ===============================*/
      30              : 
      31              : static void push_back(
      32              :     struct CCC_Doubly_linked_list *, struct CCC_Doubly_linked_list_node *
      33              : );
      34              : static void push_front(
      35              :     struct CCC_Doubly_linked_list *, struct CCC_Doubly_linked_list_node *
      36              : );
      37              : static void remove_node(
      38              :     struct CCC_Doubly_linked_list *, struct CCC_Doubly_linked_list_node *
      39              : );
      40              : static void insert_node(
      41              :     struct CCC_Doubly_linked_list *,
      42              :     struct CCC_Doubly_linked_list_node *,
      43              :     struct CCC_Doubly_linked_list_node *
      44              : );
      45              : static void *struct_base(
      46              :     struct CCC_Doubly_linked_list const *,
      47              :     struct CCC_Doubly_linked_list_node const *
      48              : );
      49              : static void erase_inclusive_range(
      50              :     struct CCC_Doubly_linked_list const *,
      51              :     struct CCC_Doubly_linked_list_node *,
      52              :     struct CCC_Doubly_linked_list_node *,
      53              :     CCC_Allocator const *
      54              : );
      55              : static size_t
      56              : len(struct CCC_Doubly_linked_list_node const *,
      57              :     struct CCC_Doubly_linked_list_node const *);
      58              : static struct CCC_Doubly_linked_list_node *
      59              : type_intruder_in(CCC_Doubly_linked_list const *, void const *);
      60              : static struct CCC_Doubly_linked_list_node *first_out_of_order(
      61              :     struct CCC_Doubly_linked_list const *,
      62              :     struct CCC_Doubly_linked_list_node *,
      63              :     CCC_Order,
      64              :     CCC_Comparator const *
      65              : );
      66              : static struct CCC_Doubly_linked_list_node *merge(
      67              :     struct CCC_Doubly_linked_list *,
      68              :     struct CCC_Doubly_linked_list_node *,
      69              :     struct CCC_Doubly_linked_list_node *,
      70              :     struct CCC_Doubly_linked_list_node *,
      71              :     CCC_Order,
      72              :     CCC_Comparator const *
      73              : );
      74              : static CCC_Order get_order(
      75              :     struct CCC_Doubly_linked_list const *,
      76              :     struct CCC_Doubly_linked_list_node const *,
      77              :     struct CCC_Doubly_linked_list_node const *,
      78              :     CCC_Comparator const *
      79              : );
      80              : 
      81              : /*===========================     Interface   ===============================*/
      82              : 
      83              : void *
      84           26 : CCC_doubly_linked_list_push_front(
      85              :     CCC_Doubly_linked_list *const list,
      86              :     CCC_Doubly_linked_list_node *type_intruder,
      87              :     CCC_Allocator const *const allocator
      88              : ) {
      89           26 :     if (!list || !type_intruder || !allocator) {
      90            3 :         return NULL;
      91              :     }
      92           23 :     if (allocator->allocate) {
      93           20 :         void *const copy = allocator->allocate((CCC_Allocator_arguments){
      94              :             .input = NULL,
      95            5 :             .bytes = list->sizeof_type,
      96            5 :             .alignment = list->alignof_type,
      97            5 :             .context = allocator->context,
      98              :         });
      99            5 :         if (!copy) {
     100            1 :             return NULL;
     101              :         }
     102            4 :         memcpy(copy, struct_base(list, type_intruder), list->sizeof_type);
     103            4 :         type_intruder = type_intruder_in(list, copy);
     104            5 :     }
     105              : 
     106           22 :     push_front(list, type_intruder);
     107           22 :     return struct_base(list, type_intruder);
     108           26 : }
     109              : 
     110              : void *
     111           49 : CCC_doubly_linked_list_push_back(
     112              :     CCC_Doubly_linked_list *const list,
     113              :     CCC_Doubly_linked_list_node *type_intruder,
     114              :     CCC_Allocator const *const allocator
     115              : ) {
     116           49 :     if (!list || !type_intruder || !allocator) {
     117            3 :         return NULL;
     118              :     }
     119           46 :     if (allocator->allocate) {
     120           32 :         void *const node = allocator->allocate((CCC_Allocator_arguments){
     121              :             .input = NULL,
     122            8 :             .bytes = list->sizeof_type,
     123            8 :             .alignment = list->alignof_type,
     124            8 :             .context = allocator->context,
     125              :         });
     126            8 :         if (!node) {
     127            1 :             return NULL;
     128              :         }
     129            7 :         (void)memcpy(node, struct_base(list, type_intruder), list->sizeof_type);
     130            7 :         type_intruder = type_intruder_in(list, node);
     131            8 :     }
     132           45 :     push_back(list, type_intruder);
     133           45 :     return struct_base(list, type_intruder);
     134           49 : }
     135              : 
     136              : void *
     137           18 : CCC_doubly_linked_list_front(CCC_Doubly_linked_list const *list) {
     138           18 :     if (!list) {
     139            1 :         return NULL;
     140              :     }
     141           17 :     return struct_base(list, list->head);
     142           18 : }
     143              : 
     144              : void *
     145           12 : CCC_doubly_linked_list_back(CCC_Doubly_linked_list const *list) {
     146           12 :     if (!list) {
     147            1 :         return NULL;
     148              :     }
     149           11 :     return struct_base(list, list->tail);
     150           12 : }
     151              : 
     152              : CCC_Result
     153            4 : CCC_doubly_linked_list_pop_front(
     154              :     CCC_Doubly_linked_list *const list, CCC_Allocator const *const allocator
     155              : ) {
     156            4 :     if (!list || !list->head || !allocator) {
     157            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     158              :     }
     159            3 :     struct CCC_Doubly_linked_list_node *const r = list->head;
     160            3 :     remove_node(list, r);
     161            3 :     if (allocator->allocate) {
     162            3 :         assert(r);
     163           12 :         (void)allocator->allocate((CCC_Allocator_arguments){
     164            3 :             .input = struct_base(list, r),
     165              :             .bytes = 0,
     166            3 :             .alignment = list->alignof_type,
     167            3 :             .context = allocator->context,
     168              :         });
     169            3 :     }
     170            3 :     return CCC_RESULT_OK;
     171            4 : }
     172              : 
     173              : CCC_Result
     174            9 : CCC_doubly_linked_list_pop_back(
     175              :     CCC_Doubly_linked_list *const list, CCC_Allocator const *const allocator
     176              : ) {
     177            9 :     if (!list || !list->head || !allocator) {
     178            1 :         return CCC_RESULT_ARGUMENT_ERROR;
     179              :     }
     180            8 :     struct CCC_Doubly_linked_list_node *const r = list->tail;
     181            8 :     remove_node(list, r);
     182            8 :     if (allocator->allocate) {
     183           16 :         (void)allocator->allocate((CCC_Allocator_arguments){
     184            4 :             .input = struct_base(list, r),
     185              :             .bytes = 0,
     186            4 :             .alignment = list->alignof_type,
     187            4 :             .context = allocator->context,
     188              :         });
     189            4 :     }
     190            8 :     return CCC_RESULT_OK;
     191            9 : }
     192              : 
     193              : void *
     194            6 : CCC_doubly_linked_list_insert(
     195              :     CCC_Doubly_linked_list *const list,
     196              :     CCC_Doubly_linked_list_node *const position,
     197              :     CCC_Doubly_linked_list_node *type_intruder,
     198              :     CCC_Allocator const *const allocator
     199              : ) {
     200            6 :     if (!list || !type_intruder || !allocator) {
     201            3 :         return NULL;
     202              :     }
     203            3 :     if (allocator->allocate) {
     204           12 :         void *const node = allocator->allocate((CCC_Allocator_arguments){
     205              :             .input = NULL,
     206            3 :             .bytes = list->sizeof_type,
     207            3 :             .alignment = list->alignof_type,
     208            3 :             .context = allocator->context,
     209              :         });
     210            3 :         if (!node) {
     211            1 :             return NULL;
     212              :         }
     213            2 :         (void)memcpy(node, struct_base(list, type_intruder), list->sizeof_type);
     214            2 :         type_intruder = type_intruder_in(list, node);
     215            3 :     }
     216            2 :     insert_node(list, position, type_intruder);
     217            2 :     return struct_base(list, type_intruder);
     218            6 : }
     219              : 
     220              : void *
     221            5 : CCC_doubly_linked_list_erase(
     222              :     CCC_Doubly_linked_list *const list,
     223              :     CCC_Doubly_linked_list_node *type_intruder,
     224              :     CCC_Allocator const *const allocator
     225              : ) {
     226            5 :     if (!list || !type_intruder || !allocator || !list->head) {
     227            3 :         return NULL;
     228              :     }
     229            2 :     void *const ret = struct_base(list, type_intruder->next);
     230            2 :     remove_node(list, type_intruder);
     231            2 :     if (allocator->allocate) {
     232            8 :         (void)allocator->allocate((CCC_Allocator_arguments){
     233            2 :             .input = struct_base(list, type_intruder),
     234              :             .bytes = 0,
     235            2 :             .alignment = list->alignof_type,
     236            2 :             .context = allocator->context,
     237              :         });
     238            2 :     }
     239            2 :     return ret;
     240            5 : }
     241              : 
     242              : void *
     243            7 : CCC_doubly_linked_list_erase_range(
     244              :     CCC_Doubly_linked_list *const list,
     245              :     CCC_Doubly_linked_list_node *const type_intruder_begin,
     246              :     CCC_Doubly_linked_list_node *type_intruder_end,
     247              :     CCC_Allocator const *const allocator
     248              : ) {
     249            7 :     if (!list || !allocator || !list->head || !type_intruder_begin
     250            5 :         || type_intruder_begin == type_intruder_end) {
     251            3 :         return NULL;
     252              :     }
     253            4 :     if (type_intruder_end) {
     254            3 :         type_intruder_end = type_intruder_end->previous;
     255            3 :     }
     256            4 :     if (type_intruder_begin == type_intruder_end) {
     257            1 :         return CCC_doubly_linked_list_erase(
     258            1 :             list, type_intruder_begin, allocator
     259              :         );
     260              :     }
     261            3 :     CCC_Doubly_linked_list_node *const previous = type_intruder_begin->previous;
     262            6 :     CCC_Doubly_linked_list_node *const next
     263            3 :         = type_intruder_end ? type_intruder_end->next : NULL;
     264              : 
     265            3 :     if (previous) {
     266            1 :         previous->next = next;
     267            1 :     } else {
     268            2 :         list->head = next;
     269              :     }
     270              : 
     271            3 :     if (next) {
     272            2 :         next->previous = previous;
     273            2 :     } else {
     274            1 :         list->tail = previous;
     275              :     }
     276              : 
     277            3 :     type_intruder_begin->previous = NULL;
     278            3 :     if (type_intruder_end) {
     279            2 :         type_intruder_end->next = NULL;
     280            2 :     }
     281            3 :     erase_inclusive_range(
     282            3 :         list, type_intruder_begin, type_intruder_end, allocator
     283              :     );
     284              : 
     285            3 :     return struct_base(list, next);
     286            7 : }
     287              : 
     288              : CCC_Doubly_linked_list_node *
     289           25 : CCC_doubly_linked_list_node_begin(CCC_Doubly_linked_list const *const list) {
     290           25 :     return list ? list->head : NULL;
     291              : }
     292              : 
     293              : void *
     294            6 : CCC_doubly_linked_list_extract(
     295              :     CCC_Doubly_linked_list *const list,
     296              :     CCC_Doubly_linked_list_node *type_intruder
     297              : ) {
     298            6 :     if (!list || !type_intruder) {
     299            2 :         return NULL;
     300              :     }
     301            4 :     remove_node(list, type_intruder);
     302            4 :     return struct_base(list, type_intruder);
     303            6 : }
     304              : 
     305              : void *
     306            5 : CCC_doubly_linked_list_extract_range(
     307              :     CCC_Doubly_linked_list *const list,
     308              :     CCC_Doubly_linked_list_node *type_intruder_begin,
     309              :     CCC_Doubly_linked_list_node *type_intruder_end
     310              : ) {
     311            5 :     if (!list || !list->head || !type_intruder_begin
     312            4 :         || type_intruder_begin == type_intruder_end) {
     313            2 :         return NULL;
     314              :     }
     315            3 :     if (type_intruder_end) {
     316            2 :         type_intruder_end = type_intruder_end->previous;
     317            2 :     }
     318            3 :     if (type_intruder_begin == type_intruder_end) {
     319            1 :         remove_node(list, type_intruder_begin);
     320            1 :         return struct_base(list, type_intruder_begin);
     321              :     }
     322              : 
     323            2 :     CCC_Doubly_linked_list_node *const previous = type_intruder_begin->previous;
     324            4 :     CCC_Doubly_linked_list_node *const next
     325            2 :         = type_intruder_end ? type_intruder_end->next : NULL;
     326              : 
     327            2 :     if (previous) {
     328            1 :         previous->next = next;
     329            1 :     } else {
     330            1 :         list->head = next;
     331              :     }
     332              : 
     333            2 :     if (next) {
     334            1 :         next->previous = previous;
     335            1 :     } else {
     336            1 :         list->tail = previous;
     337              :     }
     338              : 
     339            2 :     type_intruder_begin->previous = NULL;
     340            2 :     if (type_intruder_end) {
     341            1 :         type_intruder_end->next = NULL;
     342            1 :     }
     343            2 :     return struct_base(list, next);
     344            5 : }
     345              : 
     346              : CCC_Result
     347           23 : CCC_doubly_linked_list_splice(
     348              :     CCC_Doubly_linked_list *const position_doubly_linked_list,
     349              :     CCC_Doubly_linked_list_node *position,
     350              :     CCC_Doubly_linked_list *const to_cut_doubly_linked_list,
     351              :     CCC_Doubly_linked_list_node *to_cut
     352              : ) {
     353           23 :     if (!position_doubly_linked_list || !to_cut_doubly_linked_list || !to_cut) {
     354            3 :         return CCC_RESULT_ARGUMENT_ERROR;
     355              :     }
     356           20 :     if ((to_cut_doubly_linked_list == position_doubly_linked_list)
     357           20 :         && (to_cut == position || (to_cut && to_cut->next == position))) {
     358            1 :         return CCC_RESULT_OK;
     359              :     }
     360           19 :     remove_node(to_cut_doubly_linked_list, to_cut);
     361           19 :     insert_node(position_doubly_linked_list, position, to_cut);
     362           19 :     return CCC_RESULT_OK;
     363           23 : }
     364              : 
     365              : CCC_Result
     366           10 : CCC_doubly_linked_list_splice_range(
     367              :     CCC_Doubly_linked_list *const position_doubly_linked_list,
     368              :     CCC_Doubly_linked_list_node *const type_intruder_position,
     369              :     CCC_Doubly_linked_list *const to_cut_doubly_linked_list,
     370              :     CCC_Doubly_linked_list_node *const type_intruder_to_cut_begin,
     371              :     CCC_Doubly_linked_list_node *const type_intruder_to_cut_exclusive_end
     372              : ) {
     373           10 :     if (!position_doubly_linked_list || !to_cut_doubly_linked_list
     374            9 :         || !type_intruder_to_cut_begin
     375            8 :         || type_intruder_to_cut_begin == type_intruder_to_cut_exclusive_end) {
     376            3 :         return CCC_RESULT_ARGUMENT_ERROR;
     377              :     }
     378           14 :     CCC_Doubly_linked_list_node *const to_cut_inclusive_end
     379            7 :         = type_intruder_to_cut_exclusive_end
     380            3 :             ? type_intruder_to_cut_exclusive_end->previous
     381            4 :             : to_cut_doubly_linked_list->tail;
     382            7 :     if (type_intruder_to_cut_begin == to_cut_inclusive_end) {
     383            1 :         return CCC_doubly_linked_list_splice(
     384            1 :             position_doubly_linked_list,
     385            1 :             type_intruder_position,
     386            1 :             to_cut_doubly_linked_list,
     387            1 :             type_intruder_to_cut_begin
     388              :         );
     389              :     }
     390              : 
     391           12 :     CCC_Doubly_linked_list_node *const to_cut_previous
     392            6 :         = type_intruder_to_cut_begin->previous;
     393              : 
     394            6 :     if (to_cut_previous) {
     395            4 :         to_cut_previous->next = type_intruder_to_cut_exclusive_end;
     396            4 :     } else {
     397            2 :         to_cut_doubly_linked_list->head = type_intruder_to_cut_exclusive_end;
     398              :     }
     399              : 
     400            6 :     if (type_intruder_to_cut_exclusive_end) {
     401            2 :         type_intruder_to_cut_exclusive_end->previous = to_cut_previous;
     402            2 :     } else {
     403            4 :         to_cut_doubly_linked_list->tail = to_cut_previous;
     404              :     }
     405           12 :     CCC_Doubly_linked_list_node *const position_previous
     406            6 :         = type_intruder_position ? type_intruder_position->previous
     407            1 :                                  : position_doubly_linked_list->tail;
     408              : 
     409            6 :     if (position_previous == position_doubly_linked_list->tail) {
     410            2 :         position_doubly_linked_list->tail = to_cut_inclusive_end;
     411            2 :     }
     412            6 :     if (position_previous) {
     413            2 :         position_previous->next = type_intruder_to_cut_begin;
     414            2 :     } else {
     415            4 :         position_doubly_linked_list->head = type_intruder_to_cut_begin;
     416              :     }
     417              : 
     418            6 :     type_intruder_to_cut_begin->previous = position_previous;
     419              : 
     420            6 :     if (to_cut_inclusive_end) {
     421            6 :         to_cut_inclusive_end->next = type_intruder_position;
     422            6 :     }
     423              : 
     424            6 :     if (type_intruder_position) {
     425            5 :         type_intruder_position->previous = to_cut_inclusive_end;
     426            5 :     }
     427              : 
     428            6 :     return CCC_RESULT_OK;
     429           10 : }
     430              : 
     431              : void *
     432           41 : CCC_doubly_linked_list_begin(CCC_Doubly_linked_list const *const list) {
     433           41 :     if (!list || list->head == NULL) {
     434            1 :         return NULL;
     435              :     }
     436           40 :     return struct_base(list, list->head);
     437           41 : }
     438              : 
     439              : void *
     440           37 : CCC_doubly_linked_list_reverse_begin(CCC_Doubly_linked_list const *const list) {
     441           37 :     if (!list || list->tail == NULL) {
     442            1 :         return NULL;
     443              :     }
     444           36 :     return struct_base(list, list->tail);
     445           37 : }
     446              : 
     447              : void *
     448          286 : CCC_doubly_linked_list_end(CCC_Doubly_linked_list const *const) {
     449          286 :     return NULL;
     450              : }
     451              : 
     452              : void *
     453          260 : CCC_doubly_linked_list_reverse_end(CCC_Doubly_linked_list const *const) {
     454          260 :     return NULL;
     455              : }
     456              : 
     457              : void *
     458          243 : CCC_doubly_linked_list_next(
     459              :     CCC_Doubly_linked_list const *const list,
     460              :     CCC_Doubly_linked_list_node const *type_intruder
     461              : ) {
     462          243 :     if (!list || !type_intruder || type_intruder->next == NULL) {
     463           35 :         return NULL;
     464              :     }
     465          208 :     return struct_base(list, type_intruder->next);
     466          243 : }
     467              : 
     468              : void *
     469          231 : CCC_doubly_linked_list_reverse_next(
     470              :     CCC_Doubly_linked_list const *const list,
     471              :     CCC_Doubly_linked_list_node const *type_intruder
     472              : ) {
     473          231 :     if (!list || !type_intruder || type_intruder->previous == NULL) {
     474           34 :         return NULL;
     475              :     }
     476          197 :     return struct_base(list, type_intruder->previous);
     477          231 : }
     478              : 
     479              : CCC_Count
     480           38 : CCC_doubly_linked_list_count(CCC_Doubly_linked_list const *const list) {
     481           38 :     if (!list) {
     482            1 :         return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
     483              :     }
     484           37 :     return (CCC_Count){.count = len(list->head, NULL)};
     485           38 : }
     486              : 
     487              : CCC_Tribool
     488           12 : CCC_doubly_linked_list_is_empty(CCC_Doubly_linked_list const *const list) {
     489           12 :     if (!list) {
     490            1 :         return CCC_TRIBOOL_ERROR;
     491              :     }
     492           11 :     return !list->head;
     493           12 : }
     494              : 
     495              : CCC_Result
     496            7 : CCC_doubly_linked_list_clear(
     497              :     CCC_Doubly_linked_list *const list,
     498              :     CCC_Destructor const *const destructor,
     499              :     CCC_Allocator const *const allocator
     500              : ) {
     501            7 :     if (!list || !destructor || !allocator) {
     502            3 :         return CCC_RESULT_ARGUMENT_ERROR;
     503              :     }
     504            4 :     if (!destructor->destroy && !allocator->allocate) {
     505            1 :         list->head = list->tail = NULL;
     506            1 :         return CCC_RESULT_OK;
     507              :     }
     508           17 :     while (list->head) {
     509           14 :         struct CCC_Doubly_linked_list_node *const removed = list->head;
     510           14 :         remove_node(list, removed);
     511           14 :         void *const node = struct_base(list, removed);
     512           14 :         if (destructor->destroy) {
     513           24 :             destructor->destroy((CCC_Arguments){
     514            8 :                 .type = node,
     515            8 :                 .context = destructor->context,
     516              :             });
     517            8 :         }
     518           14 :         if (allocator->allocate) {
     519           56 :             (void)allocator->allocate((CCC_Allocator_arguments){
     520           14 :                 .input = node,
     521              :                 .bytes = 0,
     522           14 :                 .alignment = list->alignof_type,
     523           14 :                 .context = allocator->context,
     524              :             });
     525           14 :         }
     526           14 :     }
     527            3 :     list->tail = NULL;
     528            3 :     return CCC_RESULT_OK;
     529            7 : }
     530              : 
     531              : CCC_Tribool
     532          118 : CCC_doubly_linked_list_validate(CCC_Doubly_linked_list const *const list) {
     533          118 :     if (!list) {
     534            0 :         return CCC_TRIBOOL_ERROR;
     535              :     }
     536          118 :     if (!list->head && !list->tail) {
     537            7 :         return CCC_TRUE;
     538              :     }
     539          111 :     if (!list->head || !list->tail) {
     540            0 :         return CCC_FALSE;
     541              :     }
     542          111 :     struct CCC_Doubly_linked_list_node const *forward = list->head;
     543          111 :     struct CCC_Doubly_linked_list_node const *reverse = list->tail;
     544          789 :     while (forward && reverse && forward != list->tail
     545          450 :            && reverse != list->head) {
     546          339 :         if (!forward || forward->next == forward
     547          339 :             || forward->previous == forward) {
     548            0 :             return CCC_FALSE;
     549              :         }
     550          339 :         if (!reverse || reverse->next == reverse
     551          339 :             || reverse->previous == reverse) {
     552            0 :             return CCC_FALSE;
     553              :         }
     554          339 :         forward = forward->next;
     555          339 :         reverse = reverse->previous;
     556              :     }
     557          111 :     return CCC_TRUE;
     558          118 : }
     559              : 
     560              : /*==========================     Sorting     ================================*/
     561              : 
     562              : /** Returns true if the list is sorted in non-decreasing order. The user should
     563              : flip the return values of their comparison function if they want a different
     564              : order for elements.*/
     565              : CCC_Tribool
     566           20 : CCC_doubly_linked_list_is_sorted(
     567              :     CCC_Doubly_linked_list const *const list,
     568              :     CCC_Order const order,
     569              :     CCC_Comparator const *const comparator
     570              : ) {
     571           20 :     if (!list || !comparator || !comparator->compare
     572           18 :         || (order != CCC_ORDER_LESSER && order != CCC_ORDER_GREATER)) {
     573            3 :         return CCC_TRIBOOL_ERROR;
     574              :     }
     575           17 :     if (list->head == list->tail) {
     576            1 :         return CCC_TRUE;
     577              :     }
     578           32 :     CCC_Order const wrong_order
     579           16 :         = order == CCC_ORDER_LESSER ? CCC_ORDER_GREATER : CCC_ORDER_LESSER;
     580           88 :     for (struct CCC_Doubly_linked_list_node const *cur = list->head->next;
     581           79 :          cur != NULL;
     582           63 :          cur = cur->next) {
     583           72 :         if (get_order(list, cur->previous, cur, comparator) == wrong_order) {
     584            9 :             return CCC_FALSE;
     585              :         }
     586           63 :     }
     587            7 :     return CCC_TRUE;
     588           20 : }
     589              : 
     590              : /** Inserts an element in the provided order. This means an element will go to
     591              : the end of a section of duplicate values which is good for round-robin style
     592              : list use. */
     593              : void *
     594           12 : CCC_doubly_linked_list_insert_sorted(
     595              :     CCC_Doubly_linked_list *const list,
     596              :     CCC_Doubly_linked_list_node *type_intruder,
     597              :     CCC_Order const order,
     598              :     CCC_Comparator const *const comparator,
     599              :     CCC_Allocator const *const allocator
     600              : ) {
     601           12 :     if (!list || !allocator || !comparator || !comparator->compare
     602            9 :         || !type_intruder) {
     603            4 :         return NULL;
     604              :     }
     605            8 :     if (allocator->allocate) {
     606           12 :         void *const node = allocator->allocate((CCC_Allocator_arguments){
     607              :             .input = NULL,
     608            3 :             .bytes = list->sizeof_type,
     609            3 :             .alignment = list->alignof_type,
     610            3 :             .context = allocator->context,
     611              :         });
     612            3 :         if (!node) {
     613            2 :             return NULL;
     614              :         }
     615            1 :         (void)memcpy(node, struct_base(list, type_intruder), list->sizeof_type);
     616            1 :         type_intruder = type_intruder_in(list, node);
     617            3 :     }
     618            6 :     struct CCC_Doubly_linked_list_node *pos = list->head;
     619           87 :     for (; pos != NULL
     620           44 :            && get_order(list, type_intruder, pos, comparator) != order;
     621           38 :          pos = pos->next) {}
     622            6 :     insert_node(list, pos, type_intruder);
     623            6 :     return struct_base(list, type_intruder);
     624           12 : }
     625              : 
     626              : /** Sorts the list in the provided order according to the user comparison
     627              : callback function in `O(N * log(N))` time and `O(1)` space. This sort is stable.
     628              : 
     629              : The following merging algorithm and associated helper functions are based on
     630              : the iterative natural merge sort used in the list module of the pintOS project
     631              : for learning operating systems. Currently the original is located at the
     632              : following path in the pintOS source code:
     633              : 
     634              : `src/lib/kernel/list.c`
     635              : 
     636              : However, if refactors change this location, seek the list intrusive container
     637              : module for original implementations. The code has been changed for the C
     638              : Container Collection as follows:
     639              : 
     640              : - there is no sentinel node, only NULL pointer.
     641              : - splicing in the merge operation has been simplified along with other tweaks.
     642              : - comparison callbacks are handled with three way comparison.
     643              : 
     644              : If the runtime is not obvious from the code, consider that this algorithm runs
     645              : bottom up on sorted sub-ranges. It roughly "halves" the remaining sub-ranges
     646              : that need to be sorted by roughly "doubling" the length of a sorted range on
     647              : each merge step. Therefore the number of times we must perform the merge step is
     648              : `O(log(N))`. The most elements we would have to merge in the merge step is all
     649              : `N` elements so together that gives us the runtime of `O(N * log(N))`. */
     650              : CCC_Result
     651           10 : CCC_sort_doubly_linked_list_mergesort(
     652              :     CCC_Doubly_linked_list *const list,
     653              :     CCC_Order const order,
     654              :     CCC_Comparator const *const comparator
     655              : ) {
     656           10 :     if (!list || !comparator || !comparator->compare
     657            8 :         || (order != CCC_ORDER_LESSER && order != CCC_ORDER_GREATER)) {
     658            3 :         return CCC_RESULT_ARGUMENT_ERROR;
     659              :     }
     660              :     /* Algorithm is one pass if list is sorted. Merging is never true. */
     661            7 :     CCC_Tribool merging = CCC_FALSE;
     662            7 :     do {
     663           28 :         merging = CCC_FALSE;
     664              :         /* 0th index of the A list. The start of one list to merge. */
     665           28 :         struct CCC_Doubly_linked_list_node *left_start = list->head;
     666           66 :         while (left_start != NULL) {
     667              :             /* The Nth index of list A (its size) aka 0th index of B list. */
     668          106 :             struct CCC_Doubly_linked_list_node *const left_end_right_start
     669           53 :                 = first_out_of_order(list, left_start, order, comparator);
     670           53 :             if (left_end_right_start == NULL) {
     671           15 :                 break;
     672              :             }
     673              :             /* Left picks up the exclusive end of this merge, the start of
     674              :                right, in order to progress the sorting algorithm with the next
     675              :                run that needs fixing. Merge returns the end of right to indicate
     676              :                it is the final sentinel node yet to be examined. */
     677           38 :             left_start = merge(
     678           38 :                 list,
     679           38 :                 left_start,
     680           38 :                 left_end_right_start,
     681           38 :                 first_out_of_order(
     682           38 :                     list, left_end_right_start, order, comparator
     683              :                 ),
     684           38 :                 order,
     685           38 :                 comparator
     686              :             );
     687           38 :             merging = CCC_TRUE;
     688           53 :         }
     689           28 :     } while (merging);
     690            7 :     return CCC_RESULT_OK;
     691           10 : }
     692              : 
     693              : /** Merges lists `[left, right)` with `[right, right_end)`
     694              : to form `[left, right_end)`. Returns the exclusive end of the range,
     695              : `right_end`, once the merge sort is complete.
     696              : 
     697              : Notice that all ranges treat the end of their range as an exclusive sentinel for
     698              : consistency. This function assumes the provided lists are already sorted
     699              : separately. */
     700              : static inline struct CCC_Doubly_linked_list_node *
     701           38 : merge(
     702              :     struct CCC_Doubly_linked_list *const list,
     703              :     struct CCC_Doubly_linked_list_node *left,
     704              :     struct CCC_Doubly_linked_list_node *right,
     705              :     struct CCC_Doubly_linked_list_node *const right_end,
     706              :     CCC_Order const order,
     707              :     CCC_Comparator const *const comparator
     708              : ) {
     709          154 :     while (left && left != right && right && right != right_end) {
     710          116 :         if (get_order(list, right, left, comparator) == order) {
     711           77 :             struct CCC_Doubly_linked_list_node *const to_merge = right;
     712           77 :             right = to_merge->next;
     713           77 :             if (to_merge->next) {
     714           64 :                 to_merge->next->previous = to_merge->previous;
     715           64 :             } else {
     716           13 :                 list->tail = to_merge->previous;
     717              :             }
     718            0 :             assert(
     719           77 :                 to_merge->previous
     720           77 :                 && "merged element must always have a previous pointer because "
     721              :                    "lists of size 1 or less are not merged and merging "
     722              :                    "iterates forward"
     723              :             );
     724           77 :             to_merge->previous->next = to_merge->next;
     725           77 :             to_merge->previous = left->previous;
     726           77 :             to_merge->next = left;
     727           77 :             if (left->previous) {
     728           57 :                 left->previous->next = to_merge;
     729           57 :             } else {
     730           20 :                 list->head = to_merge;
     731              :             }
     732           77 :             left->previous = to_merge;
     733           77 :         } else {
     734           39 :             left = left->next;
     735              :         }
     736              :     }
     737           38 :     return right_end;
     738              : }
     739              : 
     740              : /** Finds the first element lesser than it's previous element as defined by
     741              : the user comparison callback function. If no out of order element can be
     742              : found the list sentinel is returned. */
     743              : static inline struct CCC_Doubly_linked_list_node *
     744           91 : first_out_of_order(
     745              :     struct CCC_Doubly_linked_list const *const list,
     746              :     struct CCC_Doubly_linked_list_node *start,
     747              :     CCC_Order const order,
     748              :     CCC_Comparator const *const comparator
     749              : ) {
     750           91 :     assert(list && start);
     751           91 :     do {
     752          268 :         start = start->next;
     753          359 :     } while (start != NULL
     754          268 :              && get_order(list, start, start->previous, comparator) != order);
     755           91 :     return start;
     756              : }
     757              : 
     758              : /*=======================     Private Interface   ===========================*/
     759              : 
     760              : void
     761          101 : CCC_private_doubly_linked_list_push_back(
     762              :     struct CCC_Doubly_linked_list *const list,
     763              :     struct CCC_Doubly_linked_list_node *type_intruder
     764              : ) {
     765          101 :     push_back(list, type_intruder);
     766          101 : }
     767              : 
     768              : void
     769            4 : CCC_private_doubly_linked_list_push_front(
     770              :     struct CCC_Doubly_linked_list *const list,
     771              :     struct CCC_Doubly_linked_list_node *const type_intruder
     772              : ) {
     773            4 :     push_front(list, type_intruder);
     774            4 : }
     775              : 
     776              : struct CCC_Doubly_linked_list_node *
     777          105 : CCC_private_doubly_linked_list_node_in(
     778              :     struct CCC_Doubly_linked_list const *const list,
     779              :     void const *const any_struct
     780              : ) {
     781          105 :     return type_intruder_in(list, any_struct);
     782              : }
     783              : 
     784              : /*=======================       Static Helpers    ===========================*/
     785              : 
     786              : static inline void
     787           26 : push_front(
     788              :     struct CCC_Doubly_linked_list *const list,
     789              :     struct CCC_Doubly_linked_list_node *const node
     790              : ) {
     791           26 :     node->previous = NULL;
     792           26 :     node->next = list->head;
     793           26 :     if (list->head) {
     794           17 :         list->head->previous = node;
     795           17 :     } else {
     796            9 :         list->tail = node;
     797              :     }
     798           26 :     list->head = node;
     799           26 : }
     800              : 
     801              : static inline void
     802          149 : push_back(
     803              :     struct CCC_Doubly_linked_list *const list,
     804              :     struct CCC_Doubly_linked_list_node *const node
     805              : ) {
     806          149 :     node->next = NULL;
     807          149 :     node->previous = list->tail;
     808          149 :     if (list->tail) {
     809          124 :         list->tail->next = node;
     810          124 :     } else {
     811           25 :         list->head = node;
     812              :     }
     813          149 :     list->tail = node;
     814          149 : }
     815              : 
     816              : static inline void
     817           27 : insert_node(
     818              :     struct CCC_Doubly_linked_list *const list,
     819              :     struct CCC_Doubly_linked_list_node *const position,
     820              :     struct CCC_Doubly_linked_list_node *const node
     821              : ) {
     822           27 :     if (!position) {
     823            3 :         return push_back(list, node);
     824              :     }
     825           24 :     node->next = position;
     826           24 :     node->previous = position->previous;
     827              : 
     828           24 :     if (position->previous) {
     829            7 :         position->previous->next = node;
     830            7 :     } else {
     831           17 :         list->head = node;
     832              :     }
     833           24 :     position->previous = node;
     834           51 : }
     835              : 
     836              : static inline void
     837           51 : remove_node(
     838              :     struct CCC_Doubly_linked_list *const list,
     839              :     struct CCC_Doubly_linked_list_node *const node
     840              : ) {
     841           51 :     if (node->previous) {
     842           27 :         node->previous->next = node->next;
     843           27 :     } else {
     844           24 :         list->head = node->next;
     845              :     }
     846           51 :     if (node->next) {
     847           31 :         node->next->previous = node->previous;
     848           31 :     } else {
     849           20 :         list->tail = node->previous;
     850              :     }
     851           51 :     node->next = node->previous = NULL;
     852           51 : }
     853              : 
     854              : /** Operates on each element in the specified range calling the allocation
     855              : function to free the nodes, if provided. In either case, the number of nodes
     856              : in the inclusive range is returned. */
     857              : static void
     858            3 : erase_inclusive_range(
     859              :     struct CCC_Doubly_linked_list const *const list,
     860              :     struct CCC_Doubly_linked_list_node *begin,
     861              :     struct CCC_Doubly_linked_list_node *const inclusive_end,
     862              :     CCC_Allocator const *const allocator
     863              : ) {
     864            3 :     if (!allocator->allocate) {
     865            1 :         return;
     866              :     }
     867            7 :     while (begin) {
     868            6 :         CCC_Doubly_linked_list_node *const next = begin->next;
     869           24 :         allocator->allocate((CCC_Allocator_arguments){
     870            6 :             .input = struct_base(list, begin),
     871              :             .bytes = 0,
     872            6 :             .alignment = list->alignof_type,
     873            6 :             .context = allocator->context,
     874              :         });
     875            6 :         if (begin == inclusive_end) {
     876            1 :             break;
     877              :         }
     878            5 :         begin = next;
     879            6 :     }
     880            5 : }
     881              : 
     882              : /** Finds the length from [begin, end). */
     883              : static size_t
     884           37 : len(struct CCC_Doubly_linked_list_node const *begin,
     885              :     struct CCC_Doubly_linked_list_node const *const end) {
     886           37 :     size_t s = 0;
     887          137 :     while (begin != end) {
     888          100 :         begin = begin->next;
     889          100 :         ++s;
     890              :     }
     891           74 :     return s;
     892           37 : }
     893              : 
     894              : static inline void *
     895         1581 : struct_base(
     896              :     struct CCC_Doubly_linked_list const *const list,
     897              :     struct CCC_Doubly_linked_list_node const *const node
     898              : ) {
     899         1581 :     return node ? ((char *)&node->next) - list->type_intruder_offset : NULL;
     900              : }
     901              : 
     902              : static inline struct CCC_Doubly_linked_list_node *
     903          119 : type_intruder_in(
     904              :     struct CCC_Doubly_linked_list const *const list,
     905              :     void const *const struct_base
     906              : ) {
     907          119 :     return struct_base
     908              :              ? (struct CCC_Doubly_linked_list_node
     909          119 :                     *)((char *)struct_base + list->type_intruder_offset)
     910              :              : NULL;
     911              : }
     912              : 
     913              : static inline CCC_Order
     914          471 : get_order(
     915              :     struct CCC_Doubly_linked_list const *const list,
     916              :     struct CCC_Doubly_linked_list_node const *const left,
     917              :     struct CCC_Doubly_linked_list_node const *const right,
     918              :     CCC_Comparator const *const comparator
     919              : ) {
     920         1884 :     return comparator->compare((CCC_Comparator_arguments){
     921          471 :         .type_left = struct_base(list, left),
     922          471 :         .type_right = struct_base(list, right),
     923          471 :         .context = comparator->context,
     924              :     });
     925              : }
        

Generated by: LCOV version 2.4-beta