Skip to content

Commit

Permalink
Refactor: Uri's "path nodes" renamed to "segments" as per URI spec
Browse files Browse the repository at this point in the history
The "path nodes" of the Uri had nothing to do with PathTree::Nodes or
with tree nodes in general. They simply mark portions of the URI's
path so that the start and end location of each segment of the URI path
are known.

Refactored the class implementation to simplify the code and remove
redundancies. The manually managed linked list of segments was replaced
with a QList.

Updated the unit test for the Uri class.
  • Loading branch information
skyjake committed Nov 18, 2012
1 parent 5852084 commit a00bc49
Show file tree
Hide file tree
Showing 6 changed files with 217 additions and 242 deletions.
90 changes: 48 additions & 42 deletions doomsday/engine/include/uri.hh
Expand Up @@ -56,11 +56,11 @@ namespace de {
*/
class Uri : public ISerializable, public LogEntry::Arg::Base
{
struct Instance; // needs to be friended by PathNode
struct Instance; // needs to be friended by Uri::Segment

public:
/// A nonexistent path node was referenced. @ingroup errors
DENG2_ERROR(NotPathNodeError);
/// A nonexistent path segment was referenced. @ingroup errors
DENG2_ERROR(NotSegmentError);

/// Base class for resolve-related errors. @ingroup errors
DENG2_ERROR(ResolveError);
Expand Down Expand Up @@ -90,35 +90,44 @@ public:
Q_DECLARE_FLAGS(PrintFlags, PrintFlag)

/**
* Represents a name in the URI path hierarchy.
* @todo Rename to "Segment" (cf. URI RFC section 3.3, paths are composed of segments).
* Marks a segment in the URI's path. Makes no copy of the segments in the
* path, only stores the location within the URI where they begin and end.
*
* Note that only the path is broken down to segments. The other parts of a
* URI are not processed in this fashion.
*
* @see URI paths are composed of segments:
* http://tools.ietf.org/html/rfc3986#section-3.3
*/
struct PathNode
struct Segment
{
/**
* Hash function that generates a somewhat-random number in the
* range [0..Uri::hash_range) from the node's name.
* Returns a somewhat-random number in the range [0..Uri::hash_range)
* generated from the segment.
*
* @return The generated hash key.
*/
hash_type hash();
hash_type hash() const;

/// @return Length of this node's name in characters.
/// @return Length of the segment in characters.
int length() const;

/// @return Parent of this node else @c NULL.
PathNode* parent() const;
/// @return Parent (previous) segment or @c NULL.
Segment* parent() const;

/**
* Returns the segment as a string.
*/
String toString() const;

friend class Uri;
friend struct Uri::Instance;

private:
bool haveHashKey;
hash_type hashKey;
mutable bool haveHashKey;
mutable hash_type hashKey;
char const* from, *to;
struct PathNode* parent_;
struct Segment* parent_;
};

public:
Expand All @@ -145,7 +154,7 @@ public:

~Uri();

Uri& operator = (Uri other);
Uri& operator = (Uri const& other);

bool operator == (Uri const& other) const;

Expand Down Expand Up @@ -264,61 +273,58 @@ public:
String compose(QChar delimiter = '/') const;

/**
* Retrieve the path node with index @a index. Note that nodes are indexed
* Retrieve the segment with index @a index. Note that segments are indexed
* in reverse order (right to left) and NOT the autological left to right
* order.
*
* For example, if the path is "c:/mystuff/myaddon.addon" the corresponding
* path node map is arranged as follows:
* segment map is arranged as follows:
* <pre>
* [0:{myaddon.addon}, 1:{mystuff}, 2:{c:}].
* </pre>
*
* @note The zero-length name in relative paths is also treated as a node.
* For example, the path "/Users/username" has three nodes.
* @note The zero-length name in relative paths is also treated as a segment.
* For example, the path "/Users/username" has three segments.
* (FIXME: "/Users/username" is NOT a relative path; "Users/username" is. -jk)
*
* @param index Reverse-index of the path node to lookup. All paths have
* at least one node (even empty ones) thus index @c 0 will
* @param index Reverse-index of the segment to lookup. All paths have
* at least one segment (even empty ones) thus index @c 0 will
* always be valid.
*
* @return Referenced path node.
* @return Referenced segment.
*/
PathNode& pathNode(int index) const;
const Segment& segment(int index) const;

/**
* @return Total number of nodes in the URI path name map.
* @see pathNode()
* @return Total number of segments in the URI segment map.
* @see segment()
*/
int pathNodeCount() const;
int segmentCount() const;

/**
* @return First node in the URI path name map.
* @see pathNode()
* @return First segment in the path.
* @see segment()
*/
inline PathNode& firstPathNode() const {
return pathNode(0);
inline const Segment& firstSegment() const {
return segment(0);
}

/**
* @return Last node in the URI path name map.
* @see pathNode()
* @return Last segment in the path.
* @see segment()
*/
inline PathNode& lastPathNode() const {
return pathNode(pathNodeCount() - 1);
inline const Segment& lastSegment() const {
return segment(segmentCount() - 1);
}

/**
* Transform the URI into a human-friendly representation. Percent decoding done.
* Transform the URI into a human-friendly representation. Percent-encoded
* symbols are decoded.
*
* @return Human-friendly String representation.
* @return Human-friendly representation of the URI.
*/
String asText() const;

/**
* Swaps URI @a second with URI @a first.
*/
friend void swap(Uri& first, Uri& second); // nothrow

/**
* Print debug ouput for the URI.
*
Expand Down
17 changes: 9 additions & 8 deletions doomsday/engine/src/pathtree.cpp
Expand Up @@ -73,11 +73,11 @@ struct PathTree::Instance
}

/**
* @return [ a new | the ] directory node that matches the name
* and type and which has the specified parent node.
* @return Tree node that matches the name and type and which has the
* specified parent node.
*/
PathTree::Node* direcNode(Uri::PathNode& pathNode, PathTree::NodeType nodeType,
PathTree::Node* parent)
PathTree::Node* direcNode(const Uri::Segment& pathNode, PathTree::NodeType nodeType,
PathTree::Node* parent)
{
// Have we already encountered this?
String fragment = pathNode.toString();
Expand Down Expand Up @@ -144,20 +144,21 @@ struct PathTree::Instance
/// Now that the names of a path can be accessed randomly with no
/// impact on performance - there is no need to index the nodes in
/// reverse (right to left) order. -ds

bool const hasLeaf = !uri.path().endsWith("/");

PathTree::Node* node = 0, *parent = 0;
for(int i = uri.pathNodeCount() - 1; i >= (hasLeaf? 1 : 0); --i)
for(int i = uri.segmentCount() - 1; i >= (hasLeaf? 1 : 0); --i)
{
Uri::PathNode& pn = uri.pathNode(i);
const Uri::Segment& pn = uri.segment(i);
//qDebug() << "Add branch: " << pn.toString();
node = direcNode(pn, PathTree::Branch, parent);
parent = node;
}

if(hasLeaf)
{
Uri::PathNode& pn = uri.firstPathNode();
const Uri::Segment& pn = uri.firstSegment();
//qDebug() << "Add leaf: " << pn.toString();
node = direcNode(pn, PathTree::Leaf, parent);
}
Expand Down Expand Up @@ -232,7 +233,7 @@ PathTree::Node& PathTree::find(Uri const& searchPath, int flags)
{
if(!searchPath.isEmpty() && d->size)
{
Uri::hash_type hashKey = searchPath.firstPathNode().hash();
Uri::hash_type hashKey = searchPath.firstSegment().hash();
if(!(flags & PCF_NO_LEAF))
{
Nodes& nodes = d->leafHash;
Expand Down
8 changes: 4 additions & 4 deletions doomsday/engine/src/pathtreenode.cpp
Expand Up @@ -165,10 +165,10 @@ int PathTree::Node::comparePath(de::Uri const& searchPattern, int flags) const

try
{
de::Uri::PathNode* snode = &searchPattern.firstPathNode();
const de::Uri::Segment* snode = &searchPattern.firstSegment();

// In reverse order, compare each path node in the search term.
int pathNodeCount = searchPattern.pathNodeCount();
int pathNodeCount = searchPattern.segmentCount();

PathTree::Node const* node = this;
for(int i = 0; i < pathNodeCount; ++i)
Expand Down Expand Up @@ -207,10 +207,10 @@ int PathTree::Node::comparePath(de::Uri const& searchPattern, int flags) const

// So far so good. Move one level up the hierarchy.
node = node->parent();
snode = &searchPattern.pathNode(i + 1);
snode = &searchPattern.segment(i + 1);
}
}
catch(de::Uri::NotPathNodeError const&)
catch(de::Uri::NotSegmentError const&)
{} // Ignore this error.

return 1;
Expand Down
2 changes: 1 addition & 1 deletion doomsday/engine/src/resource/lumpindex.cpp
Expand Up @@ -384,7 +384,7 @@ lumpnum_t LumpIndex::indexForPath(char const* path)

// Perform the search.
de::Uri searchPattern = de::Uri(path, RC_NULL);
ushort hash = searchPattern.firstPathNode().hash() % d->hashMap->size();
ushort hash = searchPattern.firstSegment().hash() % d->hashMap->size();
if((*d->hashMap)[hash].head == -1) return -1;

for(int idx = (*d->hashMap)[hash].head; idx != -1; idx = (*d->hashMap)[idx].next)
Expand Down
2 changes: 1 addition & 1 deletion doomsday/engine/src/resource/sys_reslocator.cpp
Expand Up @@ -115,7 +115,7 @@ static bool findResourceInNamespace(ResourceNamespace& rnamespace, de::Uri const
rnamespace.rebuild();

// A resource name is the file name sans extension.
String name = searchPath.firstPathNode().toString().fileNameWithoutExtension();
String name = searchPath.firstSegment().toString().fileNameWithoutExtension();

// Perform the search.
ResourceNamespace::ResourceList foundResources;
Expand Down

0 comments on commit a00bc49

Please sign in to comment.