diff --git a/CMakeLists.txt b/CMakeLists.txt index addd9270..989fb416 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -419,6 +419,7 @@ list ( job/queue.hpp kmeans.hpp library.hpp + list/node.hpp list/sort.hpp log.cpp log.hpp diff --git a/list/node.hpp b/list/node.hpp new file mode 100644 index 00000000..3ebaad25 --- /dev/null +++ b/list/node.hpp @@ -0,0 +1,88 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright 2019 Danny Robson + */ + +#pragma once + +#include + + +namespace cruft::list::node { + /// A node that stores the value and successor in the same object together + template + struct compound { + using value_type = ValueT; + + compound *next; + ValueT data; + }; + + + /// A node that stores either a value, or a successor pointer. Not both. + /// + /// This is quite useful for node based pool allocators and similar. + /// + /// The copying semantics of this node assume that the live member is + /// always only the 'next' pointer; it is the user's responsibility to + /// manage the lifetime of the `data` member and prevent copying of nodes + /// with a live `data` member. + template + union disjoint { + using value_type = ValueT; + + disjoint () { } + + disjoint (disjoint const &rhs) { next = rhs.next; } + disjoint& operator= (disjoint const &rhs) { next = rhs.next; } + + ~disjoint () { } + + disjoint *next; + ValueT data; + }; + + + /// A comparator object that returns the result of a supplied child + /// comparator on the data members of two node pointers. + template + class value_comparator { + public: + value_comparator (ComparatorT &&_comparator) + : m_comparator (std::move (_comparator)) + { ; } + + value_comparator (ComparatorT const &_comparator) + : m_comparator (_comparator) + { ; } + + + template + constexpr bool + operator() [[gnu::nonnull]] ( + NodeT const *a, + NodeT const *b + ) { + return m_comparator (a->data, b->data); + } + + private: + ComparatorT m_comparator; + + }; + + + struct pointer_comparator { + template + constexpr bool + operator() [[gnu::nonnull]] ( + NodeT const *a, + NodeT const *b + ) { + return a < b; + } + }; +}; diff --git a/list/sort.hpp b/list/sort.hpp index 681be83d..854cd322 100644 --- a/list/sort.hpp +++ b/list/sort.hpp @@ -8,18 +8,44 @@ #pragma once +#include "node.hpp" + #include "../debug/assert.hpp" #include #include namespace cruft::list { - /// A simple tuple-style linked list node. - template - struct node { - node *next; - ValueT data; - }; + template + std::size_t + size (NodeT const *head) + { + std::size_t count = 0; + + while (head) { + ++count; + head = head->next; + } + + return count; + } + + + /// Returns the last valid node of a linked list. ie, the node where the + /// successor is null. + /// + /// The results are undefined if the head node is null (as it is + /// impossible to return a valid node if none are valid). + template + NodeT* + end (NodeT *head) + { + CHECK (head); + + while (head->next) + head = head->next; + return head; + } /// Find the midpoint of the list specified by the sub-list [first, last]. @@ -33,11 +59,11 @@ namespace cruft::list { /// quite low. /// /// NOTE: `last` is a valid node and forms part of the sub-list. - template - node* + template + NodeT* midpoint [[gnu::nonnull]] ( - node *first, - node *last + NodeT *first, + NodeT *last ) { // Pre-increment the fast node to trigger a preference for the earlier // of the middle nodes. @@ -66,12 +92,13 @@ namespace cruft::list { /// /// 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* + template + NodeT* merge [[nodiscard]] [[using gnu: nonnull, returns_nonnull]] ( - node *first, - node *middle, - node *last + NodeT *first, + NodeT *middle, + NodeT *last, + ComparatorT &&cmp ) { // clang#xxxx clang mistakenly warns that the values of 'first' and // 'middle' will be unconditionally true on the first iteration of the @@ -90,14 +117,14 @@ namespace cruft::list { // 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 extract = [&first, &middle, &cmp] () { + auto &target = cmp (first, middle) ? first : middle; auto res = target; target = target->next; return res; }; - node *cursor = extract (); + NodeT *cursor = extract (); auto head = cursor; // Keep adding the lowest node to the new list until we exhaust one @@ -123,11 +150,12 @@ namespace cruft::list { /// An in-place merge sort for singly linked lists. - template - node* + template + NodeT* sort [[nodiscard]] [[using gnu: nonnull, returns_nonnull]] ( - node *first, - node *last + NodeT *first, + NodeT *last, + ComparatorT &&cmp ) { if (first == last) return first; @@ -136,17 +164,31 @@ namespace cruft::list { auto middle = a_end->next; a_end->next = nullptr; - auto a = ::cruft::list::sort (first, a_end); - auto b = ::cruft::list::sort (middle, last); - return ::cruft::list::merge (a, b, last); + auto a = ::cruft::list::sort (first, a_end, cmp); + auto b = ::cruft::list::sort (middle, last, cmp); + return ::cruft::list::merge (a, b, last, cmp); + } + + + template + NodeT* + sort [[nodiscard]] [[using gnu: nonnull, returns_nonnull]] ( + NodeT *head, + NodeT *tail + ) { + return sort ( + head, + tail, + node::value_comparator (std::less<> ()) + ); } /// Tests if a singly linked list is sorted. - template + template bool is_sorted [[gnu::nonnull]] ( - node const *head + NodeT const *head ) { if (!head->next) return true; diff --git a/test/list/sort.cpp b/test/list/sort.cpp index 3524cbf6..004d9bb8 100644 --- a/test/list/sort.cpp +++ b/test/list/sort.cpp @@ -28,7 +28,7 @@ test_sort ( // Pre-allocate the array so that we don't get caught by pointer // invalidation as we build the nodes. std::vector< - cruft::list::node + cruft::list::node::compound > nodes (data.size ()); // Build the list and fold in the supplied data. @@ -47,7 +47,7 @@ test_sort ( ); tap.expect ( - is_sorted (head), + cruft::list::is_sorted (head), fmt, std::forward (args)... );