MegaGlest/source/glest_game/ai/IntrHeapHash.hpp
2012-05-07 01:37:43 +00:00

547 lines
11 KiB
C++

// ==============================================================
// This file is part of Glest (www.glest.org)
//
// Copyright (C) 2012 Mark Vejvoda
//
// You can redistribute this code and/or modify it under
// the terms of the GNU General Public License as published
// by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version
// ==============================================================
#ifndef __INTRUSIVE_HEAP_HASH_SET_HPP__
#define __INTRUSIVE_HEAP_HASH_SET_HPP__
#ifndef WIN32
#include <inttypes.h>
#endif
#include <vector>
#include "IntrHashSet.hpp"
/*
Here is temlate class Intrusive Heap/HashSet.
Actuall values are stored in this container,
no copying is performed, so all values should
be allocated in a heap or in a storage with
lifetime as long as lifetime of container.
Values are also used as nodes of container,
so no value can be stored in two containers
of this kind simultaneously.
*/
/*
This is what should be done with duplicate
from hash point of view values.
*/
enum HHDuplicatePolicy{
hhdpReplaceOld, //replace old value. old values is not deallocated!
hhdpKeepOld, //container is not modified in this case.
hhdpInsertDuplicate //duplicates are stored in container
};
/*
TRAITS class MUST implement following static members:
for heap - this is something like operator< or operator>
bool TRAITS::Compare(const T* a,const T* b);
for hash
bool TRAITS::isEqual(const T* a,const T* b);
uint32_t TRAITS::getHashCode(const T& a);
*/
/*
This is base class for values stored in a intrusive heap hash
*/
struct IntrHeapHashNodeBase:public IntrHashSetNodeBase{
int ihhNodeIndex;
};
template <class T,class TRAITS,HHDuplicatePolicy policy=hhdpReplaceOld>
class IntrHeapHash{
public:
//default constructor
//preAlloc value cannot be 0!
IntrHeapHash(int preAlloc=128)
{
hash=new Node*[preAlloc];
memset(hash,0,preAlloc*sizeof(Node*));
hashSize=preAlloc;
heap=new Node*[preAlloc];
memset(heap,0,preAlloc*sizeof(Node*));
heapSize=0;
heapAlloc=preAlloc;
collisionsCount=0;
}
//guess what? destructor!
~IntrHeapHash()
{
delete [] hash;
delete [] heap;
}
// clear content container
// values are not deallocated!
void Clear()
{
memset(hash,0,hashSize*sizeof(Node*));
heapSize=0;
collisionsCount=0;
}
//insert new value into container
//return value is equal to inserted value for all policies if no duplicate detected
//in case of duplicate for keep old policy 0 is returned
//in case of duplicate for replace old policy old value is returned
T* Insert(T* value)
{
if(collisionsCount>hashSize/2)
{
Rehash();
}
uint32_t hidx=getIndex(value);
Node*& node=hash[hidx];
T* rv=value;
if(policy==hhdpReplaceOld || policy==hhdpKeepOld)
{
Node* ptr=FindHashNode(node,*value);
if(ptr)
{
if(policy==hhdpKeepOld)
{
return 0;
}
DeleteHashNode(hash+hidx,ptr);
rv=ptr;
}
}
value->ihhNodeIndex=heapSize;
if(node)
{
collisionsCount++;
}
value->ihsNextNode=node;
node=value;
if(heapSize==heapAlloc)
{
heapAlloc*=2;
Node** newHeap=new Node*[heapAlloc];
memcpy(newHeap,heap,heapSize*sizeof(Node*));
delete [] heap;
heap=newHeap;
}
heap[heapSize++]=value;
HeapPush();
return rv;
}
// value at the head of heap
const T* getHead()const
{
return *heap;
}
// same as previous but non const
T* getHead()
{
return **heap;
}
// get and remove head value of heap
T* Pop()
{
T* rv=*heap;
uint32_t hidx=getIndex(rv);
DeleteHashNode(&hash[hidx],rv);
HeapSwap(heap,heap+heapSize-1);
heapSize--;
HeapPop();
return rv;
}
//find value in hash
//const version
const T* Find(const T& value)const
{
uint32_t hidx=getIndex(&value);
Node* ptr=hash[hidx];
while(ptr)
{
if(TRAITS::isEqual(ptr,&value))
{
return ptr;
}
ptr=(Node*)ptr->ihsNextNode;
}
return 0;
}
//find value in hash
//non const version
//it's not good idea to modify key value of hash or key value of heap however
T* Find(const T& value)
{
uint32_t hidx=getIndex(&value);
Node* ptr=hash[hidx];
while(ptr)
{
if(TRAITS::isEqual(ptr,&value))
{
return ptr;
}
ptr=(Node*)ptr->ihsNextNode;
}
return 0;
}
//get from hash all values equal to given
void GetAll(const T& value,std::vector<T*>& values)const
{
uint32_t hidx=getIndex(&value);
Node* ptr=hash[hidx];
while(ptr)
{
if(TRAITS::isEqual(ptr,&value))
{
values.push_back(ptr);
}
ptr=(Node*)ptr->ihsNextNode;
}
}
//delete value from hash
//deleted object returned
T* Delete(const T& value)
{
uint32_t hidx=getIndex(&value);
int idx=DeleteHashNode(hash+hidx,value);
if(idx==-1)
{
return 0;
}
if(idx==heapSize-1)
{
heapSize--;
return heap[heapSize];
}
HeapSwap(heap+idx,heap+heapSize-1);
heapSize--;
HeapFix(idx);
return heap[heapSize];
}
//delete all values equal to given
void DeleteAll(const T& value)
{
uint32_t hidx=getIndex(&value);
Node** nodePtr=hash+hidx;
while(*nodePtr)
{
int idx=DeleteHashNode(nodePtr,value);
if(idx==-1)
{
return;
}
if(idx==heapSize-1)
{
heapSize--;
continue;
}
HeapSwap(heap+idx,heap+heapSize-1);
heapSize--;
HeapFix(idx);
}
}
//get number of elements in container
size_t getSize()const
{
return heapSize;
}
//check if container is empty
bool isEmpty()const
{
return heapSize==0;
}
//iterator
//can/should be used like this:
//for(IHHTypeDef::Iterator it(ihh);it.Next();)
//{
// dosomething(it.Get())
//}
class Iterator{
public:
Iterator(const IntrHeapHash& argHH)
{
begin=argHH.heap;
current=0;
end=argHH.heap+argHH.heapSize;
}
bool Next()
{
if(current==0)
{
current=begin;
}else
{
current++;
}
return current!=end;
}
T* Get()
{
if(current)
{
return *current;
}
return 0;
}
void Rewind()
{
current=begin;
}
protected:
typename IntrHeapHash::Node **begin,**current,**end;
};
protected:
//protected copy constructor
//values cannot be copied as well as container
IntrHeapHash(const IntrHeapHash&);
//typedef for convenience
typedef T Node;
//heap array
Node** heap;
//hash table
Node** hash;
//size of hash table
size_t hashSize;
//used size of heap
size_t heapSize;
//allocated size of heap
size_t heapAlloc;
//count of collisions in hash
int collisionsCount;
//get index of value in hash table
uint32_t getIndex(const T* value)const
{
uint32_t hashCode=TRAITS::getHashCode(value);
return hashCode%hashSize;
}
//resize hash table when
//collisions count is too big
void Rehash()
{
Node** oldHash=hash;
Node** oldHashEnd=oldHash+hashSize;
hashSize*=2;
hash=new Node*[hashSize];
memset(hash,0,hashSize*sizeof(Node*));
collisionsCount=0;
for(Node** it=oldHash;it!=oldHashEnd;it++)
{
Node* nodePtr=*it;
while(nodePtr)
{
uint32_t hidx=getIndex(nodePtr);
Node*& hnode=hash[hidx];
if(hnode)
{
collisionsCount++;
}
Node* next=(Node*)nodePtr->ihsNextNode;
nodePtr->ihsNextNode=hnode;
hnode=nodePtr;
nodePtr=next;
}
}
}
//find and delete hash node from bucket by ptr
void DeleteHashNode(Node** nodeBasePtr,Node* nodePtr)
{
if(*nodeBasePtr==nodePtr)
{
*nodeBasePtr=(Node*)nodePtr->ihsNextNode;
if(*nodeBasePtr)
{
collisionsCount--;
}
return;
}
Node* parentPtr=*nodeBasePtr;
Node* iterPtr=(Node*)parentPtr->ihsNextNode;
while(iterPtr)
{
if(iterPtr==nodePtr)
{
parentPtr->ihsNextNode=nodePtr->ihsNextNode;
collisionsCount--;
return;
}
parentPtr=iterPtr;
iterPtr=(Node*)iterPtr->ihsNextNode;
}
}
//find and delete hash node from bucked by value
//heap index returned
int DeleteHashNode(Node** nodePtr,const T& data)
{
if(!*nodePtr)return -1;
if(TRAITS::isEqual(*nodePtr,&data))
{
int rv=(*nodePtr)->ihhNodeIndex;
*nodePtr=(Node*)(*nodePtr)->ihsNextNode;
if(*nodePtr)
{
collisionsCount--;
}
return rv;
}
Node* parentPtr=*nodePtr;
Node* node=(Node*)(*nodePtr)->ihsNextNode;
while(node)
{
if(TRAITS::isEqual(node,&data))
{
parentPtr->ihsNextNode=node->ihsNextNode;
collisionsCount--;
return node->ihhNodeIndex;
}
parentPtr=node;
node=(Node*)node->ihsNextNode;
}
return -1;
}
//find hash node in a bucked by value
Node* FindHashNode(Node* nodePtr,const T& data)
{
while(nodePtr)
{
if(TRAITS::isEqual(nodePtr,&data))
{
return nodePtr;
}
nodePtr=(Node*)nodePtr->ihsNextNode;
}
return 0;
}
//float up tail element
void HeapPush(int didx=-1)
{
int idx=didx==-1?heapSize-1:didx;
int pidx;
Node** b=heap;
Node** i=b+idx;
Node** pi;
Node* ptr=*i;
while(idx)
{
pidx=(idx-1)/2;
pi=b+pidx;
if(TRAITS::Compare(ptr,(*pi)))
{
*i=*pi;
(*i)->ihhNodeIndex=idx;
i=pi;
idx=pidx;
}else
{
break;
}
}
*i=ptr;
ptr->ihhNodeIndex=idx;
}
//drow down element from head
void HeapPop(int idx=0)
{
if(heapSize<=1)
{
return;
}
Node **i,**b,**i1,**i2;
int cidx1,cidx2;
b=heap;
i=b+idx;
Node* ptr=*i;
while(idx<heapSize)
{
cidx1=idx*2+1;
cidx2=idx*2+2;
if(cidx1>=heapSize)
{
break;
}
i1=b+cidx1;
i2=b+cidx2;
if(cidx2>=heapSize || TRAITS::Compare((*i1),(*i2)))
{
if(!TRAITS::Compare(ptr,(*i1)))
{
*i=*i1;
(*i)->ihhNodeIndex=idx;
idx=cidx1;
i=i1;
}else
{
break;
}
}else
{
if(!TRAITS::Compare(ptr,(*i2)))
{
*i=*i2;
(*i)->ihhNodeIndex=idx;
idx=cidx2;
i=i2;
}else
{
break;
}
}
}
*i=ptr;
ptr->ihhNodeIndex=idx;
}
//either drow down or float up element at middle
void HeapFix(int idx)
{
int pidx=(idx-1)/2;
if(TRAITS::Compare(heap[idx],heap[pidx]))
{
HeapPush(idx);
}else
{
HeapPop(idx);
}
}
//swap two element fixing indeces
void HeapSwap(Node** i1,Node** i2)
{
int idx1=(*i1)->ihhNodeIndex;
int idx2=(*i2)->ihhNodeIndex;
Node* ptr=*i1;
*i1=*i2;
(*i1)->ihhNodeIndex=idx1;
(*i2)=ptr;
(*i2)->ihhNodeIndex=idx2;
}
};
#endif