Skip to content

Commit

Permalink
Refactor|Uri: Calculate path node name hashes with unicodes
Browse files Browse the repository at this point in the history
Plus various minor cleanup refactorings.
  • Loading branch information
danij-deng committed Nov 12, 2012
1 parent 6a907c3 commit e325fc7
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 111 deletions.
11 changes: 7 additions & 4 deletions doomsday/engine/api/uri.h
Expand Up @@ -101,21 +101,24 @@ namespace de
/// An unresolveable symbol was encountered in the embedded expression. @ingroup errors
DENG2_SUB_ERROR(ResolveError, ResolveSymbolError);

/// Type used to represent a path name hash key.
typedef ushort hash_type;

/**
* This is a hash function. It generates from @a name a somewhat-random
* number between @c 0 and @c URI_PATHNODE_NAMEHASH_SIZE
*
* @return The generated hash key.
*/
static ushort hashPathNodeName(char const* name, int len);
static hash_type hashPathNodeName(String const& name);

/**
* Path Node represents a name in the URI path hierarchy.
*/
struct PathNode
{
/// @return Hash for this node's name.
ushort hash();
hash_type hash();

/// @return Length of this node's name in characters.
int length() const;
Expand All @@ -129,8 +132,8 @@ namespace de
friend struct Uri::Instance;

private:
bool haveHash;
ushort hash_;
bool haveHashKey;
hash_type hashKey;
char const* from, *to;
struct PathNode* parent_;
};
Expand Down
10 changes: 5 additions & 5 deletions doomsday/engine/portable/include/pathtree.h
Expand Up @@ -139,7 +139,7 @@ namespace de
String const& name() const;

/// @return Hash for this node's path fragment.
ushort hash() const;
Uri::hash_type hash() const;

/**
* @param candidatePath Mapped search pattern (path).
Expand Down Expand Up @@ -213,7 +213,7 @@ namespace de
/// The requested entry could not be found in the hierarchy.
DENG2_ERROR(NotFoundError);

typedef QMultiHash<ushort, Node*> Nodes;
typedef QMultiHash<Uri::hash_type, Node*> Nodes;
typedef QList<String> FoundPaths;

public:
Expand Down Expand Up @@ -292,8 +292,8 @@ namespace de
*
* @return @c 0 iff iteration completed wholly.
*/
int traverse(int flags, Node* parent, ushort hash, int (*callback) (Node& node, void* parameters),
void* parameters = 0);
int traverse(int flags, Node* parent, Uri::hash_type hashKey,
int (*callback) (Node& node, void* parameters), void* parameters = 0);

/**
* Provides access to the nodes for efficent traversals.
Expand All @@ -316,7 +316,7 @@ namespace de
String const& fragmentName(FragmentId fragmentId) const;

/// @return Hash associated with @a fragmentId.
ushort fragmentHash(FragmentId fragmentId) const;
Uri::hash_type fragmentHash(FragmentId fragmentId) const;

private:
Instance* d;
Expand Down
55 changes: 27 additions & 28 deletions doomsday/engine/portable/src/pathtree.cpp
Expand Up @@ -65,10 +65,10 @@ struct PathTree::Instance
size = 0;
}

PathTree::FragmentId internFragmentAndUpdateIdHashMap(String fragment, ushort hash)
PathTree::FragmentId internFragmentAndUpdateIdHashMap(String fragment, Uri::hash_type hashKey)
{
PathTree::FragmentId internId = fragments.intern(fragment);
fragments.setUserValue(internId, hash);
fragments.setUserValue(internId, hashKey);
return internId;
}

Expand All @@ -85,7 +85,7 @@ struct PathTree::Instance
{
// The name is known. Perhaps we have.
PathTree::Nodes& hash = (nodeType == PathTree::Leaf? leafHash : branchHash);
ushort hashKey = fragments.userValue(fragmentId);
Uri::hash_type hashKey = fragments.userValue(fragmentId);
for(PathTree::Nodes::const_iterator i = hash.find(hashKey); i != hash.end() && i.key() == hashKey; ++i)
{
PathTree::Node* node = *i;
Expand All @@ -102,16 +102,15 @@ struct PathTree::Instance
*/

// Do we need a new identifier (and hash)?
ushort hash;
Uri::hash_type hashKey;
if(!fragmentId)
{
QByteArray fragmentUtf8 = fragment.toUtf8();
hash = Uri::hashPathNodeName(fragmentUtf8.constData(), fragmentUtf8.length());
fragmentId = internFragmentAndUpdateIdHashMap(fragment, hash);
hashKey = Uri::hashPathNodeName(fragment);
fragmentId = internFragmentAndUpdateIdHashMap(fragment, hashKey);
}
else
{
hash = self.fragmentHash(fragmentId);
hashKey = self.fragmentHash(fragmentId);
}

// Are we out of indices?
Expand All @@ -121,11 +120,11 @@ struct PathTree::Instance
// Insert the new node into the hash.
if(nodeType == PathTree::Leaf)
{
leafHash.insert(hash, node);
leafHash.insert(hashKey, node);
}
else // Branch
{
branchHash.insert(hash, node);
branchHash.insert(hashKey, node);
}

return node;
Expand Down Expand Up @@ -225,12 +224,12 @@ PathTree::Node& PathTree::find(int flags, String searchPath, QChar delimiter)
{
de::Uri mappedSearchPath = de::Uri(searchPath, RC_NULL, delimiter.toLatin1());

ushort hash = mappedSearchPath.pathNode(0).hash();
Uri::hash_type hashKey = mappedSearchPath.pathNode(0).hash();
if(!(flags & PCF_NO_LEAF))
{
Nodes& nodes = d->leafHash;
Nodes::iterator i = nodes.find(hash);
for(; i != nodes.end() && i.key() == hash; ++i)
Nodes::iterator i = nodes.find(hashKey);
for(; i != nodes.end() && i.key() == hashKey; ++i)
{
Node& node = **i;
if(!node.comparePath(mappedSearchPath, flags))
Expand All @@ -246,8 +245,8 @@ PathTree::Node& PathTree::find(int flags, String searchPath, QChar delimiter)
if(!(flags & PCF_NO_BRANCH))
{
Nodes& nodes = d->branchHash;
Nodes::iterator i = nodes.find(hash);
for(; i != nodes.end() && i.key() == hash; ++i)
Nodes::iterator i = nodes.find(hashKey);
for(; i != nodes.end() && i.key() == hashKey; ++i)
{
Node& node = **i;
if(!node.comparePath(mappedSearchPath, flags))
Expand All @@ -269,7 +268,7 @@ String const& PathTree::fragmentName(FragmentId fragmentId) const
return d->fragments.string(fragmentId);
}

ushort PathTree::fragmentHash(FragmentId fragmentId) const
Uri::hash_type PathTree::fragmentHash(FragmentId fragmentId) const
{
return d->fragments.userValue(fragmentId);
}
Expand Down Expand Up @@ -303,24 +302,24 @@ int PathTree::findAllPaths(FoundPaths& found, int flags, QChar delimiter)
return found.count() - numFoundSoFar;
}

static int iteratePathsInHash(PathTree& pathTree, ushort hash, PathTree::NodeType type, int flags,
static int iteratePathsInHash(PathTree& pathTree, Uri::hash_type hashKey, PathTree::NodeType type, int flags,
PathTree::Node* parent, int (*callback) (PathTree::Node&, void*), void* parameters)
{
int result = 0;

if(hash != PATHTREE_NOHASH && hash >= URI_PATHNODE_NAMEHASH_SIZE)
if(hashKey != PATHTREE_NOHASH && hashKey >= URI_PATHNODE_NAMEHASH_SIZE)
{
throw Error("PathTree::iteratePathsInHash", String("Invalid hash %1 (valid range is [0..%2]).").arg(hash).arg(URI_PATHNODE_NAMEHASH_SIZE-1));
throw Error("PathTree::iteratePathsInHash", String("Invalid hash %1 (valid range is [0..%2]).").arg(hashKey).arg(URI_PATHNODE_NAMEHASH_SIZE-1));
}

PathTree::Nodes const& nodes = pathTree.nodes(type);

// Are we iterating nodes with a known hash?
if(hash != PATHTREE_NOHASH)
if(hashKey != PATHTREE_NOHASH)
{
// Yes.
PathTree::Nodes::const_iterator i = nodes.constFind(hash);
for(; i != nodes.end() && i.key() == hash; ++i)
PathTree::Nodes::const_iterator i = nodes.constFind(hashKey);
for(; i != nodes.end() && i.key() == hashKey; ++i)
{
if(!((flags & PCF_MATCH_PARENT) && parent != (*i)->parent()))
{
Expand All @@ -344,17 +343,17 @@ static int iteratePathsInHash(PathTree& pathTree, ushort hash, PathTree::NodeTyp
return result;
}

int PathTree::traverse(int flags, PathTree::Node* parent, ushort hash,
int PathTree::traverse(int flags, PathTree::Node* parent, Uri::hash_type hashKey,
int (*callback) (PathTree::Node&, void*), void* parameters)
{
int result = 0;
if(callback)
{
if(!(flags & PCF_NO_LEAF))
result = iteratePathsInHash(*this, hash, Leaf, flags, parent, callback, parameters);
result = iteratePathsInHash(*this, hashKey, Leaf, flags, parent, callback, parameters);

if(!result && !(flags & PCF_NO_BRANCH))
result = iteratePathsInHash(*this, hash, Branch, flags, parent, callback, parameters);
result = iteratePathsInHash(*this, hashKey, Branch, flags, parent, callback, parameters);
}
return result;
}
Expand Down Expand Up @@ -572,10 +571,10 @@ static void printDistributionHistogram(PathTree* pt, ushort size,
Con_Printf("\n");
Con_PrintRuler();

{ ushort from = 0, n = 0, range = (size != 0? URI_PATHNODE_NAMEHASH_SIZE / size: 0);
{ Uri::hash_type from = 0, n = 0, range = (size != 0? URI_PATHNODE_NAMEHASH_SIZE / size: 0);
memset(nodeCount, 0, sizeof(nodeCount));

for(ushort i = 0; i < URI_PATHNODE_NAMEHASH_SIZE; ++i)
for(Uri::hash_type i = 0; i < URI_PATHNODE_NAMEHASH_SIZE; ++i)
{
pathtree_pathhash_t** phAdr;
phAdr = hashAddressForNodeType(pt, PathTree::Node::Branch);
Expand Down Expand Up @@ -689,7 +688,7 @@ void PathTree::debugPrintHashDistribution(PathTree& /*pt*/)
memset(nodeBucketCollisionsMax, 0, sizeof(nodeBucketCollisionsMax));
memset(nodeBucketEmpty, 0, sizeof(nodeBucketEmpty));

for(ushort i = 0; i < URI_PATHNODE_NAMEHASH_SIZE; ++i)
for(Uri::hash_type i = 0; i < URI_PATHNODE_NAMEHASH_SIZE; ++i)
{
pathtree_pathhash_t** phAdr;
phAdr = hashAddressForNodeType(pt, PathTree::Node::Branch);
Expand Down
2 changes: 1 addition & 1 deletion doomsday/engine/portable/src/pathtreenode.cpp
Expand Up @@ -91,7 +91,7 @@ String const& PathTree::Node::name() const
return tree().fragmentName(d->fragmentId);
}

ushort PathTree::Node::hash() const
Uri::hash_type PathTree::Node::hash() const
{
return tree().fragmentHash(d->fragmentId);
}
Expand Down
68 changes: 21 additions & 47 deletions doomsday/engine/portable/src/resourcenamespace.cpp
Expand Up @@ -81,10 +81,10 @@ struct NameHash
{
public:
/// Type used to represent hash keys.
typedef unsigned short key_type;
typedef Uri::hash_type hash_type;

/// Number of buckets in the hash table.
static unsigned short const hash_size = 512;
static hash_type const hash_size = 512;

struct Node
{
Expand Down Expand Up @@ -118,15 +118,15 @@ struct NameHash

void clear()
{
for(uint i = 0; i < hash_size; ++i)
for(hash_type hashKey = 0; hashKey < hash_size; ++hashKey)
{
while(buckets[i].first)
while(buckets[hashKey].first)
{
NameHash::Node* nextNode = buckets[i].first->next;
delete buckets[i].first;
buckets[i].first = nextNode;
NameHash::Node* nextNode = buckets[hashKey].first->next;
delete buckets[hashKey].first;
buckets[hashKey].first = nextNode;
}
buckets[i].last = 0;
buckets[hashKey].last = 0;
}
}
};
Expand All @@ -136,37 +136,13 @@ static inline String composeResourceName(String const& filePath)
return filePath.fileNameWithoutExtension();
}

/**
* This is a hash function. It uses the resource name to generate a
* somewhat-random number between 0 and NameHash::hash_size.
*
* @return The generated hash key.
*/
static NameHash::key_type hashResourceName(char const* name)
{
DENG_ASSERT(name);

NameHash::key_type key = 0;
byte op = 0;

for(char const* c = name; *c; c++)
{
switch(op)
{
case 0: key ^= tolower(*c); ++op; break;
case 1: key *= tolower(*c); ++op; break;
case 2: key -= tolower(*c); op=0; break;
}
}
return key % NameHash::hash_size;
}

struct ResourceNamespace::Instance
{
ResourceNamespace& self;

/// Symbolic name of this namespace.
String name;

/// Associated path directory for this namespace.
/// @todo It should not be necessary for a unique directory per namespace.
PathTree directory;
Expand All @@ -188,10 +164,10 @@ struct ResourceNamespace::Instance
: self(d), name(_name), directory(), rootNode(0), nameHash(), nameHashIsDirty(true)
{}

NameHash::Node* findResourceInNameHash(NameHash::key_type key,
NameHash::Node* findResourceInNameHash(NameHash::hash_type hashKey,
PathTree::Node const& ptNode)
{
NameHash::Node* node = nameHash.buckets[key].first;
NameHash::Node* node = nameHash.buckets[hashKey].first;
while(node && &node->resource.directoryNode() != &ptNode)
{
node = node->next;
Expand Down Expand Up @@ -383,12 +359,11 @@ bool ResourceNamespace::add(PathTree::Node& resourceNode)
if(!resourceNode.isLeaf()) return false;

String name = composeResourceName(resourceNode.name());
QByteArray nameUtf8 = name.toUtf8();
NameHash::key_type key = hashResourceName(nameUtf8.constData());
NameHash::hash_type hashKey = Uri::hashPathNodeName(name) % NameHash::hash_size;

// Is this a new resource?
bool isNewNode = false;
NameHash::Node* hashNode = d->findResourceInNameHash(key, resourceNode);
NameHash::Node* hashNode = d->findResourceInNameHash(hashKey, resourceNode);
if(!hashNode)
{
isNewNode = true;
Expand All @@ -401,7 +376,7 @@ bool ResourceNamespace::add(PathTree::Node& resourceNode)
#endif

// Link it to the list for this bucket.
NameHash::Bucket& bucket = d->nameHash.buckets[key];
NameHash::Bucket& bucket = d->nameHash.buckets[hashKey];
if(bucket.last) bucket.last->next = hashNode;
bucket.last = hashNode;
if(!bucket.first) bucket.first = hashNode;
Expand Down Expand Up @@ -469,7 +444,7 @@ void ResourceNamespace::debugPrint() const
LOG_DEBUG("[%p]:") << de::dintptr(this);

uint resIdx = 0;
for(NameHash::key_type key = 0; key < NameHash::hash_size; ++key)
for(NameHash::hash_type key = 0; key < NameHash::hash_size; ++key)
{
NameHash::Bucket& bucket = d->nameHash.buckets[key];
for(NameHash::Node* node = bucket.first; node; node = node->next)
Expand Down Expand Up @@ -497,20 +472,19 @@ int ResourceNamespace::findAll(String searchPath, ResourceList& found)
name = composeResourceName(searchPath);
}

NameHash::key_type from, to;
NameHash::hash_type fromKey, toKey;
if(!name.isEmpty())
{
QByteArray nameUtf8 = name.toUtf8();
from = hashResourceName(nameUtf8.constData());
to = from;
fromKey = Uri::hashPathNodeName(name) % NameHash::hash_size;
toKey = fromKey;
}
else
{
from = 0;
to = NameHash::hash_size - 1;
fromKey = 0;
toKey = NameHash::hash_size - 1;
}

for(NameHash::key_type key = from; key < to + 1; ++key)
for(NameHash::hash_type key = fromKey; key < toKey + 1; ++key)
{
NameHash::Bucket& bucket = d->nameHash.buckets[key];
for(NameHash::Node* hashNode = bucket.first; hashNode; hashNode = hashNode->next)
Expand Down

0 comments on commit e325fc7

Please sign in to comment.