list: prefer use of queries over direct use of members
This commit is contained in:
parent
2258279518
commit
b0bf064fdd
127
list/node.hpp
127
list/node.hpp
@ -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
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user