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

Generated by: LCOV version 2.5.0-beta