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
kmeans.hpp
library.hpp
list/node.hpp
list/sort.hpp
log.cpp
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
#include "node.hpp"
#include "../debug/assert.hpp"
#include <iterator>
#include <utility>
namespace cruft::list {
/// A simple tuple-style linked list node.
template <typename ValueT>
struct node {
node *next;
ValueT data;
};
template <typename NodeT>
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 <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].
@ -33,11 +59,11 @@ namespace cruft::list {
/// quite low.
///
/// NOTE: `last` is a valid node and forms part of the sub-list.
template <typename ValueT>
node<ValueT>*
template <typename NodeT>
NodeT*
midpoint [[gnu::nonnull]] (
node<ValueT> *first,
node<ValueT> *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 <typename ValueT>
node<ValueT>*
template <typename NodeT, typename ComparatorT>
NodeT*
merge [[nodiscard]] [[using gnu: nonnull, returns_nonnull]] (
node<ValueT> *first,
node<ValueT> *middle,
node<ValueT> *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<ValueT> *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 <typename ValueT>
node<ValueT>*
template <typename NodeT, typename ComparatorT>
NodeT*
sort [[nodiscard]] [[using gnu: nonnull, returns_nonnull]] (
node<ValueT> *first,
node<ValueT> *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 <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.
template <typename ValueT>
template <typename NodeT>
bool
is_sorted [[gnu::nonnull]] (
node<ValueT> const *head
NodeT const *head
) {
if (!head->next)
return true;

View File

@ -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<int>
cruft::list::node::compound<int>
> 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<ArgsT> (args)...
);