mirror of
https://github.com/galaxyhaxz/devilution
synced 2025-02-22 12:34:59 +00:00
261 lines
5.5 KiB
C++
261 lines
5.5 KiB
C++
/* Intrusive double-linked list implementation,
|
|
* based on https://github.com/webcoyote/coho/blob/master/Base/List.h
|
|
*/
|
|
|
|
#include <new> // for placement new
|
|
#include <stddef.h> // for offsetof
|
|
#include <typeinfo> // for typeid
|
|
|
|
#include "../3rdParty/Storm/Source/storm.h"
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning (disable : 4291) // no matching operator delete found
|
|
#endif
|
|
|
|
#define OBJECT_NAME(obj) (((const char *)&typeid(obj)) + 8)
|
|
|
|
/******************************************************************************
|
|
*
|
|
* List definition macros
|
|
*
|
|
***/
|
|
|
|
// Define a field within a structure that will be used to link it into a list
|
|
#define LIST_LINK(T) TLink<T>
|
|
|
|
template <class T>
|
|
class TLink;
|
|
|
|
/******************************************************************************
|
|
*
|
|
* TList
|
|
*
|
|
***/
|
|
|
|
//=============================================================================
|
|
template <class T>
|
|
class TList {
|
|
public:
|
|
TList();
|
|
~TList();
|
|
|
|
void UnlinkAll();
|
|
void DeleteAll();
|
|
|
|
T *Head();
|
|
|
|
enum InsertPos {
|
|
NONE = 0,
|
|
AFTER,
|
|
BEFORE
|
|
};
|
|
|
|
void Insert(T *node, InsertPos pos, T *ref);
|
|
T *Remove(T *node);
|
|
T *Create(InsertPos pos = BEFORE, size_t extra = 0, int memflags = 0);
|
|
|
|
private:
|
|
size_t m_offset;
|
|
TLink<T> m_link;
|
|
|
|
TLink<T> *GetLinkFromNode(T *node) const;
|
|
|
|
// Hide copy-constructor and assignment operator
|
|
TList(const TList &);
|
|
TList &operator=(const TList &);
|
|
|
|
// replacement new/delete operators for Storm objects
|
|
static __forceinline T *SNew(size_t extralen, int flags)
|
|
{
|
|
void *obj = SMemAlloc(sizeof(T) + extralen, (char *)OBJECT_NAME(T), SLOG_OBJECT, flags | (1<<3));
|
|
return new (obj) T();
|
|
}
|
|
|
|
static __forceinline void SDelete(T *node)
|
|
{
|
|
node->~T();
|
|
SMemFree(node, (char *)OBJECT_NAME(T), SLOG_OBJECT, 0);
|
|
}
|
|
};
|
|
|
|
//=============================================================================
|
|
template <class T>
|
|
TList<T>::~TList()
|
|
{
|
|
// BUGFIX: Unlinking does not free memory, should use DeleteAll()
|
|
UnlinkAll();
|
|
}
|
|
|
|
//=============================================================================
|
|
template <class T>
|
|
TList<T>::TList()
|
|
{
|
|
size_t offset = offsetof(T, m_Link);
|
|
// Mark this node as the end of the list, with the link offset set
|
|
m_link.m_prevLink = &m_link;
|
|
m_offset = offset;
|
|
m_link.m_nextNode = (T *)~((size_t)&m_link - offset);
|
|
}
|
|
|
|
//=============================================================================
|
|
template <class T>
|
|
void TList<T>::DeleteAll()
|
|
{
|
|
while (T *node = m_link.Next())
|
|
SDelete(node);
|
|
}
|
|
|
|
//=============================================================================
|
|
template <class T>
|
|
__forceinline T *TList<T>::Head()
|
|
{
|
|
return m_link.Next();
|
|
}
|
|
|
|
//=============================================================================
|
|
template <class T>
|
|
__forceinline TLink<T> *TList<T>::GetLinkFromNode(T *node) const
|
|
{
|
|
// assert(m_offset != (size_t) -1);
|
|
// return (TLink<T> *) ((size_t) node + m_offset);
|
|
return &node->m_Link;
|
|
}
|
|
|
|
template <class T>
|
|
T *TList<T>::Remove(T *node)
|
|
{
|
|
TLink<T> *link = node ? &node->m_Link : &m_link;
|
|
T *next = link->Next();
|
|
SDelete(node);
|
|
return next;
|
|
}
|
|
|
|
template <class T>
|
|
T *TList<T>::Create(InsertPos pos, size_t extra, int memflags)
|
|
{
|
|
T *node = SNew(extra, memflags);
|
|
if (pos != NONE)
|
|
Insert(node, pos, NULL);
|
|
return node;
|
|
}
|
|
|
|
template <class T>
|
|
void TList<T>::Insert(T *node, InsertPos pos, T *ref)
|
|
{
|
|
TLink<T> *reflink;
|
|
TLink<T> *i = node ? GetLinkFromNode(node) : &m_link;
|
|
if (i->IsLinked())
|
|
i->Unlink();
|
|
|
|
reflink = ref ? GetLinkFromNode(ref) : &m_link;
|
|
|
|
switch (pos) {
|
|
case AFTER:
|
|
i->InsertAfter(node, reflink, m_offset);
|
|
break;
|
|
case BEFORE:
|
|
i->InsertBefore(node, reflink);
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
template <class T>
|
|
void TList<T>::UnlinkAll()
|
|
{
|
|
for (;;) {
|
|
T *node = m_link.Next();
|
|
if ((int)node <= 0)
|
|
break;
|
|
node->m_Link.Unlink();
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
*
|
|
* TLink
|
|
*
|
|
***/
|
|
|
|
//=============================================================================
|
|
template <class T>
|
|
class TLink {
|
|
public:
|
|
TLink()
|
|
: m_prevLink(NULL)
|
|
, m_nextNode(NULL)
|
|
{
|
|
}
|
|
~TLink()
|
|
{
|
|
Unlink();
|
|
}
|
|
|
|
bool IsLinked() const
|
|
{
|
|
return m_prevLink != NULL;
|
|
}
|
|
void Unlink();
|
|
|
|
T *Next()
|
|
{
|
|
if ((int)m_nextNode <= 0)
|
|
return NULL;
|
|
return m_nextNode;
|
|
}
|
|
|
|
TLink<T> *NextLink(size_t offset = -1)
|
|
{
|
|
if ((int)m_nextNode <= 0)
|
|
return (TLink<T> *)~((size_t)m_nextNode);
|
|
|
|
if ((int)offset < 0) {
|
|
// Calculate the offset from a node pointer to a link structure
|
|
offset = (size_t)this - (size_t)m_prevLink->m_nextNode;
|
|
}
|
|
|
|
// Get the link field for the next node
|
|
return (TLink<T> *)((size_t)m_nextNode + offset);
|
|
}
|
|
|
|
void InsertBefore(T *node, TLink<T> *nextLink)
|
|
{
|
|
TLink<T> *p = nextLink->m_prevLink;
|
|
m_prevLink = p;
|
|
m_nextNode = p->m_nextNode;
|
|
|
|
p->m_nextNode = node;
|
|
nextLink->m_prevLink = this;
|
|
}
|
|
|
|
__forceinline void InsertAfter(T *node, TLink<T> *prevLink, const size_t &offset)
|
|
{
|
|
m_prevLink = prevLink;
|
|
m_nextNode = prevLink->m_nextNode;
|
|
|
|
prevLink->NextLink(offset)->m_prevLink = this;
|
|
prevLink->m_nextNode = node;
|
|
}
|
|
|
|
private:
|
|
TLink<T> *m_prevLink; // pointer to the previous >link field<
|
|
T *m_nextNode; // pointer to the next >object<
|
|
|
|
// Hide copy-constructor and assignment operator
|
|
TLink(const TLink &);
|
|
TLink &operator=(const TLink &);
|
|
|
|
friend class TList<T>;
|
|
};
|
|
|
|
//=============================================================================
|
|
template <class T>
|
|
void TLink<T>::Unlink()
|
|
{
|
|
if (IsLinked()) {
|
|
NextLink()->m_prevLink = m_prevLink;
|
|
m_prevLink->m_nextNode = m_nextNode;
|
|
|
|
m_prevLink = NULL;
|
|
m_nextNode = NULL;
|
|
}
|
|
}
|