/*!**************************************************************************** @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 CPVRTSkipGraphNode { private: T m_pData; CPVRTArray m_apDependencies; // What I depend on CPVRTArray 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 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& @brief Return the node associated with this element. *****************************************************************************/ CPVRTSkipGraphNode& GetNode() { return m_skipGraphNode; } /*!*************************************************************************** @fn GetNode @return CPVRTSkipGraphNode& @brief Return the node associated with this element. *****************************************************************************/ const CPVRTSkipGraphNode& GetNode() const { return m_skipGraphNode; } private: CPVRTHash m_hash; CPVRTSkipGraphNode m_skipGraphNode; }; CPVRTArray m_aHashTable; //-------------------------------------------------------------------------// public: /*!*************************************************************************** @fn AddNode @param[in] data The data of the node to be added @return CPVRTSkipGraphNode* 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* 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* pNode(NULL); CPVRTSkipGraphNode* 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 &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 &aOutputArray, CPVRTSkipGraphNode ¤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* const A handle to the found node *****************************************************************************/ CPVRTSkipGraphNode* 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* const A handle to the found node *****************************************************************************/ CPVRTSkipGraphNode* 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) *****************************************************************************/