diff --git a/doomsday/libdeng2/include/de/data/pathtree.h b/doomsday/libdeng2/include/de/data/pathtree.h index bb109b0c13..8f442e9c08 100644 --- a/doomsday/libdeng2/include/de/data/pathtree.h +++ b/doomsday/libdeng2/include/de/data/pathtree.h @@ -66,6 +66,15 @@ class DENG2_PUBLIC PathTree typedef QMultiHash Nodes; typedef QList FoundPaths; + /** + * Leaves and branches are stored in separate hashes. + * Note that one can always unite the hashes (see QMultiHash). + */ + struct DENG2_PUBLIC NodeHash { + Nodes leaves; + Nodes branches; + }; + /** * Flags that affect the properties of the tree. */ @@ -142,7 +151,7 @@ class DENG2_PUBLIC PathTree class DENG2_PUBLIC Node { public: - typedef PathTree::Nodes Children; + typedef PathTree::NodeHash Children; protected: Node(NodeArgs const &args); @@ -161,6 +170,16 @@ class DENG2_PUBLIC PathTree /// have no children -- calling this for leaf nodes is not allowed. Children const &children() const; + /** + * Returns the children of a branch node. Note that leaf nodes + * have no children -- calling this for leaf nodes is not allowed. + * + * @param type Type of hash to return (leaves or branches). + * + * @return Hash of nodes. + */ + Nodes const &childNodes(NodeType type) const; + /// Determines if the node is at the root level of the tree /// (no other node is its parent). bool isAtRootLevel() const; @@ -219,6 +238,7 @@ class DENG2_PUBLIC PathTree SegmentId segmentId() const; void addChild(Node &node); void removeChild(Node &node); + Nodes &childNodes(NodeType type); private: struct Instance; diff --git a/doomsday/libdeng2/src/data/archive.cpp b/doomsday/libdeng2/src/data/archive.cpp index 2c19951f26..8dbed74f69 100644 --- a/doomsday/libdeng2/src/data/archive.cpp +++ b/doomsday/libdeng2/src/data/archive.cpp @@ -121,10 +121,9 @@ dint Archive::listFiles(Archive::Names& names, Path const &folder) const PathTree::Node const &parent = d->index->find(folder, PathTree::MatchFull | PathTree::NoLeaf); // Traverse the parent's nodes. - for(PathTreeIterator iter(parent.children()); iter.hasNext(); ) + for(PathTreeIterator iter(parent.children().leaves); iter.hasNext(); ) { - PathTree::Node const &node = iter.next(); - if(node.isLeaf()) names.insert(node.name()); + names.insert(iter.next().name()); } return names.size(); @@ -140,10 +139,9 @@ dint Archive::listFolders(Archive::Names &names, Path const &folder) const PathTree::Node const &parent = d->index->find(folder, PathTree::MatchFull | PathTree::NoLeaf); // Traverse the parent's nodes. - for(PathTreeIterator iter(parent.children()); iter.hasNext(); ) + for(PathTreeIterator iter(parent.children().branches); iter.hasNext(); ) { - PathTree::Node const &node = iter.next(); - if(node.isBranch()) names.insert(node.name()); + names.insert(iter.next().name()); } return names.size(); diff --git a/doomsday/libdeng2/src/data/pathtree.cpp b/doomsday/libdeng2/src/data/pathtree.cpp index 205fe32da4..b91eb3d589 100644 --- a/doomsday/libdeng2/src/data/pathtree.cpp +++ b/doomsday/libdeng2/src/data/pathtree.cpp @@ -45,9 +45,8 @@ struct PathTree::Instance /// Node that represents the one root branch of all nodes. PathTree::Node rootNode; - /// Path node hashes. - PathTree::Nodes leafHash; - PathTree::Nodes branchHash; + /// Path node hashes (leaves and branches). + PathTree::NodeHash hash; Instance(PathTree &d, int _flags) : self(d), flags(_flags), size(0), @@ -61,8 +60,8 @@ struct PathTree::Instance void clear() { - clearPathHash(leafHash); - clearPathHash(branchHash); + clearPathHash(hash.leaves); + clearPathHash(hash.branches); size = 0; } @@ -80,12 +79,13 @@ struct PathTree::Instance PathTree::Node *nodeForSegment(Path::Segment const &segment, PathTree::NodeType nodeType, PathTree::Node *parent) { + PathTree::Nodes const &hash = self.nodes(nodeType); + // Have we already encountered this? PathTree::SegmentId segmentId = segments.isInterned(segment); if(segmentId) { // The name is known. Perhaps we have. - PathTree::Nodes &hash = (nodeType == PathTree::Leaf? leafHash : branchHash); Path::hash_type hashKey = segments.userValue(segmentId); for(PathTree::Nodes::const_iterator i = hash.find(hashKey); i != hash.end() && i.key() == hashKey; ++i) @@ -118,14 +118,7 @@ struct PathTree::Instance PathTree::Node *node = self.newNode(PathTree::NodeArgs(self, nodeType, segmentId, parent)); // Insert the new node into the hash. - if(nodeType == PathTree::Leaf) - { - leafHash.insert(hashKey, node); - } - else // Branch - { - branchHash.insert(hashKey, node); - } + const_cast(hash).insert(hashKey, node); return node; } @@ -193,13 +186,13 @@ struct PathTree::Instance if(!compFlags.testFlag(NoLeaf)) { - if((found = findInHash(leafHash, hashKey, searchPath, compFlags)) != 0) + if((found = findInHash(hash.leaves, hashKey, searchPath, compFlags)) != 0) return found; } if(!compFlags.testFlag(NoBranch)) { - if((found = findInHash(branchHash, hashKey, searchPath, compFlags)) != 0) + if((found = findInHash(hash.branches, hashKey, searchPath, compFlags)) != 0) return found; } } @@ -330,7 +323,7 @@ PathTree::Node *PathTree::newNode(NodeArgs const &args) PathTree::Nodes const &PathTree::nodes(NodeType type) const { - return (type == Leaf? d->leafHash : d->branchHash); + return (type == Leaf? d->hash.leaves : d->hash.branches); } static void collectPathsInHash(PathTree::FoundPaths &found, PathTree::Nodes const &ph, QChar separator) @@ -349,11 +342,11 @@ int PathTree::findAllPaths(FoundPaths &found, ComparisonFlags flags, QChar separ int numFoundSoFar = found.count(); if(!(flags & NoBranch)) { - collectPathsInHash(found, branchNodes(), separator); + collectPathsInHash(found, d->hash.branches, separator); } if(!(flags & NoLeaf)) { - collectPathsInHash(found, leafNodes(), separator); + collectPathsInHash(found, d->hash.leaves, separator); } return found.count() - numFoundSoFar; } @@ -372,6 +365,14 @@ static int iteratePathsInHash(PathTree const &pathTree, Path::hash_type hashKey, PathTree::Nodes const *nodes = &pathTree.nodes(type); + // If the parent is known, we can narrow our search to all the parent's + // children. + if(!pathTree.flags().testFlag(PathTree::NoLocalBranchIndex) && + flags.testFlag(PathTree::MatchParent) && parent) + { + nodes = &parent->childNodes(type); + } + // Are we iterating nodes with a known hash? if(hashKey != PathTree::no_hash) { @@ -388,24 +389,9 @@ static int iteratePathsInHash(PathTree const &pathTree, Path::hash_type hashKey, } else { - // No known hash, but if the parent is known, we can narrow our search - // to all the parent's children. - if(!pathTree.flags().testFlag(PathTree::NoLocalBranchIndex) && - flags.testFlag(PathTree::MatchParent) && parent) - { - nodes = &parent->children(); - } - // No known hash -- iterate all potential nodes. DENG2_FOR_EACH_CONST(PathTree::Nodes, i, *nodes) { - if(flags.testFlag(PathTree::MatchParent) && (*i)->type() != type) - { - // Wrong kind of node -- branches could maintain separate indexes - // for branches and leaves... - continue; - } - if(!(flags.testFlag(PathTree::MatchParent) && parent != &(*i)->parent())) { result = callback(**i, parameters); diff --git a/doomsday/libdeng2/src/data/pathtreenode.cpp b/doomsday/libdeng2/src/data/pathtreenode.cpp index d8d18d4d72..52bdd0a333 100644 --- a/doomsday/libdeng2/src/data/pathtreenode.cpp +++ b/doomsday/libdeng2/src/data/pathtreenode.cpp @@ -85,6 +85,18 @@ const PathTree::Node::Children &PathTree::Node::children() const return *d->children; } +PathTree::Nodes const &PathTree::Node::childNodes(PathTree::NodeType type) const +{ + DENG2_ASSERT(d->children != 0); + return (type == PathTree::Leaf? d->children->leaves : d->children->branches); +} + +PathTree::Nodes &PathTree::Node::childNodes(PathTree::NodeType type) +{ + DENG2_ASSERT(d->children != 0); + return (type == PathTree::Leaf? d->children->leaves : d->children->branches); +} + bool PathTree::Node::isAtRootLevel() const { return d->parent == &d->tree.rootBranch(); @@ -101,7 +113,7 @@ void PathTree::Node::addChild(PathTree::Node &node) { DENG2_ASSERT(d->children != 0); - d->children->insert(node.hash(), &node); + childNodes(node.type()).insert(node.hash(), &node); } } @@ -111,7 +123,7 @@ void PathTree::Node::removeChild(PathTree::Node &node) { DENG2_ASSERT(d->children != 0); - d->children->remove(node.hash(), &node); + childNodes(node.type()).remove(node.hash(), &node); } }