123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369 |
- /*
- * Copyright (c) 2014 Google, Inc.
- *
- * This software is provided 'as-is', without any express or implied
- * warranty. In no event will the authors be held liable for any damages
- * arising from the use of this software.
- * Permission is granted to anyone to use this software for any purpose,
- * including commercial applications, and to alter it and redistribute it
- * freely, subject to the following restrictions:
- * 1. The origin of this software must not be misrepresented; you must not
- * claim that you wrote the original software. If you use this software
- * in a product, an acknowledgment in the product documentation would be
- * appreciated but is not required.
- * 2. Altered source versions must be plainly marked as such, and must not be
- * misrepresented as being the original software.
- * 3. This notice may not be removed or altered from any source distribution.
- */
- #ifndef B2_INTRUSIVE_LIST
- #define B2_INTRUSIVE_LIST
- #include <Box2D/Common/b2Settings.h>
- // Whether to enable b2IntrusiveList::ValidateList().
- // Be careful when enabling this since this changes the size of
- // b2IntrusiveListNode so make sure *all* projects that include Box2D.h
- // also define this value in the same way to avoid data corruption.
- #ifndef B2_INTRUSIVE_LIST_VALIDATE
- #define B2_INTRUSIVE_LIST_VALIDATE 0
- #endif // B2_INTRUSIVE_LIST_VALIDATE
- /// b2IntrusiveListNode is used to implement an intrusive doubly-linked
- /// list.
- ///
- /// For example:
- ///
- /// class MyClass {
- /// public:
- /// MyClass(const char *msg) : m_msg(msg) {}
- /// const char* GetMessage() const { return m_msg; }
- /// B2_INTRUSIVE_LIST_GET_NODE(m_node);
- /// B2_INTRUSIVE_LIST_NODE_GET_CLASS(MyClass, m_node);
- /// private:
- /// b2IntrusiveListNode m_node;
- /// const char *m_msg;
- /// };
- ///
- /// int main(int argc, char *argv[]) {
- /// b2IntrusiveListNode list; // NOTE: type is NOT MyClass
- /// MyClass a("this");
- /// MyClass b("is");
- /// MyClass c("a");
- /// MyClass d("test");
- /// list.InsertBefore(a.GetListNode());
- /// list.InsertBefore(b.GetListNode());
- /// list.InsertBefore(c.GetListNode());
- /// list.InsertBefore(d.GetListNode());
- /// for (b2IntrusiveListNode* node = list.GetNext();
- /// node != list.GetTerminator(); node = node->GetNext()) {
- /// MyClass *cls = MyClass::GetInstanceFromListNode(node);
- /// printf("%s\n", cls->GetMessage());
- /// }
- /// return 0;
- /// }
- class b2IntrusiveListNode
- {
- public:
- /// Initialize the node.
- b2IntrusiveListNode()
- {
- Initialize();
- #if B2_INTRUSIVE_LIST_VALIDATE
- m_magic = k_magic;
- #endif // B2_INTRUSIVE_LIST_VALIDATE
- }
- /// If the node is in a list, remove it from the list.
- ~b2IntrusiveListNode()
- {
- Remove();
- #if B2_INTRUSIVE_LIST_VALIDATE
- m_magic = 0;
- #endif // B2_INTRUSIVE_LIST_VALIDATE
- }
- /// Insert this node after the specified node.
- void InsertAfter(b2IntrusiveListNode* const node)
- {
- b2Assert(!node->InList());
- node->m_next = m_next;
- node->m_prev = this;
- m_next->m_prev = node;
- m_next = node;
- }
- /// Insert this node before the specified node.
- void InsertBefore(b2IntrusiveListNode* const node)
- {
- b2Assert(!node->InList());
- node->m_next = this;
- node->m_prev = m_prev;
- m_prev->m_next = node;
- m_prev = node;
- }
- /// Get the terminator of the list.
- const b2IntrusiveListNode* GetTerminator() const
- {
- return this;
- }
- /// Remove this node from the list it's currently in.
- b2IntrusiveListNode* Remove()
- {
- m_prev->m_next = m_next;
- m_next->m_prev = m_prev;
- Initialize();
- return this;
- }
- /// Determine whether this list is empty or the node isn't in a list.
- bool IsEmpty() const
- {
- return GetNext() == this;
- }
- /// Determine whether this node is in a list or the list contains nodes.
- bool InList() const
- {
- return !IsEmpty();
- }
- /// Calculate the length of the list.
- uint32 GetLength() const
- {
- uint32 length = 0;
- const b2IntrusiveListNode * const terminator = GetTerminator();
- for (const b2IntrusiveListNode* node = GetNext();
- node != terminator; node = node->GetNext())
- {
- length++;
- }
- return length;
- }
- /// Get the next node in the list.
- b2IntrusiveListNode* GetNext() const
- {
- return m_next;
- }
- /// Get the previous node in the list.
- b2IntrusiveListNode* GetPrevious() const
- {
- return m_prev;
- }
- /// If B2_INTRUSIVE_LIST_VALIDATE is 1 perform a very rough validation
- /// of all nodes in the list.
- bool ValidateList() const
- {
- #if B2_INTRUSIVE_LIST_VALIDATE
- if (m_magic != k_magic) return false;
- const b2IntrusiveListNode * const terminator = GetTerminator();
- for (b2IntrusiveListNode *node = GetNext(); node != terminator;
- node = node->GetNext()) {
- if (node->m_magic != k_magic) return false;
- }
- #endif // B2_INTRUSIVE_LIST_VALIDATE
- return true;
- }
- /// Determine whether the specified node is present in this list.
- bool FindNodeInList(b2IntrusiveListNode* const nodeToFind) const
- {
- const b2IntrusiveListNode * const terminator = GetTerminator();
- for (b2IntrusiveListNode *node = GetNext(); node != terminator;
- node = node->GetNext())
- {
- if (nodeToFind == node) return true;
- }
- return false;
- }
- private:
- /// Initialize the list node.
- void Initialize()
- {
- m_next = this;
- m_prev = this;
- }
- private:
- #if B2_INTRUSIVE_LIST_VALIDATE
- uint32 m_magic;
- #endif // B2_INTRUSIVE_LIST_VALIDATE
- /// The next node in the list.
- b2IntrusiveListNode *m_prev;
- /// The previous node in the list.
- b2IntrusiveListNode *m_next;
- private:
- #if B2_INTRUSIVE_LIST_VALIDATE
- static const uint32 k_magic = 0x7157ac01;
- #endif // B2_INTRUSIVE_LIST_VALIDATE
- };
- /// Declares the member function GetListNode() of Class to retrieve a pointer
- /// to NodeMemberName.
- /// See #B2_INTRUSIVE_LIST_NODE_GET_CLASS_ACCESSOR()
- #define B2_INTRUSIVE_LIST_GET_NODE(NodeMemberName) \
- b2IntrusiveListNode* GetListNode() { return &NodeMemberName; } \
- const b2IntrusiveListNode* GetListNode() const { return &NodeMemberName; }
- /// Declares the member function FunctionName of Class to retrieve a pointer
- /// to a Class instance from a list node pointer. NodeMemberName references
- /// the name of the b2IntrusiveListNode member of Class.
- #define B2_INTRUSIVE_LIST_NODE_GET_CLASS_ACCESSOR( \
- Class, NodeMemberName, FunctionName) \
- static Class* FunctionName(b2IntrusiveListNode *node) \
- { \
- Class *cls = NULL; \
- /* This effectively performs offsetof(Class, NodeMemberName) */ \
- /* which ends up in the undefined behavior realm of C++ but in */ \
- /* practice this works with most compilers. */ \
- return reinterpret_cast<Class*>((uint8*)(node) - \
- (uint8*)(&cls->NodeMemberName)); \
- } \
- \
- static const Class* FunctionName(const b2IntrusiveListNode *node) \
- { \
- return FunctionName(const_cast<b2IntrusiveListNode*>(node)); \
- }
- /// Declares the member function GetInstanceFromListNode() of Class to retrieve
- /// a pointer to a Class instance from a list node pointer. NodeMemberName
- /// reference the name of the b2IntrusiveListNode member of Class.
- #define B2_INTRUSIVE_LIST_NODE_GET_CLASS(Class, NodeMemberName) \
- B2_INTRUSIVE_LIST_NODE_GET_CLASS_ACCESSOR(Class, NodeMemberName, \
- GetInstanceFromListNode)
- /// b2TypedIntrusiveListNode which supports inserting an object into a single
- /// doubly linked list. For objects that need to be inserted in multiple
- /// doubly linked lists, use b2IntrusiveListNode.
- ///
- /// For example:
- ///
- /// class IntegerItem : public b2TypedIntrusiveListNode<IntegerItem>
- /// {
- /// public:
- /// IntegerItem(int32 value) : m_value(value) { }
- /// ~IntegerItem() { }
- /// int32 GetValue() const { return m_value; }
- /// private:
- /// int32 m_value;
- /// };
- ///
- /// int main(int argc, const char *arvg[]) {
- /// b2TypedIntrusiveListNode<IntegerItem> list;
- /// IntegerItem a(1);
- /// IntegerItem b(2);
- /// IntegerItem c(3);
- /// list.InsertBefore(&a);
- /// list.InsertBefore(&b);
- /// list.InsertBefore(&c);
- /// for (IntegerItem* item = list.GetNext();
- /// item != list.GetTerminator(); item = item->GetNext())
- /// {
- /// printf("%d\n", item->GetValue());
- /// }
- /// }
- template<typename T>
- class b2TypedIntrusiveListNode
- {
- public:
- b2TypedIntrusiveListNode() { }
- ~b2TypedIntrusiveListNode() { }
- /// Insert this object after the specified object.
- void InsertAfter(T* const obj)
- {
- b2Assert(obj);
- GetListNode()->InsertAfter(obj->GetListNode());
- }
- /// Insert this object before the specified object.
- void InsertBefore(T* const obj)
- {
- b2Assert(obj);
- GetListNode()->InsertBefore(obj->GetListNode());
- }
- /// Get the next object in the list.
- /// Check against GetTerminator() before deferencing the object.
- T* GetNext() const
- {
- return GetInstanceFromListNode(GetListNode()->GetNext());
- }
- /// Get the previous object in the list.
- /// Check against GetTerminator() before deferencing the object.
- T* GetPrevious() const
- {
- return GetInstanceFromListNode(GetListNode()->GetPrevious());
- }
- /// Get the terminator of the list.
- /// This should not be dereferenced as it is a pointer to
- /// b2TypedIntrusiveListNode<T> *not* T.
- T* GetTerminator() const
- {
- return (T*)GetListNode();
- }
- /// Remove this object from the list it's currently in.
- T* Remove()
- {
- GetListNode()->Remove();
- return GetInstanceFromListNode(GetListNode());
- }
- /// Determine whether this object is in a list.
- bool InList() const
- {
- return GetListNode()->InList();
- }
- // Determine whether this list is empty.
- bool IsEmpty() const
- {
- return GetListNode()->IsEmpty();
- }
- /// Calculate the length of the list.
- uint32 GetLength() const
- {
- return GetListNode()->GetLength();
- }
- B2_INTRUSIVE_LIST_GET_NODE(m_node);
- private:
- // Node within an intrusive list.
- b2IntrusiveListNode m_node;
- public:
- /// Get a pointer to the instance of T that contains "node".
- static T* GetInstanceFromListNode(b2IntrusiveListNode* const node)
- {
- b2Assert(node);
- // Calculate the pointer to T from the offset.
- return (T*)((uint8*)node - GetNodeOffset(node));
- }
- private:
- // Get the offset of m_node within this class.
- static int32 GetNodeOffset(b2IntrusiveListNode* const node)
- {
- b2Assert(node);
- // Perform some type punning to calculate the offset of m_node in T.
- // WARNING: This could result in undefined behavior with some C++
- // compilers.
- T* obj = (T*)node;
- int32 nodeOffset = (int32)((uint8*)&obj->m_node - (uint8*)obj);
- return nodeOffset;
- }
- };
- #endif // B2_INTRUSIVE_LIST
|