list: prefer use of queries over direct use of members

This commit is contained in:
Danny Robson 2019-09-12 11:41:44 +10:00
parent 2258279518
commit b0bf064fdd
2 changed files with 138 additions and 31 deletions

View File

@ -22,6 +22,60 @@ namespace cruft::list::node {
};
/// Return a pointer to the successor node, or NULL if there isn't one.
template <typename ValueT>
compound<ValueT>*
next (compound<ValueT> *obj)
{
return obj->next;
}
/// Return a pointer to the successor node, or NULL if there isn't one.
template <typename ValueT>
compound<ValueT> const*
next (compound<ValueT> const *obj)
{
return obj->next;
}
/// Set the successor node and return a pointer to it.
template <typename ValueT>
compound<ValueT>*
next (compound<ValueT> *obj, compound<ValueT> *val)
{
return obj->next = val;
}
/// Clear the successor node and return a null pointer.
template <typename ValueT>
compound<ValueT>*
next (compound<ValueT> *obj, std::nullptr_t)
{
return obj->next = nullptr;
}
/// Return a reference to the node's data
template <typename ValueT>
ValueT&
data (compound<ValueT> *obj)
{
return obj->data;
}
/// Return a reference to the node's data
template <typename ValueT>
ValueT const&
data (compound<ValueT> const *obj)
{
return obj->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.
@ -31,21 +85,69 @@ namespace cruft::list::node {
/// manage the lifetime of the `data` member and prevent copying of nodes
/// with a live `data` member.
template <typename ValueT>
union disjoint {
struct 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;
std::aligned_storage_t<sizeof (ValueT), alignof (ValueT)> bytes;
};
/// Return a pointer to the successor node, or NULL if there isn't one.
template <typename ValueT>
disjoint<ValueT>*
next (disjoint<ValueT> *obj)
{
disjoint<ValueT> *res;
memcpy (&res, &obj->bytes, sizeof (res));
return res;
}
/// Return a pointer to the successor node, or NULL if there isn't one.
template <typename ValueT>
disjoint<ValueT> const*
next (disjoint<ValueT> const *obj)
{
disjoint<ValueT> *res;
memcpy (&res, &obj->bytes, sizeof (res));
return res;
}
/// Set the successor node and return a pointer to it.
template <typename ValueT>
disjoint<ValueT>*
next (disjoint<ValueT> *obj, disjoint<ValueT> *val)
{
memcpy (&obj->bytes, &val, sizeof (val));
return val;
}
/// Clear the successor node and return a null pointer.
template <typename ValueT>
compound<ValueT>*
next (disjoint<ValueT> *obj, std::nullptr_t)
{
memset (&obj->bytes, 0, sizeof (ValueT*));
return nullptr;
}
/// Return a reference to the node's data
template <typename ValueT>
ValueT&
data (disjoint<ValueT> *obj)
{
static_assert (
sizeof (ValueT) >= sizeof (*obj),
"ValueT < disjoint* prevents array indexing of nodes"
);
return *reinterpret_cast<ValueT*> (&obj->bytes);
}
/// A comparator object that returns the result of a supplied child
/// comparator on the data members of two node pointers.
template <typename ComparatorT>
@ -75,6 +177,11 @@ namespace cruft::list::node {
};
/// A comparator that returns true if the pointer to the first node is
/// less than the pointer to the second node.
///
/// This is useful for sorting pool allocator nodes for further
/// processing.
struct pointer_comparator {
template <typename NodeT>
constexpr bool

View File

@ -27,7 +27,7 @@ namespace cruft::list {
while (head) {
++count;
head = head->next;
head = next (head);
}
return count;
@ -45,8 +45,8 @@ namespace cruft::list {
{
CHECK (head);
while (head->next)
head = head->next;
while (next (head))
head = next (head);
return head;
}
@ -71,15 +71,15 @@ namespace cruft::list {
// Pre-increment the fast node to trigger a preference for the earlier
// of the middle nodes.
auto slow = first;
auto fast = first->next;
auto fast = next (first);
while (fast != last->next) {
fast = fast->next;
if (fast == last->next)
while (fast != next (last)) {
fast = next (fast);
if (fast == next (last))
return slow;
fast = fast->next;
slow = slow->next;
fast = next (fast);
slow = next (slow);
}
return slow;
@ -123,7 +123,7 @@ namespace cruft::list {
auto extract = [&first, &middle, &cmp] () {
auto &target = cmp (first, middle) ? first : middle;
auto res = target;
target = target->next;
target = next (target);
return res;
};
@ -132,17 +132,17 @@ namespace cruft::list {
// 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) {
while (first && middle && first != middle && middle != next (last)) {
auto target = extract ();
cursor->next = target;
next (cursor, 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;
next (cursor, middle);
else if (middle == next (last) || !middle)
next (cursor, first);
#if defined(COMPILER_CLANG)
#pragma GCC diagnostic pop
@ -171,8 +171,8 @@ namespace cruft::list {
return first;
auto a_end = ::cruft::list::midpoint (first, last);
auto middle = a_end->next;
a_end->next = nullptr;
auto middle = next (a_end);
next (a_end, nullptr);
auto a = ::cruft::list::sort (first, a_end, cmp);
auto b = ::cruft::list::sort (middle, last, cmp);
@ -201,18 +201,18 @@ namespace cruft::list {
is_sorted [[gnu::nonnull]] (
NodeT const *head
) {
if (!head->next)
if (!next (head))
return true;
auto a = head;
auto b = head->next;
auto b = next (head);
while (b) {
if (b->data < a->data)
return false;
a = a->next;
b = b->next;
a = next (a);
b = next (b);
}
return true;