libnative-utilities/include/raknet/DS_Hash.hpp
2024-08-15 18:40:30 +08:00

358 lines
9.8 KiB
C++

/*
* Copyright (c) 2014, Oculus VR, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
/// \internal
/// \brief Hashing container
///
#ifndef __HASH_H
#define __HASH_H
#include "RakAssert.hpp"
#include <string.h> // memmove
#include "Export.hpp"
#include "RakMemoryOverride.hpp"
#include "RakString.hpp"
/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures
/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish.
namespace DataStructures
{
struct HashIndex
{
unsigned int primaryIndex;
unsigned int secondaryIndex;
bool IsInvalid(void) const {return primaryIndex==(unsigned int) -1;}
void SetInvalid(void) {primaryIndex=(unsigned int) -1; secondaryIndex=(unsigned int) -1;}
};
/// \brief Using a string as a identifier for a node, store an allocated pointer to that node
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
class RAK_DLL_EXPORT Hash
{
public:
/// Default constructor
Hash();
// Destructor
~Hash();
void Push(key_type key, const data_type &input, const char *file, unsigned int line );
data_type* Peek(key_type key );
bool Pop(data_type& out, key_type key, const char *file, unsigned int line );
bool RemoveAtIndex(HashIndex index, const char *file, unsigned int line );
bool Remove(key_type key, const char *file, unsigned int line );
HashIndex GetIndexOf(key_type key);
bool HasData(key_type key);
data_type& ItemAtIndex(const HashIndex &index);
key_type KeyAtIndex(const HashIndex &index);
void GetAsList(DataStructures::List<data_type> &itemList,DataStructures::List<key_type > &keyList,const char *file, unsigned int line) const;
unsigned int Size(void) const;
/// \brief Clear the list
void Clear( const char *file, unsigned int line );
struct Node
{
Node(key_type strIn, const data_type &_data) {string=strIn; data=_data;}
key_type string;
data_type data;
// Next in the list for this key
Node *next;
};
protected:
void ClearIndex(unsigned int index,const char *file, unsigned int line);
Node **nodeList;
unsigned int size;
};
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
Hash<key_type, data_type, HASH_SIZE, hashFunction>::Hash()
{
nodeList=0;
size=0;
}
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
Hash<key_type, data_type, HASH_SIZE, hashFunction>::~Hash()
{
Clear(_FILE_AND_LINE_);
}
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
void Hash<key_type, data_type, HASH_SIZE, hashFunction>::Push(key_type key, const data_type &input, const char *file, unsigned int line )
{
unsigned long hashIndex = (*hashFunction)(key) % HASH_SIZE;
if (nodeList==0)
{
nodeList=RakNet::OP_NEW_ARRAY<Node *>(HASH_SIZE,file,line);
memset(nodeList,0,sizeof(Node *)*HASH_SIZE);
}
Node *newNode=RakNet::OP_NEW_2<Node>(file,line,key,input);
newNode->next=nodeList[hashIndex];
nodeList[hashIndex]=newNode;
size++;
}
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
data_type* Hash<key_type, data_type, HASH_SIZE, hashFunction>::Peek(key_type key )
{
if (nodeList==0)
return 0;
unsigned long hashIndex = (*hashFunction)(key) % HASH_SIZE;
Node *node = nodeList[hashIndex];
while (node!=0)
{
if (node->string==key)
return &node->data;
node=node->next;
}
return 0;
}
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
bool Hash<key_type, data_type, HASH_SIZE, hashFunction>::Pop(data_type& out, key_type key, const char *file, unsigned int line )
{
if (nodeList==0)
return false;
unsigned long hashIndex = (*hashFunction)(key) % HASH_SIZE;
Node *node = nodeList[hashIndex];
if (node==0)
return false;
if (node->next==0)
{
// Only one item.
if (node->string==key)
{
// Delete last item
out=node->data;
ClearIndex(hashIndex,_FILE_AND_LINE_);
return true;
}
else
{
// Single item doesn't match
return false;
}
}
else if (node->string==key)
{
// First item does match, but more than one item
out=node->data;
nodeList[hashIndex]=node->next;
RakNet::OP_DELETE(node,file,line);
size--;
return true;
}
Node *last=node;
node=node->next;
while (node!=0)
{
// First item does not match, but subsequent item might
if (node->string==key)
{
out=node->data;
// Skip over subsequent item
last->next=node->next;
// Delete existing item
RakNet::OP_DELETE(node,file,line);
size--;
return true;
}
last=node;
node=node->next;
}
return false;
}
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
bool Hash<key_type, data_type, HASH_SIZE, hashFunction>::RemoveAtIndex(HashIndex index, const char *file, unsigned int line )
{
if (index.IsInvalid())
return false;
Node *node = nodeList[index.primaryIndex];
if (node==0)
return false;
if (node->next==0)
{
// Delete last item
ClearIndex(index.primaryIndex,file,line);
return true;
}
else if (index.secondaryIndex==0)
{
// First item does match, but more than one item
nodeList[index.primaryIndex]=node->next;
RakNet::OP_DELETE(node,file,line);
size--;
return true;
}
Node *last=node;
node=node->next;
--index.secondaryIndex;
while (index.secondaryIndex!=0)
{
last=node;
node=node->next;
--index.secondaryIndex;
}
// Skip over subsequent item
last->next=node->next;
// Delete existing item
RakNet::OP_DELETE(node,file,line);
size--;
return true;
}
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
bool Hash<key_type, data_type, HASH_SIZE, hashFunction>::Remove(key_type key, const char *file, unsigned int line )
{
return RemoveAtIndex(GetIndexOf(key),file,line);
}
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
HashIndex Hash<key_type, data_type, HASH_SIZE, hashFunction>::GetIndexOf(key_type key)
{
if (nodeList==0)
{
HashIndex temp;
temp.SetInvalid();
return temp;
}
HashIndex idx;
idx.primaryIndex=(*hashFunction)(key) % HASH_SIZE;
Node *node = nodeList[idx.primaryIndex];
if (node==0)
{
idx.SetInvalid();
return idx;
}
idx.secondaryIndex=0;
while (node!=0)
{
if (node->string==key)
{
return idx;
}
node=node->next;
idx.secondaryIndex++;
}
idx.SetInvalid();
return idx;
}
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
bool Hash<key_type, data_type, HASH_SIZE, hashFunction>::HasData(key_type key)
{
return GetIndexOf(key).IsInvalid()==false;
}
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
data_type& Hash<key_type, data_type, HASH_SIZE, hashFunction>::ItemAtIndex(const HashIndex &index)
{
Node *node = nodeList[index.primaryIndex];
RakAssert(node);
unsigned int i;
for (i=0; i < index.secondaryIndex; i++)
{
node=node->next;
RakAssert(node);
}
return node->data;
}
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
key_type Hash<key_type, data_type, HASH_SIZE, hashFunction>::KeyAtIndex(const HashIndex &index)
{
Node *node = nodeList[index.primaryIndex];
RakAssert(node);
unsigned int i;
for (i=0; i < index.secondaryIndex; i++)
{
node=node->next;
RakAssert(node);
}
return node->string;
}
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
void Hash<key_type, data_type, HASH_SIZE, hashFunction>::Clear(const char *file, unsigned int line)
{
if (nodeList)
{
unsigned int i;
for (i=0; i < HASH_SIZE; i++)
ClearIndex(i,file,line);
RakNet::OP_DELETE_ARRAY(nodeList,file,line);
nodeList=0;
size=0;
}
}
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
void Hash<key_type, data_type, HASH_SIZE, hashFunction>::ClearIndex(unsigned int index,const char *file, unsigned int line)
{
Node *node = nodeList[index];
Node *next;
while (node)
{
next=node->next;
RakNet::OP_DELETE(node,file,line);
node=next;
size--;
}
nodeList[index]=0;
}
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
void Hash<key_type, data_type, HASH_SIZE, hashFunction>::GetAsList(DataStructures::List<data_type> &itemList,DataStructures::List<key_type > &keyList,const char *file, unsigned int line) const
{
if (nodeList==0)
return;
itemList.Clear(false,_FILE_AND_LINE_);
keyList.Clear(false,_FILE_AND_LINE_);
Node *node;
unsigned int i;
for (i=0; i < HASH_SIZE; i++)
{
if (nodeList[i])
{
node=nodeList[i];
while (node)
{
itemList.Push(node->data,file,line);
keyList.Push(node->string,file,line);
node=node->next;
}
}
}
}
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
unsigned int Hash<key_type, data_type, HASH_SIZE, hashFunction>::Size(void) const
{
return size;
}
}
#endif