From fa0ba809326eba2943dfb1842c25d1b042765985 Mon Sep 17 00:00:00 2001 From: danij Date: Tue, 9 Oct 2012 06:14:39 +0100 Subject: [PATCH] Refactor: Replaced fileidentifier_t with FileId FileId is a new C++ class which thinly wraps an MD5 hash (produced with QCryptographicHash) into an object. FileId instances can be built directly from file paths using the static fromPath() method or constructed from a known Md5Hash. FileId also implements an asText() method (and, operator String() ) which converts the hash to a textual, hexidecimal representation and as implementor of LogEntry::Arg::Base - can written directly to a Log. The file system's management of file identifiers has been replaced with an always-sorted QList of FileId instances. --- doomsday/engine/portable/include/fileid.h | 134 +++++++++++++++ doomsday/engine/portable/include/fs_main.h | 2 +- doomsday/engine/portable/src/fs_main.cpp | 190 ++++----------------- 3 files changed, 172 insertions(+), 154 deletions(-) create mode 100644 doomsday/engine/portable/include/fileid.h diff --git a/doomsday/engine/portable/include/fileid.h b/doomsday/engine/portable/include/fileid.h new file mode 100644 index 0000000000..8a5857a577 --- /dev/null +++ b/doomsday/engine/portable/include/fileid.h @@ -0,0 +1,134 @@ +/** + * @file fileid.h + * + * Implements a file identifier in terms of a MD5 hash of its absolute path. + * + * @ingroup types + * + * @author Copyright © 2003-2012 Jaakko Keränen + * @author Copyright © 2005-2012 Daniel Swanson + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. You should have received a copy of the GNU + * General Public License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef LIBDENG_FILEID_H +#define LIBDENG_FILEID_H + +#include "fs_util.h" +#include +#include + +#include +#include +#include + +namespace de { + +/** + * File identifier (an MD5 hash). + */ +class FileId : public LogEntry::Arg::Base +{ +public: + typedef QByteArray Md5Hash; + +public: + explicit FileId(Md5Hash _md5) : md5_(_md5.left(16)) + {} + + FileId(FileId const& other) + : md5_(other.md5()) + {} + + FileId& operator = (FileId other) + { + swap(*this, other); + return *this; + } + + /// @return @c true= this FileId is lexically less than @a other. + bool operator < (FileId const& other) const { return md5_ < other.md5_; } + + /// @return @c true= this FileId is equal to @a other (identical hashes). + bool operator == (FileId const& other) const { return md5_ == other.md5_; } + + /// @return @c true= this FileId is not equal @a other (differing hashes). + bool operator != (FileId const& other) const { return md5_ != other.md5_; } + + friend void swap(FileId& first, FileId& second) // nothrow + { +#ifdef DENG2_QT_4_8_OR_NEWER + first.md5_.swap(second.md5_); +#else + using std::swap; + swap(first.md5_, second.md5_); +#endif + } + + /// Converts this FileId to a text string. + operator String () const { return asText(); } + + /// Converts this FileId to a text string. + String asText() const + { + String txt; + txt.reserve(32); + for(int i = 0; i < 16; ++i) + { + txt += String("%1").arg(String::number((byte)md5_.at(i), 16), 2, '0'); + } + return txt; + } + + // Implements LogEntry::Arg::Base. + LogEntry::Arg::Type logEntryArgType() const { return LogEntry::Arg::STRING; } + + /// @return Md5hash for this FileId. + Md5Hash const& md5() const { return md5_; } + + /** + * Constructs a new FileId instance by hashing the absolute @a path. + * @param path Path to be hashed. + * @return Newly construced FileId. + */ + static FileId fromPath(char const* path) + { + return FileId(hash(path)); + } + + /** + * Calculate an MD5 identifier for the absolute @a path. + * @return MD5 hash of the path. + */ + static Md5Hash hash(char const* path) + { + // First normalize the name. + AutoStr* absPath = Str_Set(AutoStr_NewStd(), path); + F_MakeAbsolute(absPath, absPath); + F_FixSlashes(absPath, absPath); +#if defined(WIN32) || defined(MACOSX) + // This is a case insensitive operation. + strupr(Str_Text(absPath)); +#endif + return QCryptographicHash::hash(QByteArray(Str_Text(absPath)), QCryptographicHash::Md5); + } + +private: + Md5Hash md5_; +}; + +} // namespace de + +#endif /* LIBDENG_FILEID_H */ diff --git a/doomsday/engine/portable/include/fs_main.h b/doomsday/engine/portable/include/fs_main.h index 590c99cae8..5a0fc45f80 100644 --- a/doomsday/engine/portable/include/fs_main.h +++ b/doomsday/engine/portable/include/fs_main.h @@ -122,7 +122,7 @@ class FS /** * @return @c true if a file exists at @a path which can be opened for reading. */ - static bool access(char const* path); + static bool accessFile(char const* path); /** * @return The time when the file was last modified, as seconds since diff --git a/doomsday/engine/portable/src/fs_main.cpp b/doomsday/engine/portable/src/fs_main.cpp index b10a59772d..14d7a06a63 100644 --- a/doomsday/engine/portable/src/fs_main.cpp +++ b/doomsday/engine/portable/src/fs_main.cpp @@ -30,16 +30,20 @@ #include "de_console.h" #include "de_filesys.h" +#include "fileid.h" #include "game.h" #include "genericfile.h" #include "lumpindex.h" #include "lumpinfo.h" #include "lumpfile.h" -#include "m_md5.h" +//#include "m_md5.h" #include "m_misc.h" // for M_FindWhite() #include "wadfile.h" #include "zipfile.h" +#include +#include + #include #include @@ -49,11 +53,12 @@ D_CMD(ListFiles); D_CMD(ListLumps); using de::FS; -using de::AbstractFile; using de::DFile; -using de::DFileBuilder; using de::GenericFile; -using de::LumpFile; using de::LumpIndex; -using de::PathDirectory; using de::PathDirectoryNode; -using de::WadFile; using de::ZipFile; +using de::AbstractFile; using de::DFile; +using de::DFileBuilder; using de::FileId; +using de::GenericFile; using de::LumpFile; +using de::LumpIndex; using de::PathDirectory; +using de::PathDirectoryNode; using de::WadFile; +using de::ZipFile; /// Base for indicies in the auxiliary lump index. int const AUXILIARY_BASE = 100000000; @@ -74,27 +79,14 @@ typedef struct { ddstring_t destination; // Absolute path. } vdmapping_t; -/** - * FileIdentifier - */ -#define FILEIDENTIFIERID_T_MAXLEN 16 -#define FILEIDENTIFIERID_T_LASTINDEX 15 -typedef byte fileidentifierid_t[FILEIDENTIFIERID_T_MAXLEN]; - -typedef struct fileidentifier_s -{ - fileidentifierid_t hash; -} fileidentifier_t; - static bool inited = false; static bool loadingForStartup; static de::FileList* openFiles; static de::FileList* loadedFiles; -static uint fileIdentifiersCount; -static uint fileIdentifiersMax; -static fileidentifier_t* fileIdentifiers; +typedef QList FileIds; +static FileIds fileIds; static LumpIndex* zipLumpIndex; @@ -125,14 +117,6 @@ static FILE* findRealFile(char const* path, char const* mymode, ddstring_t** fou /// @return @c true if the FileId associated with @a path was released. static bool releaseFileId(char const* path); -/** - * Calculate an identifier for the file based on its full path name. - * The identifier is the MD5 hash of the path. - */ -static void generateFileId(char const* str, byte identifier[16]); - -static void printFileId(byte identifier[16]); - void FS::consoleRegister() { C_CMD("dir", "", Dir); @@ -153,21 +137,6 @@ static void errorIfNotInited(const char* callerName) exit(1); } -static fileidentifier_t* findFileIdentifierForId(fileidentifierid_t id) -{ - fileidentifier_t* found = 0; - if(fileIdentifiersCount) - { - uint i = 0; - do - { - if(!memcmp(fileIdentifiers[i].hash, id, FILEIDENTIFIERID_T_LASTINDEX)) - found = &fileIdentifiers[i]; - } while(!found && ++i < fileIdentifiersCount); - } - return found; -} - /** * @note Performance is O(n). * @return @c iterator pointing to list->end() if not found. @@ -313,82 +282,21 @@ static bool unloadFile(char const* path, bool permitRequired = false, bool quiet return true; } -static void clearFileIds() -{ - if(fileIdentifiers) - { - M_Free(fileIdentifiers); fileIdentifiers = 0; - } - if(fileIdentifiersCount) - { - fileIdentifiersCount = fileIdentifiersMax = 0; - } -} - -static void printFileId(byte identifier[16]) -{ - if(!identifier) return; - for(uint i = 0; i < 16; ++i) - { - Con_Printf("%02x", identifier[i]); - } -} - -static void generateFileId(char const* str, byte identifier[16]) -{ - // First normalize the name. - ddstring_t absPath; Str_Init(&absPath); - Str_Set(&absPath, str); - F_MakeAbsolute(&absPath, &absPath); - F_FixSlashes(&absPath, &absPath); - -#if defined(WIN32) || defined(MACOSX) - // This is a case insensitive operation. - strupr(Str_Text(&absPath)); -#endif - - md5_ctx_t context; md5_init(&context); - md5_update(&context, (byte*) Str_Text(&absPath), (unsigned int) Str_Length(&absPath)); - md5_final(&context, identifier); - - Str_Free(&absPath); -} - bool FS::checkFileId(char const* path) { DENG_ASSERT(path); - - if(!access(path)) return false; + if(!accessFile(path)) return false; // Calculate the identifier. - fileidentifierid_t id; - generateFileId(path, id); - - if(findFileIdentifierForId(id)) return false; - - // Allocate a new entry. - fileIdentifiersCount++; - if(fileIdentifiersCount > fileIdentifiersMax) - { - if(!fileIdentifiersMax) - fileIdentifiersMax = 16; - else - fileIdentifiersMax *= 2; - - fileIdentifiers = (fileidentifier_t*) M_Realloc(fileIdentifiers, fileIdentifiersMax * sizeof *fileIdentifiers); - if(!fileIdentifiers) Con_Error("FS::checkFileId: Failed on (re)allocation of %lu bytes while enlarging fileIdentifiers.", (unsigned long) (fileIdentifiersMax * sizeof *fileIdentifiers)); - - memset(fileIdentifiers + fileIdentifiersCount, 0, (fileIdentifiersMax - fileIdentifiersCount) * sizeof *fileIdentifiers); - } + FileId fileId = FileId::fromPath(path); + FileIds::iterator place = qLowerBound(fileIds.begin(), fileIds.end(), fileId); + if(place != fileIds.end() && *place == fileId) return false; #if _DEBUG - VERBOSE( - Con_Printf("Added file identifier "); - printFileId(id); - Con_Printf(" - \"%s\"\n", F_PrettyPath(path)) ) + LOG_VERBOSE("Added FileId %s - \"%s\"") << fileId << F_PrettyPath(path); #endif - memcpy(fileIdentifiers[fileIdentifiersCount - 1].hash, id, sizeof(id)); + fileIds.insert(place, fileId); return true; } @@ -396,26 +304,14 @@ static bool releaseFileId(char const* path) { if(path && path[0]) { - fileidentifierid_t id; - generateFileId(path, id); - - fileidentifier_t* fileIdentifier = findFileIdentifierForId(id); - if(fileIdentifier) + FileId fileId = FileId::fromPath(path); + FileIds::iterator place = qLowerBound(fileIds.begin(), fileIds.end(), fileId); + if(place != fileIds.end() && *place == fileId) { - size_t index = fileIdentifier - fileIdentifiers; - if(index < fileIdentifiersCount-1) - { - memmove(fileIdentifiers + index, fileIdentifiers + index + 1, fileIdentifiersCount - index - 1); - } - memset(fileIdentifiers + fileIdentifiersCount, 0, sizeof *fileIdentifiers); - --fileIdentifiersCount; - #if _DEBUG - VERBOSE( - Con_Printf("Released file identifier "); - printFileId(id); - Con_Printf(" - \"%s\"\n", F_PrettyPath(path)) ) + LOG_VERBOSE("Released FileId %s - \"%s\"") << *place << F_PrettyPath(path); #endif + fileIds.erase(place); return true; } } @@ -424,7 +320,7 @@ static bool releaseFileId(char const* path) void FS::resetFileIds() { - fileIdentifiersCount = 0; + fileIds.clear(); } void F_InitLumpInfo(LumpInfo* info) @@ -480,7 +376,7 @@ void F_Shutdown(void) clearOpenFiles(); delete openFiles; openFiles = 0; - clearFileIds(); // Should be null-op if bookkeeping is correct. + fileIds.clear(); // Should be null-op if bookkeeping is correct. clearLumpIndexes(); DFileBuilder::shutdown(); @@ -521,25 +417,15 @@ static int unloadListFiles(de::FileList* list, bool nonStartup) #if _DEBUG static void logOrphanedFileIdentifiers() { - fileidentifierid_t nullId; - memset(nullId, 0, sizeof nullId); - uint orphanCount = 0; - for(uint i = 0; i < fileIdentifiersCount; ++i) + DENG2_FOR_EACH(i, fileIds, FileIds::const_iterator) { - fileidentifier_t* id = fileIdentifiers + i; - if(!memcmp(id->hash, &nullId, FILEIDENTIFIERID_T_LASTINDEX)) continue; - if(!orphanCount) { - Con_Printf("Warning: Orphan file identifiers:\n"); + LOG_MSG("Warning: Orphan FileIds:"); } - - Con_Printf(" %u - ", orphanCount); - printFileId(id->hash); - Con_Printf("\n"); - - orphanCount++; + LOG_MSG(" %u - %s") << orphanCount << *i; + ++orphanCount; } } #endif @@ -547,17 +433,15 @@ static void logOrphanedFileIdentifiers() #if _DEBUG static void printFileList(de::FileList* list) { - byte id[16]; if(!list) return; for(int i = 0; i < list->size(); ++i) { DFile* hndl = (*list)[i]; AbstractFile* file = hndl->file(); - - Con_Printf(" %c%u: ", file->hasStartup()? '*':' ', i); - generateFileId(Str_Text(file->path()), id); - printFileId(id); - Con_Printf(" - \"%s\" [handle: %p]\n", F_PrettyPath(Str_Text(file->path())), (void*)&hndl); + FileId fileId = FileId::fromPath(Str_Text(file->path())); + LOG_MSG(" %c%d: %s - \"%s\" [handle: %p]") + << (file->hasStartup()? '*' : ' ') << i + << fileId << F_PrettyPath(Str_Text(file->path())) << (void*)&hndl; } } #endif @@ -1649,7 +1533,7 @@ DFile* FS::openFile(char const* path, char const* mode, size_t baseOffset, bool return tryOpenFile(path, mode, baseOffset, allowDuplicate); } -bool FS::access(char const* path) +bool FS::accessFile(char const* path) { DFile* hndl = tryOpenFile(path, "rx", 0, true); if(!hndl) return false; @@ -1686,7 +1570,7 @@ AbstractFile* FS::addFile(char const* path, size_t baseOffset) DFile* hndl = openFile(path, "rb", baseOffset, false); if(!hndl) { - if(access(path)) + if(accessFile(path)) { Con_Message("\"%s\" already loaded.\n", F_PrettyPath(path)); } @@ -2367,7 +2251,7 @@ int F_LumpCount(void) int F_Access(char const* path) { - return FS::access(path)? 1 : 0; + return FS::accessFile(path)? 1 : 0; } uint F_GetLastModified(char const* path)