list: add additional node types

This commit is contained in:
Danny Robson 2019-09-10 14:43:08 +10:00
parent fe6afc1edf
commit 932b93ce5e
4 changed files with 160 additions and 29 deletions

View File

@ -419,6 +419,7 @@ list (
job/queue.hpp job/queue.hpp
kmeans.hpp kmeans.hpp
library.hpp library.hpp
list/node.hpp
list/sort.hpp list/sort.hpp
log.cpp log.cpp
log.hpp log.hpp

88
list/node.hpp Normal file
View File

@ -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 <danny@nerdcruft.net>
*/
#pragma once
#include <utility>
namespace cruft::list::node {
/// A node that stores the value and successor in the same object together
template <typename ValueT>
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 <typename ValueT>
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 <typename ComparatorT>
class value_comparator {
public:
value_comparator (ComparatorT &&_comparator)
: m_comparator (std::move (_comparator))
{ ; }
value_comparator (ComparatorT const &_comparator)
: m_comparator (_comparator)
{ ; }
template <typename NodeT>
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 <typename NodeT>
constexpr bool
operator() [[gnu::nonnull]] (
NodeT const *a,
NodeT const *b
) {
return a < b;
}
};
};

View File

@ -8,18 +8,44 @@
#pragma once #pragma once
#include "node.hpp"
#include "../debug/assert.hpp" #include "../debug/assert.hpp"
#include <iterator> #include <iterator>
#include <utility> #include <utility>
namespace cruft::list { namespace cruft::list {
/// A simple tuple-style linked list node. template <typename NodeT>
template <typename ValueT> std::size_t
struct node { size (NodeT const *head)
node *next; {
ValueT data; 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 <typename NodeT>
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]. /// Find the midpoint of the list specified by the sub-list [first, last].
@ -33,11 +59,11 @@ namespace cruft::list {
/// quite low. /// quite low.
/// ///
/// NOTE: `last` is a valid node and forms part of the sub-list. /// NOTE: `last` is a valid node and forms part of the sub-list.
template <typename ValueT> template <typename NodeT>
node<ValueT>* NodeT*
midpoint [[gnu::nonnull]] ( midpoint [[gnu::nonnull]] (
node<ValueT> *first, NodeT *first,
node<ValueT> *last NodeT *last
) { ) {
// Pre-increment the fast node to trigger a preference for the earlier // Pre-increment the fast node to trigger a preference for the earlier
// of the middle nodes. // of the middle nodes.
@ -66,12 +92,13 @@ namespace cruft::list {
/// ///
/// Returns the first node of the newly sorted list. The caller _must_ /// Returns the first node of the newly sorted list. The caller _must_
/// store this value otherwise a portion of the list will be lost. /// store this value otherwise a portion of the list will be lost.
template <typename ValueT> template <typename NodeT, typename ComparatorT>
node<ValueT>* NodeT*
merge [[nodiscard]] [[using gnu: nonnull, returns_nonnull]] ( merge [[nodiscard]] [[using gnu: nonnull, returns_nonnull]] (
node<ValueT> *first, NodeT *first,
node<ValueT> *middle, NodeT *middle,
node<ValueT> *last NodeT *last,
ComparatorT &&cmp
) { ) {
// clang#xxxx clang mistakenly warns that the values of 'first' and // clang#xxxx clang mistakenly warns that the values of 'first' and
// 'middle' will be unconditionally true on the first iteration of the // '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 // Return a reference to the lower of 'first' and 'middle', and
// advance the cursor. // advance the cursor.
auto extract = [&first, &middle] () { auto extract = [&first, &middle, &cmp] () {
auto &target = first->data < middle->data ? first : middle; auto &target = cmp (first, middle) ? first : middle;
auto res = target; auto res = target;
target = target->next; target = target->next;
return res; return res;
}; };
node<ValueT> *cursor = extract (); NodeT *cursor = extract ();
auto head = cursor; auto head = cursor;
// Keep adding the lowest node to the new list until we exhaust one // 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. /// An in-place merge sort for singly linked lists.
template <typename ValueT> template <typename NodeT, typename ComparatorT>
node<ValueT>* NodeT*
sort [[nodiscard]] [[using gnu: nonnull, returns_nonnull]] ( sort [[nodiscard]] [[using gnu: nonnull, returns_nonnull]] (
node<ValueT> *first, NodeT *first,
node<ValueT> *last NodeT *last,
ComparatorT &&cmp
) { ) {
if (first == last) if (first == last)
return first; return first;
@ -136,17 +164,31 @@ namespace cruft::list {
auto middle = a_end->next; auto middle = a_end->next;
a_end->next = nullptr; a_end->next = nullptr;
auto a = ::cruft::list::sort (first, a_end); auto a = ::cruft::list::sort (first, a_end, cmp);
auto b = ::cruft::list::sort (middle, last); auto b = ::cruft::list::sort (middle, last, cmp);
return ::cruft::list::merge (a, b, last); return ::cruft::list::merge (a, b, last, cmp);
}
template <typename NodeT>
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. /// Tests if a singly linked list is sorted.
template <typename ValueT> template <typename NodeT>
bool bool
is_sorted [[gnu::nonnull]] ( is_sorted [[gnu::nonnull]] (
node<ValueT> const *head NodeT const *head
) { ) {
if (!head->next) if (!head->next)
return true; return true;

View File

@ -28,7 +28,7 @@ test_sort (
// Pre-allocate the array so that we don't get caught by pointer // Pre-allocate the array so that we don't get caught by pointer
// invalidation as we build the nodes. // invalidation as we build the nodes.
std::vector< std::vector<
cruft::list::node<int> cruft::list::node::compound<int>
> nodes (data.size ()); > nodes (data.size ());
// Build the list and fold in the supplied data. // Build the list and fold in the supplied data.
@ -47,7 +47,7 @@ test_sort (
); );
tap.expect ( tap.expect (
is_sorted (head), cruft::list::is_sorted (head),
fmt, fmt,
std::forward<ArgsT> (args)... std::forward<ArgsT> (args)...
); );