AnimTestbed/3rdparty/ozz-animation/include/ozz/base/containers/intrusive_list.h

1129 lines
41 KiB
C++

//----------------------------------------------------------------------------//
// //
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
// and distributed under the MIT License (MIT). //
// //
// Copyright (c) Guillaume Blanc //
// //
// Permission is hereby granted, free of charge, to any person obtaining a //
// copy of this software and associated documentation files (the "Software"), //
// to deal in the Software without restriction, including without limitation //
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
// and/or sell copies of the Software, and to permit persons to whom the //
// Software is furnished to do so, subject to the following conditions: //
// //
// The above copyright notice and this permission notice shall be included in //
// all copies or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
// DEALINGS IN THE SOFTWARE. //
// //
//----------------------------------------------------------------------------//
#ifndef OZZ_OZZ_BASE_CONTAINERS_INTRUSIVE_LIST_H_
#define OZZ_OZZ_BASE_CONTAINERS_INTRUSIVE_LIST_H_
#include <cassert>
#include <cstddef>
#include <iterator>
namespace ozz {
namespace containers {
// Enumerate all the link modes that can be used.
struct LinkMode {
enum Value {
kSafe, // RECOMMENDED default mode.
// Hooks and lists can not be deleted while they are linked.
// Programming errors that can corrupt the list are detected:
// - pushing a hook twice in a list.
// - popping an unlinked hook.
// - deleting a linked hook.
// - deleting a list that still contains hooks.
// This is the default and preferred mode as the rules above
// give a lot of guarantees to the user, often even about its own
// algorithm consistency.
kAuto, // Does the same checks as kSafe, but automatically unlink all hooks
// when the list is destroyed. It automatically unlinks a hook when
// it is destroyed also. BE CAREFUL that the containers can silently
// be modified (without any container's function call), which can
// easily lead to thread-unsafe code.
kUnsafe, // NOT RECOMMENDED.
// Behaves exactly as kSafe mode, but does not assert for
// deletion of a linked hook or a non empty list.
// This mode is unsafe as deleting a linked hook or a non empty
// list leads to corrupt data (dangling pointers). This is useful
// when the user knows that all the data (hooks + list) are going
// to be erased and that neither the list or any hook of the list
// will be accessed. This mode is NOT RECOMMENDED, but still
// allows to remove a O(n) algorithm (release all hooks) in some
// rare cases where a list is not by nature empty (or relatively
// small) at destruction time: .
};
};
// Holds the options for the IntrusiveList containers.
// _Unique is never used in the code, but differentiates the type of multiple
// IntrusiveList at compile time. This is useful in order to store the same
// hook in more than one list (differentiated by their _Unique identifier thus)
// at the same time.
// _LinkMode is a value of LinkMode enumeration.
template <LinkMode::Value _LinkMode = LinkMode::kSafe, int _Unique = 0>
struct Option {
static const LinkMode::Value kLinkMode = _LinkMode;
};
// Defines the intrusive list container class.
// In order to use a type _Ty within the IntrusiveList, _Ty elements must
// inherit from IntrusiveList<...>::Hook objects. This Hook type is the
// "intrusive" part of the intrusive list implementation, defining pointers of
// the linked list.
// The IntrusiveList implements all std::list functions, taking advantage of the
// O(1) capabilities of the intrusive list. The size() function is NOT constant
// time though, but linear O(n). If you wish to test whether a list is empty,
// you should use empty() rather than size() == 0.
template <typename _Ty, typename _Option = Option<>>
class IntrusiveList;
// Enters the internal namespace that encloses private implementation details.
namespace internal {
// Forward declares IntrusiveNodeList.
class IntrusiveNodeList;
// Defines the node class that's linked by the IntrusiveListImpl.
// This is an internal class as the user's node class must inherit from
// IntrusiveList<>::Hook (which inherit from Node).
class Node {
public:
// Unlinks *this node from its current list.
// This function must be called on a linked node.
void unlink();
// Test if *this node is linked in a list.
// This function is not able to test for a particular list.
bool is_linked() const { return prev_ != this; }
#ifndef NDEBUG
// Test if *this node is linked in _list.
// This function is only available for debug purpose.
// It tests the same thing is_linked does, but allows to test
// which particular list links *this node. Will return false if the node is
// linked in another list.
bool debug_is_linked_in(const IntrusiveNodeList& _list) const {
return &_list == list_;
}
#endif // NDEBUG
protected:
// Constructs an unlinked node.
Node()
#ifndef NDEBUG
: list_(nullptr)
#endif // NDEBUG
{
prev_ = this;
next_ = this;
}
// Destructs the node, no check is done as they depend on the LinkMode.
~Node() {}
private:
// The node class can be publicly used by the internal layers.
friend class IntrusiveNodeList;
template <typename, typename>
friend class IntrusiveListIterator;
// Pushes (inserts) *this node before _node.
// *this node must be unlinked and _node must be linked.
void insert(Node* _where);
#ifndef NDEBUG
// Tests if *this node is the end node of a list.
// This function is only available for debug purpose.
// end_ is the first member of the list, which allows to compare *this
// address with list_.
bool debug_is_end_node() const {
return list_ == reinterpret_cast<IntrusiveNodeList const*>(this);
}
#endif // NDEBUG
// Disallow Node copy and assignation
Node(const Node&);
void operator=(const Node&);
// prev_ and next_ points to *this node if *this is NOT linked.
Node* prev_; // Pointer to the previous node in the list.
Node* next_; // Pointer to the next node in the list.
#ifndef NDEBUG
// Pointer to the list_ that references this node, used for debugging only.
IntrusiveNodeList* list_;
#endif // NDEBUG
};
// Implements non template algorithms of the IntrusiveList class.
// This class is based on Node type only, but still implement IntrusiveList
// public algorithms.
class IntrusiveNodeList {
public:
// Constructs an empty list.
IntrusiveNodeList() {
#ifndef NDEBUG
end_.list_ = this;
#endif // NDEBUG
}
// Destructs a list. Assertions are done in the templates class as they
// depend on the LinkMode argument.
~IntrusiveNodeList() {}
// Removes all the elements from the list iteratively.
// This function has an O(n) complexity.
void clear();
// Returns true if the list contains no element.
bool empty() const { return end_.next_ == &end_; }
// Reverses the order of elements in the list.
// All iterators remain valid and continue to point to the same elements.
// This function is linear time O(n).
void reverse();
// Swaps the contents of two lists.
// This function as O(1) complexity (except in debug builds) as opposed to
// the std::list implementation.
void swap(IntrusiveNodeList& _list);
// Returns the size of the list.
// This function is NOT constant time but linear O(n). If you wish to test
// whether a list is empty, you should write l.empty() rather than
// l.size() == 0.
size_t size() const;
protected:
// The type used to counts the number of elements in a list.
typedef size_t size_type;
// Returns the first node of the list if it is not empty, end node otherwise.
Node& begin_node() { return *end_.next_; }
const Node& begin_node() const { return *end_.next_; }
// Returns the last node of the list if it is not empty, end node otherwise.
Node& last_node() { return *end_.prev_; }
const Node& last_node() const { return *end_.prev_; }
// Returns the end node of the list.
Node& end_node() { return end_; }
const Node& end_node() const { return end_; }
// Links _node at the front of the list, ie: just after end node.
void link_front(Node* _node) { _node->insert(end_.next_); }
// Links _node at the back of the list, ie: just before end node.
void link_back(Node* _node) { _node->insert(&end_); }
// Inserts _node before _where.
void _insert(Node* _node, Node* _where) { _node->insert(_where); }
#ifndef NDEBUG
// Tests if the range [_begin, end_[ is valid, ie: _begin <= _end.
// The range invalidity is triggered if _begin and _end are not in the same
// list, are not linked, or if the end node of the list is traversed while
// iterating from _begin to _end. Unfortunately it makes the algorithm O(n).
bool debug_is_range_valid(const Node& _begin, const Node& _end) {
if (!_begin.debug_is_linked_in(*this) || !_end.debug_is_linked_in(*this)) {
return false;
}
const Node* node = &_begin;
while (node != &_end) {
if (node == &_begin.list_->end_) {
return false;
}
node = node->next_;
}
return true;
}
#endif // NDEBUG
// Implements splice algorithm.
void _splice(Node* _where, Node* _first, Node* _end);
// Implements erase algorithm.
void _erase(Node* _begin, Node* _end);
// Implements equality test using _pred functor.
template <typename _Pred>
bool _is_equal(IntrusiveNodeList const& _list, _Pred _pred) const;
// Implements "less than" test using _pred functor.
template <typename _Pred>
bool _is_less(IntrusiveNodeList const& _list, _Pred _pred) const;
// Implements merge algorithm using _pred functor.
template <typename _Pred>
void _merge(IntrusiveNodeList* _list, _Pred _pred);
// Implements sort algorithm using _pred functor.
template <typename _Pred>
void _sort(_Pred _pred);
// Implements merge algorithm using _pred functor.
template <typename _Pred>
bool _is_ordered(_Pred _pred) const;
// Implements remove_if algorithm using _pred functor.
template <typename _Pred>
void _remove_if(_Pred _pred);
private:
// Base iterator can access end_ for debug purpose
template <typename, typename>
friend class IntrusiveListIterator;
// The node that is used to link the first and last elements of the list,
// in order to create a circular list.
// This node is the one returned by the end() function.
Node end_;
};
// Declares the trait configuration of mutable iterators.
template <typename _List>
struct MutableCfg {
typedef typename _List::pointer pointer;
typedef typename _List::reference reference;
typedef Node ListNode;
typedef typename _List::Hook Hook;
enum { kReverse = 0 };
};
// Declares the trait configuration of const iterators.
template <typename _List>
struct ConstCfg {
typedef typename _List::const_pointer pointer;
typedef typename _List::const_reference reference;
typedef const Node ListNode;
typedef const typename _List::Hook Hook;
enum { kReverse = 0 };
};
// Declares the trait configuration of mutable reverse iterators.
template <typename _List>
struct MutableReverseCfg {
typedef typename _List::pointer pointer;
typedef typename _List::reference reference;
typedef Node ListNode;
typedef typename _List::Hook Hook;
enum { kReverse = 1 };
};
// Declares the trait configuration of const reverse iterators.
template <typename _List>
struct ConstReverseCfg {
typedef typename _List::const_pointer pointer;
typedef typename _List::const_reference reference;
typedef const Node ListNode;
typedef const typename _List::Hook Hook;
enum { kReverse = 1 };
};
// Implements the IntrusiveList bidirectional iterator.
// The _Config template argument is a trait that configures the iterator for
// const/mutable and forward/reverse iteration orders.
template <typename _List, typename _Config>
class IntrusiveListIterator {
public:
// Defines iterator types as required by std::
typedef std::bidirectional_iterator_tag iterator_category;
typedef typename _List::value_type value_type;
typedef typename _List::difference_type difference_type;
typedef typename _Config::pointer pointer;
typedef typename _Config::reference reference;
typedef typename _Config::ListNode ListNode;
// Constructs an iterator pointing _node.
// _node can be nullptr which creates a default un-dereferencable iterator.
explicit IntrusiveListIterator(ListNode* _node = nullptr) : node_(_node) {
assert((!_node || _node->list_) &&
"Cannot build an iterator from a node that's unlinked");
}
~IntrusiveListIterator() {}
explicit IntrusiveListIterator(const IntrusiveListIterator& _it)
: node_(_it.node_) {}
// Constructs an iterator from an iterator with a different config, like
// forward/reverse, const/mutable variations.
// Disallowed conversions, like const to mutable, do not compile.
template <typename _OConfig>
IntrusiveListIterator(IntrusiveListIterator<_List, _OConfig> const& _it)
: node_(_it.node_) {}
// Compares two iterators with different configurations.
template <typename _OConfig>
bool operator==(IntrusiveListIterator<_List, _OConfig> const& _it) const {
assert(node_ && _it.node_ && node_->list_ == _it.node_->list_ &&
"List iterators incompatible");
return node_ == _it.node_;
}
// Compares two iterators with different configurations.
template <typename _OConfig>
bool operator!=(IntrusiveListIterator<_List, _OConfig> const& _it) const {
assert(node_ && _it.node_ && node_->list_ == _it.node_->list_ &&
"List iterators incompatible");
return node_ != _it.node_;
}
// Dereferences the object pointed by *this iterator.
// *this must be a valid iterator: initialized and not end().
reference operator*() const {
assert(node_ && !node_->debug_is_end_node() &&
"List iterator not dereferencable");
return static_cast<reference>(static_cast<typename _Config::Hook&>(*node_));
}
// Pre-increments iterator to the next object. The direction depends on
// iterator configuration (forward or reverse).
// *this must be a valid iterator: initialized and not end().
inline IntrusiveListIterator operator++() {
assert(node_ && !node_->debug_is_end_node() &&
"List iterator is already on list boundaries");
node_ = _Config::kReverse ? node_->prev_ : node_->next_;
return *this;
}
// Pre-decrements iterator to the next object. The direction depends on
// iterator configuration (forward or reverse).
// *this must be a valid iterator: initialized and not end().
inline IntrusiveListIterator operator--() {
assert(node_ &&
node_ != (_Config::kReverse ? node_->list_->end_.prev_
: node_->list_->end_.next_) &&
"List iterator is already on list boundaries");
node_ = _Config::kReverse ? node_->next_ : node_->prev_;
return *this;
}
// Post-increments iterator to the next object. The direction depends on
// iterator configuration (forward or reverse).
// *this must be a valid iterator: initialized and not end().
// DO NOT use the post-increment function if the returned value is ignored.
IntrusiveListIterator operator++(int) { // NOLINT unnamed argument
const IntrusiveListIterator old(*this);
++(*this);
return old;
}
// Post-decrements iterator to the next object. The direction depends on
// iterator configuration (forward or reverse).
// *this must be a valid iterator: initialized and not end().
// DO NOT use the post-decrement function if the returned value is ignored.
IntrusiveListIterator operator--(int) { // NOLINT unnamed argument
const IntrusiveListIterator old(*this);
--(*this);
return old;
}
private:
// Grants the right to IntrusiveList to access node() function.
template <typename, typename>
friend class ozz::containers::IntrusiveList;
// Get the node currently pointed by the iterator.
// *this iterator must be initialized, but can point a list end node.
Node& node() const {
assert(node_ && "Iterator isn't initialized");
return *node_;
}
// Other iterator specialization can access each other
template <typename, typename>
friend class IntrusiveListIterator;
// The list Node designated by *this iterator, which can be the end Node of a
// list. A default iterator has a nullptr designated Node.
ListNode* node_;
};
} // namespace internal
// IntrusiveList implementation.
template <typename _Ty, typename _Option>
class IntrusiveList : public internal::IntrusiveNodeList {
public:
class Hook : public internal::Node {
protected:
Hook() {}
~Hook() {
if (void(0), _Option::kLinkMode == LinkMode::kAuto && is_linked()) {
unlink();
}
assert((_Option::kLinkMode == LinkMode::kUnsafe || !is_linked()) &&
"Node is still linked");
}
private:
Hook(const Hook&);
void operator=(const Hook&);
};
// The type of te object T (aka the walue) stored in the list.
typedef _Ty value_type;
// Pointer to T.
typedef _Ty* pointer;
// Const pointer to T.
typedef _Ty const* const_pointer;
// Reference to T.
typedef _Ty& reference;
// Const reference to T.
typedef _Ty const& const_reference;
// A type that counts the number of elements in a list.
typedef internal::IntrusiveNodeList::size_type size_type;
// A type that provides the difference between two iterators.
typedef ptrdiff_t difference_type;
// Iterator used to iterate through a list;
typedef internal::IntrusiveListIterator<IntrusiveList,
internal::MutableCfg<IntrusiveList>>
iterator;
// Const iterator used to iterate through a list.
typedef internal::IntrusiveListIterator<IntrusiveList,
internal::ConstCfg<IntrusiveList>>
const_iterator;
// Iterator used to iterate backwards through a list.
typedef internal::IntrusiveListIterator<
IntrusiveList, internal::MutableReverseCfg<IntrusiveList>>
reverse_iterator;
// Const iterator used to iterate backwards through a list.
typedef internal::IntrusiveListIterator<
IntrusiveList, internal::ConstReverseCfg<IntrusiveList>>
const_reverse_iterator;
// Constructs an empty list.
IntrusiveList() {}
// Destructs a list that must be empty if link mode is not kUnsafe, otherwise
// an assertion is thrown.
~IntrusiveList() {
if (void(0), _Option::kLinkMode == LinkMode::kAuto) {
clear();
}
assert(_Option::kLinkMode == LinkMode::kUnsafe || empty());
}
// Inserts an unlinked element at the beginning of the list.
void push_front(reference _val) { link_front(static_cast<Hook*>(&_val)); }
// Inserts an unlinked element at the end of the list.
void push_back(reference _val) { link_back(static_cast<Hook*>(&_val)); }
// Removes the first element of the list and returns its reference.
// Compared to the std::list, this function can return the reference as
// pop_front does not delete the element.
// This function asserts if list is empty.
reference pop_front() {
assert(!empty() && "Invalid function on an empty list");
internal::Node& node = begin_node();
node.unlink();
return static_cast<reference>(static_cast<Hook&>(node));
}
// Removes the last element of the list and returns its reference.
// Compared to the std::list, this function can return the reference as
// pop_back does not delete the element.
// This function asserts if list is empty.
reference pop_back() {
assert(!empty() && "Invalid function on an empty list");
internal::Node& node = last_node();
node.unlink();
return static_cast<reference>(static_cast<Hook&>(node));
}
// Returns the a reference to the first element.
// This function asserts if list is empty.
reference front() {
assert(!empty() && "Invalid function on an empty list");
return static_cast<reference>(static_cast<Hook&>(begin_node()));
}
// Returns the a const reference to the first element.
// This function asserts if list is empty.
const_reference front() const {
assert(!empty() && "Invalid function on an empty list");
return static_cast<const_reference>(static_cast<const Hook&>(begin_node()));
}
// Returns the a reference to the last element.
// This function asserts if list is empty.
reference back() {
assert(!empty() && "Invalid function on an empty list");
return static_cast<reference>(static_cast<Hook&>(last_node()));
}
// Returns the a const reference to the last element.
// This function asserts if list is empty.
const_reference back() const {
assert(!empty() && "Invalid function on an empty list");
return static_cast<const_reference>(static_cast<const Hook&>(last_node()));
}
// Returns an iterator pointing to the beginning of the list.
iterator begin() { return iterator(&begin_node()); }
// Returns a const_iterator pointing to the beginning of the list.
const_iterator begin() const { return const_iterator(&begin_node()); }
// Returns an iterator pointing to the end of the list.
// The returned iterator can not be dereferenced.
iterator end() { return iterator(&end_node()); }
// Returns a const_iterator pointing to the end of the list.
// The returned iterator can not be dereferenced.
const_iterator end() const { return const_iterator(&end_node()); }
// Returns a reverse_iterator pointing to the beginning of the reversed list.
// The returned iterator can not be dereferenced.
reverse_iterator rbegin() { return reverse_iterator(&last_node()); }
// Returns a const_reverse_iterator pointing to the beginning of the reversed
// list. The returned iterator can not be dereferenced.
const_reverse_iterator rbegin() const {
return const_reverse_iterator(&last_node());
}
// Returns a reverse_iterator pointing to the end of the reversed list.
reverse_iterator rend() { return reverse_iterator(&end_node()); }
// Returns a const_reverse_iterator pointing to the end of the reversed list.
const_reverse_iterator rend() const {
return const_reverse_iterator(&end_node());
}
// Removes _val element from the list with a O(1) complexity.
// The relative order of elements is unchanged, and iterators to elements
// that are not removed remain valid.
// This functions asserts if _val is not element of the list.
void remove(reference _val) {
Hook& hook = static_cast<Hook&>(_val);
assert(hook.debug_is_linked_in(*this) && "The node is linked by this list");
hook.unlink();
}
// Removes all elements such that _pred() is true, with an O(n) complexity.
// The relative order of elements that are not removed is unchanged.
// Iterators to elements that are not removed remain valid.
template <typename _Pred>
void remove_if(_Pred _pred) {
_remove_if(UnnaryPredFw<_Pred>(_pred));
}
// Erases element at _where and returns an iterator that designates the first
// element remaining beyond the element removed.
// _where must be a valid iterator.
// ::remove should be preferred as it avoid creating and returning an
// iterator.
iterator erase(iterator _where) {
internal::Node& where_node = _where.node();
assert(where_node.debug_is_linked_in(*this) &&
"The node is linked by this list");
++_where; // Offset the iterator to return before modifying the list
where_node.unlink();
return _where;
}
// Erases elements in range [_begin, _end[, and returns an iterator that
// designates the first element remaining beyond the element removed.
// _first and _end iterators must be a valid.
iterator erase(iterator const& _begin, iterator const& _end) {
internal::Node& begin_node = _begin.node();
internal::Node& end_node = _end.node();
_erase(&begin_node, &end_node);
return _end; // _end is still a valid iterator
}
// Insert _val before _where.
// Compared to std::list, this function does not return an iterator as
// IntrusiveLisrt iterators can be constructed in O(1) directly from _val.
void insert(iterator const& _where, reference _val) {
// Dereference iterator to ensure its validity
_insert(static_cast<Hook*>(&_val), &_where.node());
}
// All of the elements of _list are inserted before _where and removed from
// _list.
// This function is constant time.
void splice(iterator _where,
IntrusiveList& _list) { // NOLINT conforms with std::list API
if (this != &_list && !_list.empty()) {
_splice(&_where.node(), &_list.begin_node(), &_list.end_node());
}
}
// The elements _what from _list is inserted before _where and removed from
// _list.
// This function is constant time.
void splice(iterator _where,
IntrusiveList& _list, // NOLINT conforms with std::list API
iterator _what) {
reference val = static_cast<reference>(static_cast<Hook&>(_what.node()));
_list.remove(val);
insert(_where, val);
}
// All of the elements in the range [_begin, _end[ are inserted before
// _where and removed from _list.
// This function is constant time.
void splice(iterator _where,
IntrusiveList& _list, // NOLINT conforms with std::list API
iterator _begin, iterator _end) {
internal::Node* where_node = &_where.node();
internal::Node* begin_node = &_begin.node();
internal::Node* end_node = &_end.node();
if (begin_node != end_node && (this != &_list || where_node != end_node)) {
_splice(where_node, begin_node, end_node);
}
}
// Removes all of _list's elements and inserts them in order into *this.
// _Pred must be a comparison function that induces a strict weak ordering
// (as defined in the LessThan Comparable requirements) on objects of type
// _Ty, and both *this and _list must be sorted according to that ordering.
// The merge is stable; that is, if an element from *this is equivalent to
// one from x, then the element from *this will precede the one from x.
// This function is linear time and performs at most:
// size() + _list.size() - 1 applications of _Pred.
template <typename _Pred>
void merge(IntrusiveList& _list,
_Pred _pred) { // NOLINT conforms with std::list API
_merge(&_list, BinaryPredFw<_Pred>(_pred));
}
// Removes all of _list's elements and inserts them in order into *this.
// Both *this and x must be sorted according to operator<.
// The merge is stable; that is, if an element from *this is equivalent to
// one from x, then the element from *this will precede the one from x.
// All iterators to elements in *this and x remain valid.
// This function is linear time and performs at most
// size() + _list.size() - 1 comparisons.
void merge(IntrusiveList& _list) { // NOLINT conforms with std::list API
_merge(&_list, LessTester());
}
// Sorts the list *this according to Comp.
// Comp must be a comparison function that induces a strict weak ordering
// (as defined in the LessThan Comparable requirements on objects of type T.
// The sort is stable, that is, the relative order of equivalent elements is
// preserved.
// The number of comparisons is approximately n.log(n).
template <class _Pred>
void sort(_Pred _pred) {
_sort(BinaryPredFw<_Pred>(_pred));
}
// Sorts *this according to operator<.
// The sort is stable, that is, the relative order of equivalent elements is
// preserved.
// The number of comparisons is approximately n.log(n).
void sort() { _sort(LessTester()); }
// Tests two lists for equality according to operator==.
bool operator==(IntrusiveList const& _list) const {
return _is_equal(_list, EqualTester());
}
// Tests two lists for inequality according to operator==.
bool operator!=(IntrusiveList const& _list) const {
return !(*this == _list);
}
// Lexicographical "less" comparison according to operator<.
bool operator<(IntrusiveList const& _list) const {
return _is_less(_list, LessTester());
}
// Lexicographical "less or equal" comparison according to operator<.
bool operator<=(IntrusiveList const& _list) const { return !(_list < *this); }
// Lexicographical "greater" comparison according to operator<.
bool operator>(IntrusiveList const& _list) const { return _list < *this; }
// Lexicographical "greater or equal" comparison according to operator<.
bool operator>=(IntrusiveList const& _list) const { return !(*this < _list); }
private:
// Internal function that tests the order of the list according to _Pred.
template <typename _Pred>
bool is_ordered(_Pred _pred) const {
return _is_ordered(BinaryPredFw<_Pred>(_pred));
}
// Helper binary functor that converts the nodes in argument to
// a value_type that are given as arguments to _pred.
template <typename _Pred>
struct BinaryPredFw {
explicit BinaryPredFw(_Pred _pred) : pred_(_pred) {}
bool operator()(const internal::Node& _left, const internal::Node& _right) {
const_reference left =
static_cast<const_reference>(static_cast<const Hook&>(_left));
const_reference right =
static_cast<const_reference>(static_cast<const Hook&>(_right));
return pred_(left, right);
}
_Pred pred_;
};
// Helper unary functor that converts the node in argument to
// a value_type that is given as an argument to _pred.
template <typename _Pred>
struct UnnaryPredFw {
explicit UnnaryPredFw(_Pred _pred) : pred_(_pred) {}
bool operator()(const internal::Node& _node) {
const_reference val =
static_cast<const_reference>(static_cast<const Hook&>(_node));
return pred_(val);
}
_Pred pred_;
};
// Compares 2 nodes according to the value_type operator ==.
struct EqualTester {
bool operator()(const internal::Node& _left, const internal::Node& _right) {
const_reference left =
static_cast<const_reference>(static_cast<const Hook&>(_left));
const_reference right =
static_cast<const_reference>(static_cast<const Hook&>(_right));
return left == right;
}
};
// Compares 2 nodes according to the value_type operator <.
struct LessTester {
bool operator()(const internal::Node& _left, const internal::Node& _right) {
const_reference left =
static_cast<const_reference>(static_cast<const Hook&>(_left));
const_reference right =
static_cast<const_reference>(static_cast<const Hook&>(_right));
return left < right;
}
};
// Disallow copy and assignment
IntrusiveList(const IntrusiveList&);
void operator=(const IntrusiveList&);
};
// Enters the internal namespace that encloses private implementation details.
namespace internal {
// Connect the next and previous nodes together, resets internal linked state.
inline void Node::unlink() {
assert(is_linked() && "This node is not linked");
assert(!debug_is_end_node() && "The end_ node cannot be unlinked");
next_->prev_ = prev_; // Reconnect prev and next nodes
prev_->next_ = next_;
// Reset this node to the NOT linked state
prev_ = this;
next_ = this;
#ifndef NDEBUG
list_ = nullptr;
#endif // NDEBUG
}
inline void Node::insert(Node* _where) {
assert(!is_linked() && _where->list_ && // Cannot test _where->is_linked
// as end_ would return false.
"*this node must be unlinked and _node must be linked");
assert(!debug_is_end_node() && "The end_ node cannot be linked");
prev_ = _where->prev_; // Connect the previous of *this node
_where->prev_->next_ = this;
next_ = _where; // Connect the next of *this node
_where->prev_ = this;
#ifndef NDEBUG
list_ = _where->list_;
#endif // NDEBUG
}
// Iterates through all elements to unlink them from the list.
inline void IntrusiveNodeList::clear() {
while (end_.next_ != &end_) {
end_.next_->unlink();
}
}
// Iterates through all elements in range [_begin, _end[ to unlink them from
// the list.
inline void IntrusiveNodeList::_erase(Node* _begin, Node* _end) {
assert(debug_is_range_valid(*_begin, *_end) && "Invalid iterator range");
while (_begin != _end) {
internal::Node* next_node = _begin->next_;
_begin->unlink();
_begin = next_node;
}
}
// Loops and inserts the first element in front of the original last one,
// until the last one is reached.
inline void IntrusiveNodeList::reverse() {
Node* const last = end_.prev_;
while (end_.next_ != last) {
Node* node = end_.next_;
node->unlink();
node->insert(last->next_);
}
}
// Loops and counts the number of elements, excluding the end_ node.
inline size_t IntrusiveNodeList::size() const {
size_t size = 0;
for (const Node *node = end_.next_; node != &end_;
node = node->next_, ++size) {
}
return size;
}
// Takes advantage of the intrusive property to swap end_ nodes without
// iterating through all nodes.
// This makes this implementation O(1) rather than O(n).
// In debug build though, every node between of the two lists must be traversed
// to reset their list_ member.
inline void IntrusiveNodeList::swap(IntrusiveNodeList& _list) {
// Don't use std::swap to avoid including <algorithm> in a h file.
// Also std::swap does a branch for nothing when dealing with pointers.
#define _SWAP_PTR(_a, _b) \
{ \
Node* temp = _a; \
_a = _b; \
_b = temp; \
}
_SWAP_PTR(_list.end_.prev_->next_, end_.prev_->next_);
_SWAP_PTR(_list.end_.prev_, end_.prev_);
_SWAP_PTR(_list.end_.next_->prev_, end_.next_->prev_);
_SWAP_PTR(_list.end_.next_, end_.next_);
#undef _SWAP_PTR
#ifndef NDEBUG
// Reset node internal list_ pointer
Node* node = end_.next_;
while (node != &end_) {
node->list_ = this;
node = node->next_;
}
node = _list.end_.next_;
while (node != &_list.end_) {
node->list_ = &_list;
node = node->next_;
}
#endif // NDEBUG
}
// Takes advantage of the intrusive property to splice nodes without iterating.
// This makes this implementation O(1) rather than O(n).
// In debug build though, every node between _first and _end must be traversed
// to reset their list_ member.
inline void IntrusiveNodeList::_splice(Node* _where, Node* _first, Node* _end) {
assert(_where->list_ == this && "_where is not a member of *this list");
assert(_first->list_ && _first->list_->debug_is_range_valid(*_first, *_end) &&
"Invalid iterator range");
assert(_first != _end);
// Keep a pointer to the last node as _end->prev_ is modified early
Node* last = _end->prev_;
// De-link _first and last from its original list
_first->prev_->next_ = _end;
_end->prev_ = _first->prev_;
// Re-link _first
_first->prev_ = _where->prev_;
_where->prev_->next_ = _first;
// Re-link _end
_where->prev_ = last;
last->next_ = _where;
#ifndef NDEBUG
// Reset node internal list_ pointer, for all the inserted nodes
Node* node = _first;
while (node != _where) {
node->list_ = this;
node = node->next_;
}
#endif // NDEBUG
}
template <typename _Pred>
inline bool IntrusiveNodeList::_is_equal(IntrusiveNodeList const& _list,
_Pred _pred) const {
const internal::Node* left_node = end_.next_;
const internal::Node* right_node = _list.end_.next_;
while (left_node != &end_ && right_node != &_list.end_) {
if (!_pred(*left_node, *right_node)) {
return false;
}
left_node = left_node->next_;
right_node = right_node->next_;
}
// Finally returns true if the two lists have the same sizes
return left_node == &end_ && right_node == &_list.end_;
}
template <typename _Pred>
inline bool IntrusiveNodeList::_is_less(IntrusiveNodeList const& _list,
_Pred _pred) const {
const internal::Node* left_node = end_.next_;
const internal::Node* right_node = _list.end_.next_;
while (left_node != &end_ && right_node != &_list.end_) {
if (_pred(*left_node, *right_node)) {
return true;
} else if (_pred(*right_node, *left_node)) {
return false;
}
left_node = left_node->next_;
right_node = right_node->next_;
}
// Finally returns true if "this" list has less elements
return left_node == &end_ && right_node != &_list.end_;
}
// Tries to splice more than one element at a time, as the intrusive policy
// allow splicing of n consecutive nodes in O(1) complexity.
template <typename _Pred>
inline void IntrusiveNodeList::_merge(IntrusiveNodeList* _list, _Pred _pred) {
assert(_is_ordered(_pred) && "This list must be ordered");
if (this == _list) {
return;
}
assert(_list->_is_ordered(_pred) && "The list in argument must be ordered");
internal::Node* node = end_.next_;
internal::Node* to__insertbegin = _list->end_.next_;
while (node != &end_ && to__insertbegin != &_list->end_) {
if (_pred(*node, *to__insertbegin)) {
node = node->next_;
} else { // Try to find consecutive nodes satisfying _pred
internal::Node* to__insertend = to__insertbegin->next_;
while (to__insertend != &_list->end_) {
if (_pred(*node, *to__insertend)) {
break;
}
to__insertend = to__insertend->next_;
}
_splice(node, to__insertbegin, to__insertend);
to__insertbegin = to__insertend;
}
}
if (to__insertbegin != &_list->end_) { // Appends the rest of _list
_splice(&end_, to__insertbegin, &_list->end_);
}
}
// Iterate and test predicate _pred for every node.
template <typename _Pred>
inline void IntrusiveNodeList::_remove_if(_Pred _pred) {
internal::Node* node = end_.next_;
while (node != &end_) {
internal::Node* next_node = node->next_;
if (_pred(*node)) {
node->unlink();
}
node = next_node;
}
}
// Bin sort algorithm, takes advantage of O(1) complexity of swap and splice.
template <typename _Pred>
inline void IntrusiveNodeList::_sort(_Pred _pred) {
// It's worth sorting if there is more than one element
if (end_.next_->next_ == &end_) {
return;
}
const int kMaxBins = 25;
IntrusiveNodeList bin_lists[kMaxBins + 1];
IntrusiveNodeList temp_list;
int used_bins = 0;
while (!empty()) {
// Inserts the front node at the front of temp_list
internal::Node* node = end_.next_;
node->unlink();
temp_list.link_front(node);
int bin = 0;
for (; bin < used_bins && !bin_lists[bin].empty(); ++bin) {
// Merges into ever larger bins
bin_lists[bin]._merge(&temp_list, _pred);
bin_lists[bin].swap(temp_list);
}
if (bin == kMaxBins) { // No more bin, merge in the last one
bin_lists[kMaxBins - 1]._merge(&temp_list, _pred);
} else { // Spills to new bin, while they last
bin_lists[bin].swap(temp_list);
if (bin == used_bins) {
used_bins++;
}
}
}
for (int bin = 1; bin < used_bins; ++bin) { // Merge up every bin
bin_lists[bin]._merge(&bin_lists[bin - 1], _pred);
}
if (used_bins != 0) { // Result is in last bin
IntrusiveNodeList& last_bin = bin_lists[used_bins - 1];
_splice(end_.next_, last_bin.end_.next_, &last_bin.end_);
}
}
// Loops and tests if _Pred is true for all nodes
template <typename _Pred>
inline bool IntrusiveNodeList::_is_ordered(_Pred _pred) const {
const internal::Node* next_node = end_.next_->next_;
while (next_node != &end_) {
if (!_pred(*next_node->prev_, *next_node)) {
return false;
}
next_node = next_node->next_;
}
return true;
}
} // namespace internal
} // namespace containers
} // namespace ozz
// Specialization of the std::swap algorithm for the IntusiveList class.
// Does not need to be implemented in std namespace thanks to ADL.
template <typename _Ty, typename _Option>
inline void swap(ozz::containers::IntrusiveList<_Ty, _Option>&
_left, // NOLINT Don't want to #include <algorithm>
ozz::containers::IntrusiveList<_Ty, _Option>& _right) {
_left.swap(_right);
}
// Undefines local macros
#endif // OZZ_OZZ_BASE_CONTAINERS_INTRUSIVE_LIST_H_