1129 lines
41 KiB
C++
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_
|