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

Generated by: LCOV version 2.4.1-beta