diff --git a/storm/List.hpp b/storm/List.hpp new file mode 100644 index 0000000..2145523 --- /dev/null +++ b/storm/List.hpp @@ -0,0 +1,9 @@ +#ifndef STORM_LIST_HPP +#define STORM_LIST_HPP + +#include "list/TSExplicitList.hpp" +#include "list/TSLink.hpp" +#include "list/TSLinkedNode.hpp" +#include "list/TSList.hpp" + +#endif diff --git a/storm/list/TSExplicitList.hpp b/storm/list/TSExplicitList.hpp new file mode 100644 index 0000000..944a26b --- /dev/null +++ b/storm/list/TSExplicitList.hpp @@ -0,0 +1,23 @@ +#ifndef STORM_LIST_TS_EXPLICIT_LIST_HPP +#define STORM_LIST_TS_EXPLICIT_LIST_HPP + +#include "list/TSGetExplicitLink.hpp" +#include "list/TSList.hpp" +#include + +#define STORM_EXPLICIT_LIST(T, link) TSExplicitList + +template +class TSExplicitList : public TSList> { + public: + // Member functions + TSExplicitList(); +}; + +template +TSExplicitList::TSExplicitList() + : TSList>() { + this->SetLinkOffset(offset); +} + +#endif diff --git a/storm/list/TSGetExplicitLink.hpp b/storm/list/TSGetExplicitLink.hpp new file mode 100644 index 0000000..64c364c --- /dev/null +++ b/storm/list/TSGetExplicitLink.hpp @@ -0,0 +1,20 @@ +#ifndef STORM_LIST_TS_GET_EXPLICIT_LINK_HPP +#define STORM_LIST_TS_GET_EXPLICIT_LINK_HPP + +#include "list/TSLink.hpp" +#include +#include + +template +class TSGetExplicitLink { + public: + // Static functions + static TSLink* Link(const void* nodeptr, ptrdiff_t linkoffset); +}; + +template +TSLink* TSGetExplicitLink::Link(const void* nodeptr, ptrdiff_t linkoffset) { + return reinterpret_cast*>((reinterpret_cast(nodeptr) + linkoffset)); +} + +#endif diff --git a/storm/list/TSGetLink.hpp b/storm/list/TSGetLink.hpp new file mode 100644 index 0000000..f808383 --- /dev/null +++ b/storm/list/TSGetLink.hpp @@ -0,0 +1,21 @@ +#ifndef STORM_LIST_TS_GET_LINK_HPP +#define STORM_LIST_TS_GET_LINK_HPP + +#include "list/TSLink.hpp" +#include "list/TSLinkedNode.hpp" +#include +#include + +template +class TSGetLink { + public: + // Static functions + static TSLink* Link(const TSLinkedNode* nodeptr, ptrdiff_t linkoffset); +}; + +template +TSLink* TSGetLink::Link(const TSLinkedNode* nodeptr, ptrdiff_t linkoffset) { + return reinterpret_cast*>(reinterpret_cast(nodeptr)); +} + +#endif diff --git a/storm/list/TSLink.hpp b/storm/list/TSLink.hpp new file mode 100644 index 0000000..46c8099 --- /dev/null +++ b/storm/list/TSLink.hpp @@ -0,0 +1,81 @@ +#ifndef STORM_LIST_TS_LINK_HPP +#define STORM_LIST_TS_LINK_HPP + +#include +#include + +template +class TSLink { + public: + // Member variables + TSLink* m_prevlink = nullptr; + T* m_next = nullptr; + + // Member functions + ~TSLink(); + bool IsLinked(void); + T* Next(void); + TSLink* NextLink(ptrdiff_t linkoffset); + T* Prev(void); + T* RawNext(void); + void Unlink(void); +}; + +template +TSLink::~TSLink() { + this->Unlink(); +} + +template +bool TSLink::IsLinked() { + return this->m_next != nullptr; +} + +template +T* TSLink::Next() { + // Check for sentinel node (indicates list end) + return reinterpret_cast(this->m_next) <= 0 ? nullptr : this->m_next; +} + +template +TSLink* TSLink::NextLink(ptrdiff_t linkoffset) { + T* next = this->m_next; + + if (reinterpret_cast(next) <= 0) { + // End of list + return reinterpret_cast*>(~reinterpret_cast(next)); + } else { + ptrdiff_t offset; + + if (linkoffset < 0) { + offset = reinterpret_cast(this) - reinterpret_cast(this->m_prevlink->m_next); + } else { + offset = linkoffset; + } + + return reinterpret_cast*>(reinterpret_cast(this->m_next) + offset); + } +} + +template +T* TSLink::Prev() { + return this->m_prevlink->m_prevlink->Next(); +} + +template +T* TSLink::RawNext() { + return this->m_next; +} + +template +void TSLink::Unlink() { + if (this->m_prevlink) { + this->NextLink(-1)->m_prevlink = this->m_prevlink; + this->m_prevlink->m_next = this->m_next; + + this->m_prevlink = nullptr; + this->m_next = nullptr; + } +} + +#endif diff --git a/storm/list/TSLinkedNode.hpp b/storm/list/TSLinkedNode.hpp new file mode 100644 index 0000000..270f468 --- /dev/null +++ b/storm/list/TSLinkedNode.hpp @@ -0,0 +1,39 @@ +#ifndef STORM_LIST_TS_LINKED_NODE_HPP +#define STORM_LIST_TS_LINKED_NODE_HPP + +#include "list/TSLink.hpp" + +template +class TSLinkedNode { + public: + // Member variables + TSLink m_link; + + // Member functions + ~TSLinkedNode(); + T* Next(void); + T* Prev(void); + void Unlink(void); +}; + +template +TSLinkedNode::~TSLinkedNode() { + this->Unlink(); +} + +template +T* TSLinkedNode::Next() { + return this->m_link.Next(); +} + +template +T* TSLinkedNode::Prev() { + return this->m_link.Prev(); +} + +template +void TSLinkedNode::Unlink() { + this->m_link.Unlink(); +} + +#endif diff --git a/storm/list/TSList.hpp b/storm/list/TSList.hpp new file mode 100644 index 0000000..3ee9084 --- /dev/null +++ b/storm/list/TSList.hpp @@ -0,0 +1,215 @@ +#ifndef STORM_LIST_TS_LIST_HPP +#define STORM_LIST_TS_LIST_HPP + +#include "Memory.hpp" +#include "list/TSGetLink.hpp" +#include "list/TSLink.hpp" +#include +#include + +#define STORM_LIST(T) TSList> + +template +class TSList { + public: + // Member variables + ptrdiff_t m_linkoffset = 0; + TSLink m_terminator; + + // Member functions + TSList(); + ~TSList(); + void ChangeLinkOffset(ptrdiff_t linkoffset); + void DeleteAll(void); + T* DeleteNode(T* ptr); + T* Head(void); + void InitializeTerminator(void); + bool IsLinked(T* ptr); + TSLink* Link(const T* ptr); + void LinkNode(T* ptr, uint32_t linktype, T* existingptr); + void LinkToHead(T* ptr); + void LinkToTail(T* ptr); + T* NewNode(uint32_t location, size_t extrabytes, uint32_t flags); + T* Next(const T* ptr); + T* RawNext(const T* ptr); + void SetLinkOffset(ptrdiff_t linkoffset); + T* Tail(void); + void UnlinkAll(void); + void UnlinkNode(T* node); +}; + +template +TSList::TSList() { + this->InitializeTerminator(); +} + +template +TSList::~TSList() { + this->UnlinkAll(); +} + +template +void TSList::ChangeLinkOffset(ptrdiff_t linkoffset) { + if (linkoffset != this->m_linkoffset) { + this->UnlinkAll(); + this->SetLinkOffset(linkoffset); + } +} + +template +void TSList::DeleteAll() { + T* node; + + while ((node = this->Head())) { + this->DeleteNode(node); + } +} + +template +T* TSList::DeleteNode(T* ptr) { + T* next = this->Next(ptr); + + ptr->~T(); + SMemFree(ptr, __FILE__, __LINE__, 0); + + return next; +} + +template +T* TSList::Head() { + return this->m_terminator.Next(); +} + +template +void TSList::InitializeTerminator() { + this->m_terminator.m_prevlink = &this->m_terminator; + + // Set sentinel node (indicates list end) + this->m_terminator.m_next = reinterpret_cast(~reinterpret_cast(&this->m_terminator)); +} + +template +bool TSList::IsLinked(T* ptr) { + return TGetLink::Link(ptr, this->m_linkoffset)->IsLinked(); +} + +template +TSLink* TSList::Link(const T* ptr) { + if (ptr) { + return TGetLink::Link(ptr, this->m_linkoffset); + } else { + return &this->m_terminator; + } +} + +template +void TSList::LinkNode(T* ptr, uint32_t linktype, T* existingptr) { + TSLink* v5 = this->Link(ptr); + + if (v5->m_prevlink) { + v5->Unlink(); + } + + TSLink* v7; + + if (existingptr) { + v7 = this->Link(existingptr); + } else { + v7 = &this->m_terminator; + } + + TSLink* v8; + + switch (linktype) { + case 1: + // After existingptr + v5->m_prevlink = v7; + v5->m_next = v7->m_next; + v7->NextLink(this->m_linkoffset)->m_prevlink = v5; + v7->m_next = ptr; + + break; + + case 2: + // Before existingptr + v8 = v7->m_prevlink; + v5->m_prevlink = v7->m_prevlink; + v5->m_next = v8->m_next; + v8->m_next = ptr; + v7->m_prevlink = v5; + + break; + + default: + // TODO error + break; + } +} + +template +void TSList::LinkToHead(T* ptr) { + this->LinkNode(ptr, 1, nullptr); +} + +template +void TSList::LinkToTail(T* ptr) { + this->LinkNode(ptr, 2, nullptr); +} + +template +T* TSList::NewNode(uint32_t location, size_t extrabytes, uint32_t flags) { + void* m = SMemAlloc(sizeof(T) + extrabytes, __FILE__, __LINE__, flags); + + T* node; + + if (m) { + node = new (m) T(); + } else { + node = nullptr; + } + + if (location) { + this->LinkNode(node, location, nullptr); + } + + return node; +} + +template +T* TSList::Next(const T* ptr) { + return this->Link(ptr)->Next(); +} + +template +T* TSList::RawNext(const T* ptr) { + TSLink* link = this->Link(ptr); + return link->RawNext(); +} + +template +void TSList::SetLinkOffset(ptrdiff_t linkoffset) { + this->m_linkoffset = linkoffset; + this->InitializeTerminator(); +} + +template +T* TSList::Tail() { + return this->m_terminator.Prev(); +} + +template +void TSList::UnlinkAll() { + T* node; + + while ((node = this->Head())) { + this->UnlinkNode(node); + } +} + +template +void TSList::UnlinkNode(T* node) { + TSLink* link = this->Link(node); + link->Unlink(); +} + +#endif diff --git a/test/List.cpp b/test/List.cpp new file mode 100644 index 0000000..e44217c --- /dev/null +++ b/test/List.cpp @@ -0,0 +1,26 @@ +#include "Test.hpp" +#include "List.hpp" + +struct TestListNode : TSLinkedNode { + uint32_t index = 0; +}; + +TEST_CASE("TSList", "[list]") { + SECTION("constructs correctly") { + STORM_LIST(TestListNode) list; + REQUIRE(list.Head() == nullptr); + } +} + +TEST_CASE("TSList::LinkToHead", "[list]") { + SECTION("links node to head correctly") { + STORM_LIST(TestListNode) list; + + auto node = new TestListNode(); + list.LinkToHead(node); + + REQUIRE(list.Head() == node); + + delete(node); + } +}