list: add additional node types
This commit is contained in:
parent
fe6afc1edf
commit
932b93ce5e
@ -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
88
list/node.hpp
Normal 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;
|
||||
}
|
||||
};
|
||||
};
|
@ -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;
|
||||
|
@ -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)...
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user