recast&detour源码中有关的数据结构,特此记录,以便以后查看。
先看介绍:
哈希介绍 以及 哈希函数生成方法:
http://www.cnblogs.com/jillzhang/archive/2006/11/02/547679.html
哈希之冲突解决方法 即 哈希的实现:
http://www.cnblogs.com/jillzhang/archive/2006/11/03/548671.html
源码举例一
使用“链地址表”的数据结构
哈希函数不知道属于哪种
1)计算哈希值,即哈希函数,生成链表数组的下标
inline int computeTileHash(int x, int y, const int mask)
{
const unsigned int h1 = 0x8da6b343; // Large multiplicative constants;
const unsigned int h2 = 0xd8163841; // here arbitrarily chosen primes
unsigned int n = h1 * x + h2 * y;
return (int)(n & mask);
}
分析:
mask 为 (2^m - 1) ,(n & mask)把 n 截断为 最低的 m 位有值。即哈希表的表长为2^m = 0 ~ (2^m - 1) 。
关键字就是 x 和 y 。
2)加入相应链表表头,并更新头
分配一个可用tile。
// Allocate a tile.
dtMeshTile* tile = 0;
if (!lastRef)
{
if (m_nextFree)
{
tile = m_nextFree;
m_nextFree = tile->next;
tile->next = 0;
}
}
并把tile根据关键字计算位置,存入。
// Insert tile into the position lut.
int h = computeTileHash(header->x, header->y, m_tileLutMask);
tile->next = m_posLookup[h];
m_posLookup[h] = tile;
分析:
m_posLookup 是那个链表数组。数组的每一个元素都是一个指向Tile的指针,Tile又有next指针,指向下一个Tile。指针就是一个地址。
3)使用的时候,先找到下标,再在相应的链表中查找需要的元素。
const dtMeshTile* dtNavMesh::getTileAt(const int x, const int y, const int layer) const
{
// Find tile based on hash.
int h = computeTileHash(x,y,m_tileLutMask);
dtMeshTile* tile = m_posLookup[h];
while (tile)
{
if (tile->header &&
tile->header->x == x &&
tile->header->y == y &&
tile->header->layer == layer)
{
return tile;
}
tile = tile->next;
}
return 0;
}
补充:
类结构(一部分):
class dtNavMesh
{
int m_maxTiles; ///< Max number of tiles.
int m_tileLutSize; ///< Tile hash lookup size (must be pot).
int m_tileLutMask; ///< Tile hash lookup mask.
dtMeshTile** m_posLookup; ///< Tile hash lookup.
dtMeshTile* m_nextFree; ///< Freelist of tiles.
dtMeshTile* m_tiles; ///< List of tiles.
}
原本 (m_params.maxTiles) 个 压缩 到 (dtNextPow2(m_params.maxTiles/4))个。
比如 100 --> nextPow2(25) = 32。
这里总是压缩到 2的幂次 个数。
m_maxTiles = params->maxTiles;
m_tileLutMask = dtNextPow2(m_params.maxTiles/4);
if (!m_tileLutSize) m_tileLutSize = 1;
m_tileLutMask = m_tileLutSize -1;
初始化 链表数组m_posLookup(大小m_tileLutSize) 和 分配 m_tiles(大小m_maxTiles),并把 m_tiles之间的元素通过next指针形成链表。
m_tiles = (dtCompressedTile*)dtAlloc(sizeof(dtCompressedTile)*m_params.maxTiles, DT_ALLOC_PERM);
if (!m_tiles)
return DT_FAILURE | DT_OUT_OF_MEMORY;
m_posLookup = (dtCompressedTile**)dtAlloc(sizeof(dtCompressedTile*)*m_tileLutSize, DT_ALLOC_PERM);
if (!m_posLookup)
return DT_FAILURE | DT_OUT_OF_MEMORY;
memset(m_tiles, 0, sizeof(dtCompressedTile)*m_params.maxTiles);
memset(m_posLookup, 0, sizeof(dtCompressedTile*)*m_tileLutSize);
m_nextFreeTile = 0;
for (int i = m_params.maxTiles-1; i >= 0; --i)
{
m_tiles[i].salt = 1;
m_tiles[i].next = m_nextFreeTile;
m_nextFreeTile = &m_tiles[i];
}
源码举例二
再举个栗子 dtNodePool 对象中应用的哈希存储node
dtNodePool类型:
class dtNodePool
{
private:
// Explicitly disabled copy constructor and copy assignment operator.
dtNodePool(const dtNodePool&);
dtNodePool& operator=(const dtNodePool&);
dtNode* m_nodes; // 可用的真正的对象,个数为 m_maxNodes 。
dtNodeIndex* m_first; // 链表数组(大小m_hashSize) 。 dtNodeIndex 就是 unsigned short类型。
dtNodeIndex* m_next; // 起到链表的作用(大小m_maxNodes)。dtNodeIndex 是个基本类型,没有next指针,所以通过这个来指明next。
const int m_maxNodes; // 源个数 m_maxNodes
const int m_hashSize; // 数组链表的数组长度
int m_nodeCount;
};
实例化类:
m_nodePool = new (dtAlloc(sizeof(dtNodePool), DT_ALLOC_PERM)) dtNodePool(maxNodes, dtNextPow2(maxNodes/4));
调用其构造函数:
dtNodePool::dtNodePool(int maxNodes, int hashSize) :
m_nodes(0),
m_first(0),
m_next(0),
m_maxNodes(maxNodes),
m_hashSize(hashSize),
m_nodeCount(0)
{
dtAssert(dtNextPow2(m_hashSize) == (unsigned int)m_hashSize);
// pidx is special as 0 means "none" and 1 is the first node. For that reason
// we have 1 fewer nodes available than the number of values it can contain.
dtAssert(m_maxNodes > 0 && m_maxNodes <= DT_NULL_IDX && m_maxNodes <= (1 << DT_NODE_PARENT_BITS) - 1);
m_nodes = (dtNode*)dtAlloc(sizeof(dtNode)*m_maxNodes, DT_ALLOC_PERM);
m_next = (dtNodeIndex*)dtAlloc(sizeof(dtNodeIndex)*m_maxNodes, DT_ALLOC_PERM);
m_first = (dtNodeIndex*)dtAlloc(sizeof(dtNodeIndex)*hashSize, DT_ALLOC_PERM);
dtAssert(m_nodes);
dtAssert(m_next);
dtAssert(m_first);
// dtNodeIndex 全初始化为 0xffff, 即 DT_NULL_IDX ,表明链表数组为空,没有任何存入数据。
memset(m_first, 0xff, sizeof(dtNodeIndex)*m_hashSize);
memset(m_next, 0xff, sizeof(dtNodeIndex)*m_maxNodes);
}
哈希函数,计算下标:
inline unsigned int dtHashRef(dtPolyRef a)
{
a += ~(a<<15);
a ^= (a>>10);
a += (a<<3);
a ^= (a>>6);
a += ~(a<<11);
a ^= (a>>16);
return (unsigned int)a;
}
获取和创建放在同一个函数中:
先根据关键字id看能否在相应的数组链表中找到 id 和 state 对应的 node。
如果没有创建一个 node ,设置 id 和 state 为对应的值,插入表头,更新表头。(next指针用next数组来代替)
dtNode* dtNodePool::getNode(dtPolyRef id, unsigned char state)
{
unsigned int bucket = dtHashRef(id) & (m_hashSize-1);
dtNodeIndex i = m_first[bucket];
dtNode* node = 0;
while (i != DT_NULL_IDX)
{
if (m_nodes[i].id == id && m_nodes[i].state == state)
return &m_nodes[i];
i = m_next[i];
}
if (m_nodeCount >= m_maxNodes)
return 0;
i = (dtNodeIndex)m_nodeCount;
m_nodeCount++;
// Init node 分配 node
node = &m_nodes[i];
node->pidx = 0;
node->cost = 0;
node->total = 0;
node->id = id;
node->state = state;
node->flags = 0;
m_next[i] = m_first[bucket];
m_first[bucket] = i;
return node;
}