From e63c80c613d906e1d74fd4030f8227b9145b3abd Mon Sep 17 00:00:00 2001 From: Vladislav Belov Date: Sun, 5 Oct 2025 01:11:03 +0200 Subject: [PATCH] Reduces string allocations in paths. --- source/lib/file/archive/archive_zip.cpp | 10 +++---- source/lib/file/common/file_loader.h | 2 +- source/lib/file/common/real_directory.h | 10 +++---- source/lib/file/file_system.cpp | 9 +++--- source/lib/file/vfs/tests/test_vfs_tree.h | 14 +++++---- source/lib/path.h | 35 +++++++++++++++++------ source/ps/CacheLoader.cpp | 4 +-- source/scriptinterface/ModuleLoader.cpp | 2 +- 8 files changed, 54 insertions(+), 32 deletions(-) diff --git a/source/lib/file/archive/archive_zip.cpp b/source/lib/file/archive/archive_zip.cpp index f64fe3fbdb..a6e011a51a 100644 --- a/source/lib/file/archive/archive_zip.cpp +++ b/source/lib/file/archive/archive_zip.cpp @@ -311,7 +311,7 @@ cassert(sizeof(ECDR) == 22); // ArchiveFile_Zip //----------------------------------------------------------------------------- -class ArchiveFile_Zip : public IArchiveFile +class ArchiveFile_Zip final : public IArchiveFile { public: ArchiveFile_Zip(const PFile& file, off_t ofs, off_t csize, u32 checksum, ZipMethod method) @@ -321,22 +321,22 @@ public: { } - virtual size_t Precedence() const + size_t Precedence() const override { return 2u; } - virtual wchar_t LocationCode() const + wchar_t LocationCode() const override { return 'A'; } - virtual OsPath Path() const + const OsPath& Path() const override { return m_file->Pathname(); } - virtual Status Load(const OsPath& /*name*/, const std::shared_ptr& buf, size_t size) const + Status Load(const OsPath& /*name*/, const std::shared_ptr& buf, size_t size) const override { AdjustOffset(); diff --git a/source/lib/file/common/file_loader.h b/source/lib/file/common/file_loader.h index 1627597375..54126a990a 100644 --- a/source/lib/file/common/file_loader.h +++ b/source/lib/file/common/file_loader.h @@ -36,7 +36,7 @@ struct IFileLoader virtual size_t Precedence() const = 0; virtual wchar_t LocationCode() const = 0; - virtual OsPath Path() const = 0; + virtual const OsPath& Path() const = 0; virtual Status Load(const OsPath& name, const std::shared_ptr& buf, size_t size) const = 0; }; diff --git a/source/lib/file/common/real_directory.h b/source/lib/file/common/real_directory.h index 99e57471ea..2e16679730 100644 --- a/source/lib/file/common/real_directory.h +++ b/source/lib/file/common/real_directory.h @@ -33,7 +33,7 @@ #include #include -class RealDirectory : public IFileLoader +class RealDirectory final : public IFileLoader { NONCOPYABLE(RealDirectory); public: @@ -50,13 +50,13 @@ public: } // IFileLoader - virtual size_t Precedence() const; - virtual wchar_t LocationCode() const; - virtual OsPath Path() const + size_t Precedence() const override; + wchar_t LocationCode() const override; + const OsPath& Path() const override { return m_path; } - virtual Status Load(const OsPath& name, const std::shared_ptr& buf, size_t size) const; + Status Load(const OsPath& name, const std::shared_ptr& buf, size_t size) const override; Status Store(const OsPath& name, const std::shared_ptr& fileContents, size_t size); diff --git a/source/lib/file/file_system.cpp b/source/lib/file/file_system.cpp index 619dc8213c..9991f64e1a 100644 --- a/source/lib/file/file_system.cpp +++ b/source/lib/file/file_system.cpp @@ -114,7 +114,8 @@ Status GetDirectoryEntries(const OsPath& path, CFileInfos* files, DirectoryNames for(size_t i = 0; osEnt->d_name[i] != '\0'; i++) RETURN_STATUS_IF_ERR(Path::Validate(osEnt->d_name[i])); - const OsPath name(osEnt->d_name); + + const std::wstring_view name{osEnt->d_name}; // get file information (mode, size, mtime) struct stat s; @@ -124,7 +125,7 @@ Status GetDirectoryEntries(const OsPath& path, CFileInfos* files, DirectoryNames #else // .. call regular stat(). errno = 0; - const OsPath pathname = path / name; + const OsPath pathname = path / OsPath(osEnt->d_name); if(wstat(pathname, &s) != 0) { if(errno == ENOENT) @@ -140,9 +141,9 @@ Status GetDirectoryEntries(const OsPath& path, CFileInfos* files, DirectoryNames #endif if(files && S_ISREG(s.st_mode)) - files->push_back(CFileInfo(name, s.st_size, s.st_mtime)); + files->emplace_back(osEnt->d_name, s.st_size, s.st_mtime); else if(subdirectoryNames && S_ISDIR(s.st_mode) && name != L"." && name != L"..") - subdirectoryNames->push_back(name); + subdirectoryNames->emplace_back(osEnt->d_name); } } diff --git a/source/lib/file/vfs/tests/test_vfs_tree.h b/source/lib/file/vfs/tests/test_vfs_tree.h index b2fce3788c..62a58b6a18 100644 --- a/source/lib/file/vfs/tests/test_vfs_tree.h +++ b/source/lib/file/vfs/tests/test_vfs_tree.h @@ -36,21 +36,23 @@ class MockLoader : public IFileLoader { -private: - size_t m_Precedence; public: MockLoader(size_t precedence) : m_Precedence(precedence) { } - size_t Precedence() const { return m_Precedence; } - wchar_t LocationCode() const { return L'\0'; } - OsPath Path() const { return L"";} - Status Load(const OsPath& /*name*/, const std::shared_ptr& /*buf*/, size_t /*size*/) const + size_t Precedence() const override { return m_Precedence; } + wchar_t LocationCode() const override { return L'\0'; } + const OsPath& Path() const override { return m_Path; } + Status Load(const OsPath& /*name*/, const std::shared_ptr& /*buf*/, size_t /*size*/) const override { return INFO::OK; } + +private: + size_t m_Precedence; + OsPath m_Path; }; class TestVfsTree : public CxxTest::TestSuite diff --git a/source/lib/path.h b/source/lib/path.h index 2a50ed4218..b98ce4aefc 100644 --- a/source/lib/path.h +++ b/source/lib/path.h @@ -49,6 +49,7 @@ #include #include #include +#include namespace ERR { @@ -95,6 +96,12 @@ public: DetectSeparator(); } + Path(Path&& p) + : path(std::move(p.path)) + { + DetectSeparator(); + } + Path(const char* p) : path((const unsigned char*)p, (const unsigned char*)p+strlen(p)) // interpret bytes as unsigned; makes no difference for ASCII, @@ -115,7 +122,13 @@ public: DetectSeparator(); } - Path(const std::wstring& s) + Path(std::wstring s) + : path(std::move(s)) + { + DetectSeparator(); + } + + Path(const std::wstring_view& s) : path(s) { DetectSeparator(); @@ -128,6 +141,13 @@ public: return *this; } + Path& operator=(Path&& rhs) + { + path = std::move(rhs.path); + DetectSeparator(); + return *this; + } + bool empty() const { return path.empty(); @@ -217,14 +237,13 @@ public: return filename.string().substr(0, idxDot); } - // (Path return type allows callers to use our operator==) - Path Extension() const + std::wstring_view Extension() const { - const Path filename = Filename(); - const size_t idxDot = filename.string().find_last_of('.'); + const std::wstring_view filename{path}; + const size_t idxDot = filename.find_last_of('.'); if(idxDot == String::npos) - return Path(); - return filename.string().substr(idxDot); + return {}; + return filename.substr(idxDot); } Path ChangeExtension(Path extension) const @@ -232,7 +251,7 @@ public: return Parent() / Path(Basename().string() + extension.string()); } - Path operator/(Path rhs) const + Path operator/(const Path& rhs) const { Path ret = *this; if(ret.path.empty()) // (empty paths assume '/') diff --git a/source/ps/CacheLoader.cpp b/source/ps/CacheLoader.cpp index bbdaf8ceae..26ed1f212e 100644 --- a/source/ps/CacheLoader.cpp +++ b/source/ps/CacheLoader.cpp @@ -116,7 +116,7 @@ bool CCacheLoader::CanUseArchiveCache(const VfsPath& sourcePath, const VfsPath& VfsPath CCacheLoader::ArchiveCachePath(const VfsPath& sourcePath) const { - return sourcePath.ChangeExtension(sourcePath.Extension().string() + L".cached" + m_FileExtension); + return sourcePath.ChangeExtension(std::wstring{sourcePath.Extension()} + L".cached" + m_FileExtension); } VfsPath CCacheLoader::LooseCachePath(const VfsPath& sourcePath, const MD5& initialHash, u32 version) @@ -148,7 +148,7 @@ VfsPath CCacheLoader::LooseCachePath(const VfsPath& sourcePath, const MD5& initi return VfsPath("cache") / path_name_only(path.BeforeCommon(sourcePath).Parent().string().c_str()) / - sourcePath.ChangeExtension(sourcePath.Extension().string() + + sourcePath.ChangeExtension(std::wstring{sourcePath.Extension()} + L"." + // Use a short prefix of the full hash (we don't need high collision-resistance) wstring_from_utf8(Hexify(digest, 8)) + diff --git a/source/scriptinterface/ModuleLoader.cpp b/source/scriptinterface/ModuleLoader.cpp index 16e1865469..9e93ec9911 100644 --- a/source/scriptinterface/ModuleLoader.cpp +++ b/source/scriptinterface/ModuleLoader.cpp @@ -126,7 +126,7 @@ VfsPath GetBaseFilename(const VfsPath& filename) if (!VfsFileExists(filePath)) throw std::runtime_error{fmt::format("The file \"{}\" does not exist.", filePath.string8())}; - if (filePath.Extension().string8() != ".js") + if (filePath.Extension() != L".js") { throw std::runtime_error{fmt::format("The file \"{}\" is not a JavaScript module.", filePath.string8())};