mirror of https://github.com/axmolengine/axmol.git
370 lines
10 KiB
C
370 lines
10 KiB
C
|
/*
|
||
|
* 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/b2_settings.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
|
||
|
|