summaryrefslogtreecommitdiff
path: root/Runtime/Misc/UserList.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Runtime/Misc/UserList.cpp')
-rw-r--r--Runtime/Misc/UserList.cpp195
1 files changed, 195 insertions, 0 deletions
diff --git a/Runtime/Misc/UserList.cpp b/Runtime/Misc/UserList.cpp
new file mode 100644
index 0000000..ca2783e
--- /dev/null
+++ b/Runtime/Misc/UserList.cpp
@@ -0,0 +1,195 @@
+#include "UnityPrefix.h"
+#include "UserList.h"
+#include "Runtime/Threads/Thread.h"
+#include "Runtime/BaseClasses/MessageIdentifier.h"
+#include "Runtime/BaseClasses/GameObject.h"
+
+void UserListNode::Clear ()
+{
+ if (IsConnected())
+ {
+ UserList* other = static_cast<UserList*>(m_Entry.other);
+ other->RemoveIndex(m_Entry.indexInOther);
+ m_Entry = Entry();
+ }
+}
+
+void UserList::Clear ()
+{
+ for (int index=0; index<m_Entries.size(); index++)
+ {
+ Entry& user = m_Entries[index];
+ if (user.indexInOther == -1)
+ {
+ // Other is a node
+ UserListNode* other = static_cast<UserListNode*>(user.other);
+ DebugAssert(other->m_Entry.other == this);
+ DebugAssert(other->m_Entry.indexInOther == index);
+ other->m_Entry = Entry();
+ }
+ else
+ {
+ // Other is a another list
+ UserList* other = static_cast<UserList*>(user.other);
+ DebugAssert(other->m_Entries[user.indexInOther].other == this);
+ DebugAssert(other->m_Entries[user.indexInOther].indexInOther == index);
+ other->RemoveIndex(user.indexInOther);
+ }
+ }
+ m_Entries.clear();
+}
+
+void UserList::Reserve (size_t size)
+{
+ m_Entries.reserve(size);
+}
+
+void UserList::AddUser (UserListNode& other)
+{
+ other.Clear();
+ other.m_Entry.other = this;
+ other.m_Entry.indexInOther = m_Entries.size();
+ Entry& user = m_Entries.push_back();
+ user.other = &other;
+ user.indexInOther = -1;
+}
+
+void UserList::AddUser (UserList& other)
+{
+ DebugAssert(&other != this);
+ int index = m_Entries.size();
+ int indexInOther = other.m_Entries.size();
+ Entry& user = m_Entries.push_back();
+ user.other = &other;
+ user.indexInOther = indexInOther;
+ Entry& otherUser = other.m_Entries.push_back();
+ otherUser.other = this;
+ otherUser.indexInOther = index;
+}
+
+void UserList::SendMessage (const MessageIdentifier& msg)
+{
+ ASSERT_RUNNING_ON_MAIN_THREAD
+
+ MessageData data;
+ // Traverse list backwards, makes it easier if an element removes itself
+ int index = (int)m_Entries.size() - 1;
+ while (index >= 0)
+ {
+ UserListBase* other = m_Entries[index].other;
+#if DEBUGMODE
+ // Verify that the link back is correct
+ GetEntryInOther(index);
+#endif
+ SendMessageDirect(*other->GetTarget(), msg, data);
+ // Make sure index is within range
+ index = std::min(index, (int)m_Entries.size());
+ index--;
+ }
+}
+
+UserList::Entry& UserList::GetEntryInOther (int index)
+{
+ Entry& user = m_Entries[index];
+ Entry* entryInOther;
+ if (user.indexInOther == -1)
+ {
+ // Other is a node
+ UserListNode* other = static_cast<UserListNode*>(user.other);
+ entryInOther = &other->m_Entry;
+ }
+ else
+ {
+ // Other is a another list
+ UserList* other = static_cast<UserList*>(user.other);
+ entryInOther = &other->m_Entries[user.indexInOther];
+ }
+ DebugAssert(entryInOther->other == this);
+ DebugAssert(entryInOther->indexInOther == index);
+ return *entryInOther;
+}
+
+void UserList::RemoveIndex (int index)
+{
+ int lastIndex = m_Entries.size() - 1;
+ if (index != lastIndex)
+ {
+ // Move last entry to index
+ Entry& lastUser = m_Entries[lastIndex];
+ m_Entries[index] = lastUser;
+ Entry& entryInOther = GetEntryInOther(lastIndex);
+ entryInOther.indexInOther = index;
+ }
+ m_Entries.pop_back();
+}
+
+#if ENABLE_UNIT_TESTS
+
+#include "External/UnitTest++/src/UnitTest++.h"
+
+class TestUserList : public UserList
+{
+public:
+ TestUserList() : UserList(NULL) {}
+};
+
+class TestUserListNode : public UserListNode
+{
+public:
+ TestUserListNode() : UserListNode(NULL) {}
+};
+
+SUITE (UserListTests)
+{
+TEST(UserList_BasicUserList)
+{
+ const int kNumA = 10;
+ const int kNumB = 20;
+ const int kNumC = 30;
+ TestUserList listsA[kNumA];
+ TestUserList listsB[kNumB];
+ TestUserList listsC[kNumC];
+ TestUserListNode nodesD[kNumC];
+ for (int c = 0; c < kNumC; c++)
+ if ((c % 3) == 1)
+ listsC[c].AddUser(nodesD[c]);
+ for (int a = 0; a < kNumA; a++)
+ for (int b = 0; b < kNumB; b++)
+ if (((a + b) % 3) == 0)
+ listsA[a].AddUser(listsB[b]);
+ for (int b = 0; b < kNumB; b++)
+ for (int c = 0; c < kNumC; c++)
+ if (((b + c) % 5) == 0)
+ listsB[b].AddUser(listsC[c]);
+ for (int c = 0; c < kNumC; c++)
+ if ((c % 3) == 2)
+ listsC[c].AddUser(nodesD[c]);
+ for (int a = kNumA - 1; a >= 0; a--)
+ if ((a % 2) == 0)
+ listsA[a].Clear();
+ for (int c = 0; c < kNumC; c++)
+ if ((c % 4) == 0)
+ nodesD[c].Clear();
+ for (int b = 0; b < kNumB; b++)
+ listsB[b].Clear();
+ for (int a = 0; a < kNumA; a++)
+ {
+ CHECK_EQUAL (0, listsA[a].GetSize());
+ }
+ for (int c = 0; c < kNumC; c++)
+ {
+ if ((c % 3) != 0 && (c % 4) != 0)
+ {
+ CHECK_EQUAL (1, listsC[c].GetSize());
+ CHECK_EQUAL (true, nodesD[c].IsConnected());
+ }
+ else
+ {
+ CHECK_EQUAL (0, listsC[c].GetSize());
+ CHECK_EQUAL (false, nodesD[c].IsConnected());
+ }
+ }
+}
+}
+
+#endif