diff --git a/list/sort.hpp b/list/sort.hpp index e2778265..681be83d 100644 --- a/list/sort.hpp +++ b/list/sort.hpp @@ -22,10 +22,25 @@ namespace cruft::list { }; + /// Find the midpoint of the list specified by the sub-list [first, last]. + /// + /// On even length lists the earlier middle node will be returned. + /// + /// Uses the fast/slow walking method. Double advancing the fast iterator, + /// and singly advancing the slow iterator. + /// + /// This necessarily walks the entire list, so the performance may be + /// quite low. + /// + /// NOTE: `last` is a valid node and forms part of the sub-list. template node* - midpoint (node *first, node *last) - { + midpoint [[gnu::nonnull]] ( + node *first, + node *last + ) { + // Pre-increment the fast node to trigger a preference for the earlier + // of the middle nodes. auto slow = first; auto fast = first->next; @@ -41,14 +56,31 @@ namespace cruft::list { return slow; } - + /// Merges a list that has been divided into two sorted halves. + /// + /// This operation will mutate the `next` pointers but will not mutate + /// the inner data objects. + /// + /// NOTE: The list is specified inclusively, and the `last` node is a + /// valid piece of data. + /// + /// Returns the first node of the newly sorted list. The caller _must_ + /// store this value otherwise a portion of the list will be lost. template node* - merge ( + merge [[nodiscard]] [[using gnu: nonnull, returns_nonnull]] ( node *first, node *middle, node *last ) { + // clang#xxxx clang mistakenly warns that the values of 'first' and + // 'middle' will be unconditionally true on the first iteration of the + // while loop. + #if defined(COMPILER_CLANG) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpointer-bool-conversion" + #endif + CHECK (first); CHECK (middle); CHECK (last); @@ -56,7 +88,9 @@ namespace cruft::list { if (first == last) return first; - auto extract = [&] () { + // Return a reference to the lower of 'first' and 'middle', and + // advance the cursor. + auto extract = [&first, &middle] () { auto &target = first->data < middle->data ? first : middle; auto res = target; target = target->next; @@ -66,26 +100,35 @@ namespace cruft::list { node *cursor = extract (); auto head = cursor; + // Keep adding the lowest node to the new list until we exhaust one + // or both of our sources. while (first && middle && first != middle && middle != last->next) { auto target = extract (); cursor->next = target; cursor = target; } + // Append the remaining data to the new list. if (first == middle || !first) cursor->next = middle; else if (middle == last->next || !middle) cursor->next = first; + #if defined(COMPILER_CLANG) + #pragma GCC diagnostic pop + #endif + return head; } - // Merge sort for singly-linked lists + /// An in-place merge sort for singly linked lists. template node* - sort (node *first, node *last) - { + sort [[nodiscard]] [[using gnu: nonnull, returns_nonnull]] ( + node *first, + node *last + ) { if (first == last) return first; @@ -99,9 +142,12 @@ namespace cruft::list { } + /// Tests if a singly linked list is sorted. template - bool is_sorted (node const *head) - { + bool + is_sorted [[gnu::nonnull]] ( + node const *head + ) { if (!head->next) return true;