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; }