Skip to content

Commit

Permalink
Refactor|PathTree: Compose paths to de::Uri instead of de::String
Browse files Browse the repository at this point in the history
Composing the paths to Uri rather than String presents several
opportunities to improve both the design and performance of the
whole virtual file system. (should a string representation be
required it can be trivially obtained with Uri::compose()).

As PathTree is already aware of the path fragment hashes (they
are produced in the process of building the tree), these can now
be passed on to the composed Uri.

Uri should now be redesigned so that the internal representation
is modelled as a list of path fragments with associated hashes.
(in the process, improving the performance of Uri as these same
hashes can be used internally to accelerate Uri comparisons).

This will in turn mean that PathMap is made redundant.

Once these refactorings are completed, a fully composed path will
be necessary only when populating the PathTree or when displaying
the path to the user (e.g., logging, console).
  • Loading branch information
danij-deng committed Nov 9, 2012
1 parent 7777bc3 commit 9877284
Show file tree
Hide file tree
Showing 17 changed files with 97 additions and 61 deletions.
2 changes: 1 addition & 1 deletion doomsday/engine/api/uri.h
Expand Up @@ -191,7 +191,7 @@ namespace de
*
* @return Plain-text String representation.
*/
AutoStr* compose() const;
String compose() const;

/**
* Transform the uri into a human-friendly representation. Percent decoding done.
Expand Down
15 changes: 14 additions & 1 deletion doomsday/engine/portable/include/file.h
Expand Up @@ -83,14 +83,27 @@ class File1
/// @return Name of this file.
virtual String const& name() const;

/**
* Compose the a URI to this file.
*
* @param delimiter Delimit directory using this character.
*
* @return The composed URI.
*/
virtual de::Uri composeUri(QChar delimiter = '/') const;

/**
* Compose the absolute VFS path to this file.
*
* @param delimiter Delimit directory using this character.
*
* @return String containing the absolute path.
*
* @deprecated Prefer to use @ref composeUri() instead.
*/
virtual String composePath(char delimiter = '/') const;
String composePath(QChar delimiter = '/') const {
return composeUri(delimiter).compose();
}

/// @return @c true iff this file is contained by another.
bool isContained() const;
Expand Down
19 changes: 18 additions & 1 deletion doomsday/engine/portable/include/pathtree.h
Expand Up @@ -36,6 +36,7 @@
#include <de/String>
#include <de/str.h>
#include "pathmap.h"
#include "uri.h"

/**
* @defgroup pathComparisonFlags Path Comparison Flags
Expand Down Expand Up @@ -165,6 +166,18 @@ namespace de
*/
int comparePath(PathMap& candidatePath, int flags) const;

/**
* Composes the URI for this node. 'Composing' the path of a node is to
* upwardly reconstruct the whole path toward the root of the hierarchy.
*
* @param delimiter Names in the composed path hierarchy will be delimited
* with this character. Paths to branches always include
* a terminating delimiter.
*
* @return The composed uri.
*/
Uri composeUri(QChar delimiter = '/') const;

/**
* Composes the full path for this node. 'Composing' the path of a node is
* to upwardly reconstruct the whole path toward the root of the hierarchy.
Expand All @@ -174,8 +187,12 @@ namespace de
* a terminating delimiter.
*
* @return The composed path.
*
* @deprecated Prefer to use @ref composeUri() instead.
*/
String composePath(QChar delimiter = '/') const;
String composePath(QChar delimiter = '/') const {
return composeUri(delimiter).compose();
}

/**
* Sets the user-specified custom pointer.
Expand Down
10 changes: 5 additions & 5 deletions doomsday/engine/portable/include/wad.h
Expand Up @@ -78,17 +78,17 @@ class Wad : public File1
PathTree::Node& lumpDirectoryNode(int lumpIdx) const;

/**
* Compose the absolute VFS path to a lump contained by this file.
* Compose an absolute URI to a lump contained by this file.
*
* @note Always returns a valid string object. If @a lumpIdx is not valid a
* zero-length string is returned.
* @note Always returns a valid Uri object. If @a lumpIdx is not valid a
* zero-length Uri is returned.
*
* @param lumpIdx Logical index for the lump.
* @param delimiter Delimit directory separators using this character.
*
* @return String containing the absolute path.
* @return The absolute URI.
*/
String composeLumpPath(int lumpIdx, char delimiter = '/');
de::Uri composeLumpUri(int lumpIdx, QChar delimiter = '/');

/**
* Retrieve a lump contained by this file.
Expand Down
10 changes: 5 additions & 5 deletions doomsday/engine/portable/include/zip.h
Expand Up @@ -81,17 +81,17 @@ class Zip : public File1
PathTree::Node& lumpDirectoryNode(int lumpIdx) const;

/**
* Compose the absolute VFS path to a lump contained by this file.
* Compose an absolute URI to a lump contained by this file.
*
* @note Always returns a valid string object. If @a lumpIdx is not valid a
* zero-length string is returned.
* @note Always returns a valid Uri object. If @a lumpIdx is not valid a
* zero-length Uri is returned.
*
* @param lumpIdx Logical index for the lump.
* @param delimiter Delimit directory separators using this character.
*
* @return String containing the absolute path.
* @return The absolute URI.
*/
String composeLumpPath(int lumpIdx, char delimiter = '/');
de::Uri composeLumpUri(int lumpIdx, QChar delimiter = '/');

/**
* Retrieve a lump contained by this file.
Expand Down
3 changes: 2 additions & 1 deletion doomsday/engine/portable/src/con_data.cpp
Expand Up @@ -443,7 +443,8 @@ int CVar_Flags(const cvar_t* var)
AutoStr* CVar_ComposePath(cvar_t const* var)
{
DENG_ASSERT(var);
QByteArray path = reinterpret_cast<CVarDirectory::Node*>(var->directoryNode)->composePath(CVARDIRECTORY_DELIMITER).toUtf8();
CVarDirectory::Node& node = *reinterpret_cast<CVarDirectory::Node*>(var->directoryNode);
QByteArray path = node.composePath(CVARDIRECTORY_DELIMITER).toUtf8();
return AutoStr_FromTextStd(path.constData());
}

Expand Down
6 changes: 4 additions & 2 deletions doomsday/engine/portable/src/dd_main.cpp
Expand Up @@ -2338,7 +2338,8 @@ static bool tryLoadFile(String path, size_t baseOffset, de::File1** file)
{
de::FileHandle& hndl = App_FileSystem()->openFile(path, "rb", baseOffset, false /* no duplicates */);

QByteArray pathUtf8 = NativePath(hndl.file().composePath()).pretty().toUtf8();
de::Uri uri = hndl.file().composeUri();
QByteArray pathUtf8 = NativePath(uri.asText()).pretty().toUtf8();
VERBOSE( Con_Message("Loading \"%s\"...\n", pathUtf8.constData()) )
App_FileSystem()->index(hndl.file());

Expand All @@ -2363,7 +2364,8 @@ static bool tryUnloadFile(String path)
try
{
de::File1& file = App_FileSystem()->find(path);
QByteArray pathUtf8 = de::NativePath(file.composePath()).pretty().toUtf8();
de::Uri uri = file.composeUri();
QByteArray pathUtf8 = NativePath(uri.asText()).pretty().toUtf8();

// Do not attempt to unload a resource required by the current game.
if(games->currentGame().isRequiredFile(file))
Expand Down
4 changes: 3 additions & 1 deletion doomsday/engine/portable/src/def_main.cpp
Expand Up @@ -25,6 +25,8 @@
#include <string.h>
#include <ctype.h>

#include <de/NativePath>

#include "de_base.h"
#include "de_system.h"
#include "de_platform.h"
Expand Down Expand Up @@ -760,7 +762,7 @@ void Def_ReadLumpDefs(void)

if(!DED_ReadLump(&defs, lump.info().lumpIdx))
{
QByteArray path = lump.container().composePath().toUtf8();
QByteArray path = de::NativePath(lump.container().composePath()).pretty().toUtf8();
Con_Error("DD_ReadLumpDefs: Parse error reading \"%s:DD_DEFNS\".\n", path.constData());
}
}
Expand Down
11 changes: 7 additions & 4 deletions doomsday/engine/portable/src/file.cpp
Expand Up @@ -21,6 +21,8 @@
* 02110-1301 USA</small>
*/

#include <de/NativePath>

#include "de_base.h"
#include "de_filesys.h"

Expand Down Expand Up @@ -57,7 +59,7 @@ bool File1::isContained() const

File1& File1::container() const
{
if(!container_) throw NotContainedError("File1::container", "File \"" + composePath() + " is not contained");
if(!container_) throw NotContainedError("File1::container", "File \"" + NativePath(composePath()).pretty() + " is not contained");
return *container_;
}

Expand All @@ -66,10 +68,11 @@ de::FileHandle& File1::handle()
return *handle_;
}

String File1::composePath(char delimiter) const
de::Uri File1::composeUri(QChar delimiter) const
{
String result = path_;
if(delimiter != '/') throw Error("File1::composePath", "Non '/' delimiter not yet implemented");
QByteArray pathUtf8 = path_.toUtf8();
de::Uri result = de::Uri(pathUtf8.constData(), RC_NULL);
if(delimiter != '/') throw Error("File1::composeUri", "Non '/' delimiter not yet implemented");
return result;
}

Expand Down
5 changes: 2 additions & 3 deletions doomsday/engine/portable/src/fonts.cpp
Expand Up @@ -143,8 +143,7 @@ static inline fontnamespaceid_t namespaceIdForDirectoryNode(FontRepository::Node
static de::Uri composeUriForDirectoryNode(FontRepository::Node const& node)
{
Str const* namespaceName = Fonts_NamespaceName(namespaceIdForDirectoryNode(node));
QByteArray path = node.composePath(FONTS_PATH_DELIMITER).toUtf8();
return de::Uri(path.constData(), RC_NULL).setScheme(Str_Text(namespaceName));
return node.composeUri(FONTS_PATH_DELIMITER).setScheme(Str_Text(namespaceName));
}

/// @pre fontIdMap has been initialized and is large enough!
Expand Down Expand Up @@ -1357,7 +1356,7 @@ static int composeAndCompareDirectoryNodePaths(void const* a, void const* b)
QByteArray pathBUtf8 = nodeB.composePath(FONTS_PATH_DELIMITER).toUtf8();
AutoStr* pathA = Str_PercentDecode(AutoStr_FromTextStd(pathAUtf8.constData()));
AutoStr* pathB = Str_PercentDecode(AutoStr_FromTextStd(pathBUtf8.constData()));
return stricmp(Str_Text(pathA), Str_Text(pathB));
return qstricmp(Str_Text(pathA), Str_Text(pathB));
}

/**
Expand Down
2 changes: 1 addition & 1 deletion doomsday/engine/portable/src/fs_main.cpp
Expand Up @@ -461,7 +461,7 @@ FS1& FS1::index(de::File1& file)
// Ensure this hasn't yet been indexed.
FileList::const_iterator found = findListFile(d->loadedFiles, file);
if(found != d->loadedFiles.end())
throw de::Error("FS1::index", "File \"" + file.composePath() + "\" has already been indexed");
throw de::Error("FS1::index", "File \"" + NativePath(file.composePath()).pretty() + "\" has already been indexed");
#endif

// Publish lumps to one or more indexes?
Expand Down
15 changes: 7 additions & 8 deletions doomsday/engine/portable/src/pathtreenode.cpp
Expand Up @@ -219,7 +219,7 @@ static size_t maxStackDepth;

typedef struct pathconstructorparams_s {
size_t length;
String& composedPath;
String composedPath;
QChar delimiter;
} pathconstructorparams_t;

Expand Down Expand Up @@ -278,11 +278,9 @@ static void pathConstructor(pathconstructorparams_t& parm, PathTree::Node const&
*
* Perhaps a fixed size MRU cache? -ds
*/
String PathTree::Node::composePath(QChar delimiter) const
Uri PathTree::Node::composeUri(QChar delimiter) const
{
String result;

pathconstructorparams_t parm = { 0, result, delimiter };
pathconstructorparams_t parm = { 0, String(), delimiter };
#ifdef LIBDENG_STACK_MONITOR
stackStart = &parm;
#endif
Expand All @@ -296,16 +294,17 @@ String PathTree::Node::composePath(QChar delimiter) const

// Terminating delimiter for branches.
if(!delimiter.isNull() && !isLeaf())
result.append(delimiter);
parm.composedPath += delimiter;

DENG2_ASSERT(result.size() == (int)parm.length);
DENG2_ASSERT(parm.composedPath.length() == (int)parm.length);

#ifdef LIBDENG_STACK_MONITOR
LOG_AS("pathConstructor");
LOG_INFO("Max stack depth: %1 bytes") << maxStackDepth;
#endif

return result;
QByteArray pathUtf8 = parm.composedPath.toUtf8();
return Uri(pathUtf8.constData(), RC_NULL);
}

} // namespace de
3 changes: 1 addition & 2 deletions doomsday/engine/portable/src/resource/materials.cpp
Expand Up @@ -264,8 +264,7 @@ static materialnamespaceid_t namespaceIdForDirectory(MaterialRepository& directo
static de::Uri composeUriForDirectoryNode(MaterialRepository::Node const& node)
{
Str const* namespaceName = Materials_NamespaceName(namespaceIdForDirectory(node.tree()));
QByteArray path = node.composePath(MATERIALS_PATH_DELIMITER).toUtf8();
return de::Uri(path.constData(), RC_NULL).setScheme(Str_Text(namespaceName));
return node.composeUri(MATERIALS_PATH_DELIMITER).setScheme(Str_Text(namespaceName));
}

static MaterialAnim* getAnimGroup(int number)
Expand Down
3 changes: 1 addition & 2 deletions doomsday/engine/portable/src/resource/textures.cpp
Expand Up @@ -151,8 +151,7 @@ static inline texturenamespaceid_t namespaceIdForDirectoryNode(TextureRepository
static de::Uri composeUriForDirectoryNode(TextureRepository::Node const& node)
{
Str const* namespaceName = Textures_NamespaceName(namespaceIdForDirectoryNode(node));
QByteArray path = node.composePath(TEXTURES_PATH_DELIMITER).toUtf8();
return de::Uri(path.constData(), RC_NULL).setScheme(Str_Text(namespaceName));
return node.composeUri(TEXTURES_PATH_DELIMITER).setScheme(Str_Text(namespaceName));
}

/// @pre textureIdMap has been initialized and is large enough!
Expand Down
18 changes: 10 additions & 8 deletions doomsday/engine/portable/src/uri.cpp
Expand Up @@ -373,21 +373,22 @@ Uri& Uri::setUri(ddstring_t const* path)
return setUri(path != 0? Str_Text(path) : 0);
}

AutoStr* Uri::compose() const
String Uri::compose() const
{
AutoStr* out = AutoStr_New();
if(!Str_IsEmpty(&d->scheme))
Str_Appendf(out, "%s:", Str_Text(&d->scheme));
Str_Append(out, Str_Text(&d->path));
return out;
{
return String(Str_Text(&d->scheme)) + ":" + String(Str_Text(&d->path));
}
return String(Str_Text(&d->path));
}

String Uri::asText() const
{
// Just compose it for now, we can worry about making it 'pretty' later.
AutoStr* path = compose();
QByteArray composed = compose().toUtf8();
AutoStr* path = AutoStr_FromTextStd(composed.constData());
Str_PercentDecode(path);
return QString::fromUtf8(Str_Text(path));
return String(Str_Text(path));
}

static void writeUri(ddstring_t const* scheme, ddstring_t const* path, struct writer_s& writer)
Expand Down Expand Up @@ -572,7 +573,8 @@ Uri* Uri_SetUriStr(Uri* uri, ddstring_t const* path)
AutoStr* Uri_Compose(Uri const* uri)
{
SELF_CONST(uri);
return self->compose();
QByteArray composed = self->compose().toUtf8();
return AutoStr_FromTextStd(composed.constData());
}

AutoStr* Uri_ToString(Uri const* uri)
Expand Down
14 changes: 7 additions & 7 deletions doomsday/engine/portable/src/wad.cpp
Expand Up @@ -70,15 +70,15 @@ class WadFile : public File1
}

/**
* Compose the absolute VFS path to this file.
* Compose an absolute URI to this file.
*
* @param delimiter Delimit directory using this character.
*
* @return String containing the absolute path.
* @return The absolute URI.
*/
String composePath(char delimiter = '/') const
Uri composeUri(QChar delimiter = '/') const
{
return dynamic_cast<Wad&>(container()).composeLumpPath(info_.lumpIdx, delimiter);
return dynamic_cast<Wad&>(container()).composeLumpUri(info_.lumpIdx, delimiter);
}

/**
Expand Down Expand Up @@ -407,10 +407,10 @@ PathTree::Node& Wad::lumpDirectoryNode(int lumpIdx) const
return *((*d->lumpNodeLut)[lumpIdx]);
}

String Wad::composeLumpPath(int lumpIdx, char delimiter)
de::Uri Wad::composeLumpUri(int lumpIdx, QChar delimiter)
{
if(!isValidIndex(lumpIdx)) return String();
return lump(lumpIdx).directoryNode().composePath(delimiter);
if(!isValidIndex(lumpIdx)) return de::Uri();
return lump(lumpIdx).directoryNode().composeUri(delimiter);
}

File1& Wad::lump(int lumpIdx)
Expand Down

0 comments on commit 9877284

Please sign in to comment.