/*!****************************************************************************
|
|
@file PVRTSkipGraph.h
|
@copyright Copyright (c) Imagination Technologies Limited.
|
@brief A "tree-like" structure for storing data which, unlike a tree, can
|
reference any other node.
|
|
******************************************************************************/
|
#ifndef __PVRTSKIPGRAPH_H__
|
#define __PVRTSKIPGRAPH_H__
|
|
|
#include "PVRTArray.h"
|
#include "PVRTHash.h"
|
|
/*!***************************************************************************
|
@class CPVRTSkipGraphNode
|
@brief Stores a pointer to the node's data and also uses a dynamic
|
array to store pointer to nodes this node depends on and
|
another to store pointers to nodes that are dependant on this node
|
*****************************************************************************/
|
template<class T>
|
class CPVRTSkipGraphNode
|
{
|
private:
|
T m_pData;
|
CPVRTArray<CPVRTSkipGraphNode*> m_apDependencies; // What I depend on
|
CPVRTArray<CPVRTSkipGraphNode*> m_apDependents; // What depends on me
|
|
public:
|
/*!***************************************************************************
|
@brief Constructor
|
*****************************************************************************/
|
CPVRTSkipGraphNode()
|
{}
|
|
/*!***************************************************************************
|
@brief Overloaded constructor
|
@param[in] data Pointer to a node
|
*****************************************************************************/
|
CPVRTSkipGraphNode(const T& data) : m_pData(data)
|
{}
|
|
/*!***************************************************************************
|
@brief Destructor
|
*****************************************************************************/
|
~CPVRTSkipGraphNode()
|
{}
|
|
/*!***************************************************************************
|
@fn GetNumDependencies
|
@return unsigned int
|
@brief Returns the number of dependencies referenced by this node.
|
*****************************************************************************/
|
unsigned int GetNumDependencies() const
|
{
|
return (unsigned int)m_apDependencies.GetSize();
|
}
|
|
/*!***************************************************************************
|
@fn GetDependency
|
@param[in] ui32Id
|
@return CPVRTSkipGraphNode &
|
@brief Returns given dependency.
|
*****************************************************************************/
|
CPVRTSkipGraphNode& GetDependency(const unsigned int ui32Id) const
|
{
|
_ASSERT(ui32Id >= 0 && ui32Id < (unsigned int)m_apDependencies.GetSize());
|
return *m_apDependencies[ui32Id];
|
}
|
|
|
/*!***************************************************************************
|
@fn AddDependency
|
@param[out] pDependentNode
|
@return bool
|
@brief Adds a dependency to this node.
|
*****************************************************************************/
|
bool AddDependency(CPVRTSkipGraphNode* pDependentNode)
|
{
|
unsigned int ui(0);
|
|
if(pDependentNode == this)
|
return false;
|
|
if(!pDependentNode)
|
return false;
|
|
/*
|
Check the dependency doesn't already exist
|
*/
|
for(ui = 0; ui < (unsigned int)m_apDependencies.GetSize(); ++ui)
|
{
|
if(m_apDependencies[ui] == pDependentNode)
|
{
|
return true;
|
}
|
}
|
|
/*
|
Add the dependency and also set this node as a dependent of
|
the referenced node
|
*/
|
m_apDependencies.Append(pDependentNode);
|
pDependentNode->AddDependent(this);
|
|
return true;
|
}
|
|
/*!***************************************************************************
|
@fn GetData
|
@return T &
|
@brief Returns the data associated with this node.
|
*****************************************************************************/
|
T& GetData()
|
{
|
return m_pData;
|
}
|
|
private:
|
/*!***************************************************************************
|
@fn AddDependent
|
@param[out] pDependancyNode
|
@return bool
|
@brief Adds a dependent to this node.
|
*****************************************************************************/
|
bool AddDependent(CPVRTSkipGraphNode* pDependencyNode)
|
{
|
unsigned int ui(0);
|
|
if(!pDependencyNode)
|
return false;
|
|
/*
|
Check the dependency doesn't already exist
|
*/
|
for(ui = 0; ui < (unsigned int)m_apDependents.GetSize(); ++ui)
|
{
|
if(m_apDependencies[ui] == pDependencyNode)
|
{
|
return true;
|
}
|
}
|
|
/*
|
Add the dependancy
|
*/
|
m_apDependents.Append(pDependencyNode);
|
return true;
|
}
|
};
|
|
/*!***************************************************************************
|
@class CPVRTSkipGraphRoot
|
@brief This class is the entry point for creating and accessing
|
the elements of a skip graph. It uses a hash table to store
|
the nodes of the structure and a hash value that allows
|
fast searching of the skip graph
|
*****************************************************************************/
|
template<class T>
|
class CPVRTSkipGraphRoot
|
{
|
//-------------------------------------------------------------------------//
|
private:
|
|
/*!***************************************************************************
|
@struct SPVRTHashElement
|
@brief A struct to store data and a hash value generated from the
|
data. The hash value allows faster searching of the skip graph.
|
*****************************************************************************/
|
struct SPVRTHashElement
|
{
|
public:
|
/*!***************************************************************************
|
@fn SPVRTHashElement
|
@param[in] hash
|
@param[in] data
|
@brief Overloaded constructor.
|
*****************************************************************************/
|
SPVRTHashElement(const CPVRTHash& hash, const T& data)
|
:
|
m_hash(hash),
|
m_skipGraphNode(data)
|
{}
|
|
/*!***************************************************************************
|
@fn SPVRTHashElement
|
@brief Constructor
|
*****************************************************************************/
|
SPVRTHashElement()
|
{}
|
|
/*!***************************************************************************
|
@fn ~SPVRTHashElement
|
@brief Destructor
|
*****************************************************************************/
|
~SPVRTHashElement()
|
{}
|
|
/*!***************************************************************************
|
@fn GetHash
|
@return unsigned int
|
@brief Returns the element's hash value.
|
*****************************************************************************/
|
const CPVRTHash& GetHash() const
|
{
|
return m_hash;
|
}
|
|
/*!***************************************************************************
|
@fn GetNode
|
@return CPVRTSkipGraphNode<T>&
|
@brief Return the node associated with this element.
|
*****************************************************************************/
|
CPVRTSkipGraphNode<T>& GetNode()
|
{
|
return m_skipGraphNode;
|
}
|
|
/*!***************************************************************************
|
@fn GetNode
|
@return CPVRTSkipGraphNode<T>&
|
@brief Return the node associated with this element.
|
*****************************************************************************/
|
const CPVRTSkipGraphNode<T>& GetNode() const
|
{
|
return m_skipGraphNode;
|
}
|
|
private:
|
CPVRTHash m_hash;
|
CPVRTSkipGraphNode<T> m_skipGraphNode;
|
};
|
|
|
CPVRTArray<SPVRTHashElement> m_aHashTable;
|
|
//-------------------------------------------------------------------------//
|
public:
|
|
/*!***************************************************************************
|
@fn AddNode
|
@param[in] data The data of the node to be added
|
@return CPVRTSkipGraphNode<T>* const A handle to the added node
|
@brief Searches through the hash table to see if the added node already
|
exists. If it doesn't, it creates a node.
|
The function returns true if the node was found or was created
|
successfully.
|
*****************************************************************************/
|
bool AddNode(const T& data)
|
{
|
CPVRTHash NewNodeHash((void*)&data, sizeof(T), 1);
|
int iArrayElement(-1);
|
/*
|
First, search the hash table to see
|
if the node already exists
|
*/
|
CPVRTSkipGraphNode<T>* skipGraphNode(FindNode(NewNodeHash));
|
|
if(skipGraphNode == NULL)
|
{
|
/*
|
The node wasn't found, so a new node needs to be
|
created
|
*/
|
iArrayElement = m_aHashTable.Append(SPVRTHashElement(NewNodeHash, data));
|
|
/*
|
Now point to the new instance
|
*/
|
skipGraphNode = &m_aHashTable[iArrayElement].GetNode();
|
}
|
|
return skipGraphNode ? true : false;
|
}
|
|
|
/*!***************************************************************************
|
@brief Adds a node dependency.
|
@param[in] nodeData
|
@param[in] dependantData
|
@return bool
|
*****************************************************************************/
|
bool AddNodeDependency(const T& nodeData, const T& dependantData)
|
{
|
CPVRTSkipGraphNode<T>* pNode(NULL);
|
CPVRTSkipGraphNode<T>* pDependantNode(NULL);
|
|
pNode = FindNode(nodeData);
|
if(!pNode)
|
{
|
return false;
|
}
|
|
pDependantNode = FindNode(dependantData);
|
if(!pDependantNode)
|
{
|
return false;
|
}
|
|
/*
|
Nodes are not allowed to self reference
|
*/
|
if(pNode == pDependantNode)
|
{
|
return false;
|
}
|
pNode->AddDependency(pDependantNode);
|
|
return true;
|
}
|
|
/*!***************************************************************************
|
@fn GetNumNodes
|
@return unsigned int The total number of nodes
|
@brief Returns the total number of nodes in the skip graph.
|
*****************************************************************************/
|
unsigned int GetNumNodes() const
|
{
|
return (unsigned int)m_aHashTable.GetSize();
|
}
|
|
/*!***************************************************************************
|
@brief Returns a sorted list of dependencies for the specified
|
node. The list is ordered with the leaf nodes at the front,
|
followed by nodes that depend on them and so forth until
|
the root node is reached and added at the end of the list
|
@param[in] aOutputArray The dynamic array to store
|
the sorted results in
|
@param[in] ui32NodeID The ID of the root node for
|
the dependency search
|
*****************************************************************************/
|
void RetreiveSortedDependencyList(CPVRTArray<T> &aOutputArray,
|
const unsigned int ui32NodeID)
|
{
|
_ASSERT(ui32NodeID >= 0 && ui32NodeID < (unsigned int)m_aHashTable.GetSize());
|
RecursiveSortedListAdd(aOutputArray, m_aHashTable[ui32NodeID].GetNode());
|
}
|
|
/*!***************************************************************************
|
@brief Overloads operator[] to returns a handle to the node data
|
for the specified ID
|
@return T& Handle to the node data
|
*****************************************************************************/
|
T& operator[](const unsigned int ui32NodeID)
|
{
|
return *(GetNodeData(ui32NodeID));
|
}
|
|
/*!***************************************************************************
|
@brief Overloads operator[] to returns a const handle to the node
|
data for the specified ID
|
@return T& Handle to the node data
|
*****************************************************************************/
|
const T& operator[](const unsigned int ui32NodeID) const
|
{
|
return *(GetNodeData(ui32NodeID));
|
}
|
|
//-------------------------------------------------------------------------//
|
private:
|
/*!***************************************************************************
|
@brief Recursively adds node dependancies to aOutputArray.
|
By doing so, aOutputArray will be ordered from leaf nodes to
|
the root node that started the recursive chain
|
@param[in] aOutputArray The dynamic array to store
|
the sorted results in
|
@param[in] currentNode The current node to process
|
*****************************************************************************/
|
void RecursiveSortedListAdd(CPVRTArray<T> &aOutputArray,
|
CPVRTSkipGraphNode<T> ¤tNode)
|
{
|
unsigned int ui(0);
|
|
/*
|
Recursively add dependancies first
|
*/
|
for(ui = 0; ui < currentNode.GetNumDependencies(); ++ui)
|
{
|
RecursiveSortedListAdd(aOutputArray, currentNode.GetDependency(ui));
|
}
|
|
/*
|
Then add this node to the array
|
*/
|
aOutputArray.Append(currentNode.GetData());
|
}
|
|
/*!***************************************************************************
|
@fn GetNodeData
|
@param[in,out] ui32NodeID The node's ID
|
@return T* A handle to node's data
|
@brief Retrieve a handle to the specified node's data
|
*****************************************************************************/
|
T* GetNodeData(unsigned int ui32NodeID)
|
{
|
_ASSERT(ui32NodeID >= 0 && ui32NodeID < (unsigned int)m_aHashTable.GetSize());
|
return &m_aHashTable[ui32NodeID].GetNode().GetData();
|
}
|
|
/*!***************************************************************************
|
@brief Use the input hash to search the hash table and see if the
|
node already exists. If it does, the function returns a pointer
|
to the node. If it doesn't, it returns NULL
|
@param[in,out] ui32Hash The hash value to search with
|
@return CPVRTSkipGraphNode<T>* const A handle to the found node
|
*****************************************************************************/
|
CPVRTSkipGraphNode<T>* FindNode(const CPVRTHash& Hash)
|
{
|
int i(0);
|
int i32HashTableSize(m_aHashTable.GetSize());
|
|
/*
|
A NULL hash means the node has not initialised
|
correctly
|
*/
|
if(Hash == 0)
|
return NULL;
|
|
/*
|
NOTE:
|
In the future, the hash table could be sorted from
|
lowest hash value to highest so that binary search could
|
be used to find a given node. It should be possible to
|
use a bool (or some other mechanism) to toggle this form of search
|
(if the skip graph is small, it might be faster to just use a brute
|
force for loop to search through)
|
*/
|
for(i = 0; i < i32HashTableSize; i++)
|
{
|
if(m_aHashTable[i].GetHash() == Hash)
|
{
|
return &m_aHashTable[i].GetNode();
|
}
|
}
|
|
/*
|
The element wasn't found, so return null
|
*/
|
return NULL;
|
}
|
|
/*!***************************************************************************
|
@brief Use the input data to generate a hash and then
|
search the hash table and see if the node already exists.
|
If it does, the function returns a pointer
|
to the node. If it doesn't, it returns NULL
|
@param[in,out] data Data to use as a source for the hash
|
@return CPVRTSkipGraphNode<T>* const A handle to the found node
|
*****************************************************************************/
|
CPVRTSkipGraphNode<T>* FindNode(const T& data)
|
{
|
CPVRTHash inputHash((void*)&data, sizeof(T), 1); // Generate hash for searching
|
return FindNode(inputHash);
|
}
|
};
|
|
#endif //__PVRTSKIPGRAPH_H__
|
|
/*****************************************************************************
|
End of file (PVRTSkipGraph.h)
|
*****************************************************************************/
|