diff options
| author | isanae <14251494+isanae@users.noreply.github.com> | 2019-12-19 23:00:18 -0500 |
|---|---|---|
| committer | isanae <14251494+isanae@users.noreply.github.com> | 2020-02-04 03:33:19 -0500 |
| commit | 2be531470d54fa56307e392ad5bdfbc02048a744 (patch) | |
| tree | 4ab9e436d40b1650fdce9378bdef83b970cd2136 /src | |
| parent | 0a908c49625fe0e54bc45e29fe8c4908d20b0dbe (diff) | |
renamed ToLower() to avoid confusion with in-place vs copy
pre-hashed file lookup in DirectoryEntry
Diffstat (limited to 'src')
| -rw-r--r-- | src/filetree.cpp | 5 | ||||
| -rw-r--r-- | src/filetree.h | 1 | ||||
| -rw-r--r-- | src/filetreeitem.cpp | 26 | ||||
| -rw-r--r-- | src/filetreeitem.h | 31 | ||||
| -rw-r--r-- | src/filetreemodel.cpp | 40 | ||||
| -rw-r--r-- | src/filetreemodel.h | 13 | ||||
| -rw-r--r-- | src/organizercore.cpp | 10 | ||||
| -rw-r--r-- | src/organizercore.h | 12 | ||||
| -rw-r--r-- | src/shared/directoryentry.cpp | 125 | ||||
| -rw-r--r-- | src/shared/directoryentry.h | 88 | ||||
| -rw-r--r-- | src/shared/util.cpp | 12 | ||||
| -rw-r--r-- | src/shared/util.h | 8 |
12 files changed, 258 insertions, 113 deletions
diff --git a/src/filetree.cpp b/src/filetree.cpp index 8fc9e908..71a49200 100644 --- a/src/filetree.cpp +++ b/src/filetree.cpp @@ -144,6 +144,11 @@ void FileTree::refresh() m_model->refresh(); } +void FileTree::clear() +{ + m_model->clear(); +} + FileTreeItem* FileTree::singleSelection() { const auto sel = m_tree->selectionModel()->selectedRows(); diff --git a/src/filetree.h b/src/filetree.h index 77d5012c..1a17354f 100644 --- a/src/filetree.h +++ b/src/filetree.h @@ -20,6 +20,7 @@ public: FileTreeModel* model(); void refresh(); + void clear(); void open(); void openHooked(); diff --git a/src/filetreeitem.cpp b/src/filetreeitem.cpp index 0469dfcc..39cdd9c4 100644 --- a/src/filetreeitem.cpp +++ b/src/filetreeitem.cpp @@ -6,24 +6,22 @@ using namespace MOBase; using namespace MOShared; -FileTreeItem::FileTreeItem() - : m_flags(NoFlags), m_loaded(false) -{ -} - FileTreeItem::FileTreeItem( FileTreeItem* parent, int originID, std::wstring dataRelativeParentPath, std::wstring realPath, Flags flags, std::wstring file, std::wstring mod) : - m_parent(parent), m_originID(originID), - m_virtualParentPath(QString::fromStdWString(dataRelativeParentPath)), - m_realPath(QString::fromStdWString(realPath)), - m_flags(flags), - m_wsFile(file), m_wsLcFile(ToLower(file)), - m_file(QString::fromStdWString(file)), - m_mod(QString::fromStdWString(mod)), - m_loaded(false), - m_expanded(false) + m_parent(parent), + m_originID(originID), + m_virtualParentPath(QString::fromStdWString(dataRelativeParentPath)), + m_realPath(QString::fromStdWString(realPath)), + m_flags(flags), + m_wsFile(file), + m_wsLcFile(ToLowerCopy(file)), + m_key(m_wsLcFile), + m_file(QString::fromStdWString(file)), + m_mod(QString::fromStdWString(mod)), + m_loaded(false), + m_expanded(false) { } diff --git a/src/filetreeitem.h b/src/filetreeitem.h index 1e820f3f..fb9eccce 100644 --- a/src/filetreeitem.h +++ b/src/filetreeitem.h @@ -1,6 +1,7 @@ #ifndef MODORGANIZER_FILETREEITEM_INCLUDED #define MODORGANIZER_FILETREEITEM_INCLUDED +#include "directoryentry.h" #include <QFileIconProvider> class FileTreeItem @@ -16,7 +17,6 @@ public: Q_DECLARE_FLAGS(Flags, Flag); - FileTreeItem(); FileTreeItem( FileTreeItem* parent, int originID, std::wstring dataRelativeParentPath, std::wstring realPath, Flags flags, @@ -35,6 +35,12 @@ public: void insert(std::unique_ptr<FileTreeItem> child, std::size_t at); void remove(std::size_t i); + void clear() + { + m_children.clear(); + m_loaded = false; + } + const std::vector<std::unique_ptr<FileTreeItem>>& children() const { return m_children; @@ -57,6 +63,7 @@ public: } QString virtualPath() const; + const QString& filename() const { return m_file; @@ -72,6 +79,11 @@ public: return m_wsLcFile; } + const MOShared::DirectoryEntry::FileKey& key() const + { + return m_key; + } + const QString& mod() const { return m_mod; @@ -152,13 +164,16 @@ public: private: FileTreeItem* m_parent; - int m_originID; - QString m_virtualParentPath; - QString m_realPath; - Flags m_flags; - std::wstring m_wsFile, m_wsLcFile; - QString m_file; - QString m_mod; + + const int m_originID; + const QString m_virtualParentPath; + const QString m_realPath; + const Flags m_flags; + const std::wstring m_wsFile, m_wsLcFile; + const MOShared::DirectoryEntry::FileKey m_key; + const QString m_file; + const QString m_mod; + bool m_loaded; bool m_expanded; std::vector<std::unique_ptr<FileTreeItem>> m_children; diff --git a/src/filetreemodel.cpp b/src/filetreemodel.cpp index 9332b354..ed62b8ae 100644 --- a/src/filetreemodel.cpp +++ b/src/filetreemodel.cpp @@ -17,9 +17,13 @@ void trace(F&&) } -FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) - : QAbstractItemModel(parent), m_core(core), m_flags(NoFlags) +FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) : + QAbstractItemModel(parent), m_core(core), + m_root(nullptr, 0, L"", L"", FileTreeItem::Directory, L"", L"<root>"), + m_flags(NoFlags) { + m_root.setExpanded(true); + connect(&m_iconPendingTimer, &QTimer::timeout, [&]{ updatePendingIcons(); }); connect( @@ -33,21 +37,6 @@ FileTreeModel::FileTreeModel(OrganizerCore& core, QObject* parent) }); } -void FileTreeModel::setFlags(Flags f) -{ - m_flags = f; -} - -bool FileTreeModel::showConflicts() const -{ - return (m_flags & Conflicts); -} - -bool FileTreeModel::showArchives() const -{ - return (m_flags & Archives) && m_core.getArchiveParsing(); -} - void FileTreeModel::refresh() { if (m_root.hasChildren()) { @@ -56,13 +45,24 @@ void FileTreeModel::refresh() } else { TimeThis tt("FileTreeModel::fill()"); beginResetModel(); - m_root = {nullptr, 0, L"", L"", FileTreeItem::Directory, L"", L"<root>"}; - m_root.setExpanded(true); + m_root.clear(); fill(m_root, *m_core.directoryStructure(), L""); endResetModel(); } } +void FileTreeModel::clear() +{ + beginResetModel(); + m_root.clear(); + endResetModel(); +} + +bool FileTreeModel::showArchives() const +{ + return (m_flags & Archives) && m_core.getArchiveParsing(); +} + void FileTreeModel::ensureLoaded(FileTreeItem* item) const { if (!item) { @@ -426,7 +426,7 @@ void FileTreeModel::updateFiles( continue; } - if (auto f=parentEntry.findFile(item->filenameWsLowerCase(), true)) { + if (auto f=parentEntry.findFile(item->key())) { if (shouldShowFile(*f)) { // file still exists trace([&]{ log::debug("{} still exists", item->debugName()); }); diff --git a/src/filetreemodel.h b/src/filetreemodel.h index 0cfe19c7..8a1738b3 100644 --- a/src/filetreemodel.h +++ b/src/filetreemodel.h @@ -23,8 +23,13 @@ public: FileTreeModel(OrganizerCore& core, QObject* parent=nullptr); - void setFlags(Flags f); + void setFlags(Flags f) + { + m_flags = f; + } + void refresh(); + void clear(); QModelIndex index(int row, int col, const QModelIndex& parent={}) const override; QModelIndex parent(const QModelIndex& index) const override; @@ -53,7 +58,11 @@ private: mutable std::vector<QModelIndex> m_iconPending; mutable QTimer m_iconPendingTimer; - bool showConflicts() const; + bool showConflicts() const + { + return (m_flags & Conflicts); + } + bool showArchives() const; void fill( diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 336be37d..c4f9e081 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -500,16 +500,6 @@ std::wstring OrganizerCore::crashDumpsPath() { ).toStdWString(); } -bool OrganizerCore::getArchiveParsing() const -{ - return m_ArchiveParsing; -} - -void OrganizerCore::setArchiveParsing(const bool archiveParsing) -{ - m_ArchiveParsing = archiveParsing; -} - void OrganizerCore::setCurrentProfile(const QString &profileName) { if ((m_CurrentProfile != nullptr) diff --git a/src/organizercore.h b/src/organizercore.h index 6c9edb9f..4ee6ddc5 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -218,8 +218,16 @@ public: bool onFinishedRun(const std::function<void (const QString &, unsigned int)> &func);
void refreshModList(bool saveChanges = true);
QStringList modsSortedByProfilePriority() const;
- bool getArchiveParsing() const;
- void setArchiveParsing(bool archiveParsing);
+
+ bool getArchiveParsing() const
+ {
+ return m_ArchiveParsing;
+ }
+
+ void setArchiveParsing(bool archiveParsing)
+ {
+ m_ArchiveParsing = archiveParsing;
+ }
public: // IPluginDiagnose interface
diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index 97da1061..639d6cac 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -482,6 +482,8 @@ const std::wstring &DirectoryEntry::getName() const void DirectoryEntry::clear()
{
m_Files.clear();
+ m_FilesLookup.clear();
+
for (DirectoryEntry *entry : m_SubDirectories) {
delete entry;
}
@@ -663,6 +665,8 @@ void DirectoryEntry::removeDirRecursive() m_FileRegister->removeFile(m_Files.begin()->second);
}
+ m_FilesLookup.clear();
+
for (DirectoryEntry *entry : m_SubDirectories) {
entry->removeDirRecursive();
delete entry;
@@ -709,6 +713,56 @@ void DirectoryEntry::removeDir(const std::wstring &path) }
}
+bool DirectoryEntry::remove(const std::wstring &fileName, int *origin)
+{
+ const auto lcFileName = ToLowerCopy(fileName);
+
+ auto iter = m_Files.find(lcFileName);
+ bool b = false;
+
+ if (iter != m_Files.end()) {
+ if (origin != nullptr) {
+ FileEntry::Ptr entry = m_FileRegister->getFile(iter->second);
+ if (entry.get() != nullptr) {
+ bool ignore;
+ *origin = entry->getOrigin(ignore);
+ }
+ }
+
+ b = m_FileRegister->removeFile(iter->second);
+ }
+
+ if (m_Files.size() != m_FilesLookup.size()) {
+ DebugBreak();
+ }
+
+ return b;
+}
+
+void DirectoryEntry::insert(
+ const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime,
+ const std::wstring &archive, int order)
+{
+ std::wstring fileNameLower = ToLowerCopy(fileName);
+ auto iter = m_Files.find(fileNameLower);
+ FileEntry::Ptr file;
+
+ if (iter != m_Files.end()) {
+ file = m_FileRegister->getFile(iter->second);
+ } else {
+ file = m_FileRegister->createFile(fileName, this);
+ m_Files.emplace(fileNameLower, file->getIndex());
+ m_FilesLookup.emplace(fileNameLower, file->getIndex());
+ }
+
+ if (m_Files.size() != m_FilesLookup.size()) {
+ DebugBreak();
+ }
+
+ file->addOrigin(origin.getID(), fileTime, archive, order);
+ origin.addFile(file->getIndex());
+}
+
bool DirectoryEntry::hasContentsFromOrigin(int originID) const
{
return m_Origins.find(originID) != m_Origins.end();
@@ -726,13 +780,34 @@ void DirectoryEntry::insertFile(const std::wstring &filePath, FilesOrigin &origi }
}
-
void DirectoryEntry::removeFile(FileEntry::Index index)
{
+ if (!m_FilesLookup.empty()) {
+ auto iter = std::find_if(
+ m_FilesLookup.begin(), m_FilesLookup.end(),
+ [&index](auto&& pair) { return (pair.second == index); }
+ );
+
+ if (iter != m_FilesLookup.end()) {
+ m_FilesLookup.erase(iter);
+ } else {
+ log::error(
+ "file \"{}\" not in directory for lookup \"{}\"",
+ m_FileRegister->getFile(index)->getName(), this->getName());
+ }
+ } else {
+ log::error(
+ "file \"{}\" not in directory \"{}\" for lookup, directory empty",
+ m_FileRegister->getFile(index)->getName(), this->getName());
+ }
+
if (!m_Files.empty()) {
- auto iter = std::find_if(m_Files.begin(), m_Files.end(),
- [&index](const std::pair<std::wstring, FileEntry::Index> &iter) -> bool {
- return iter.second == index; } );
+ auto iter = std::find_if(
+ m_Files.begin(), m_Files.end(),
+ [&index](const std::pair<std::wstring, FileEntry::Index> &iter) -> bool {
+ return iter.second == index; }
+ );
+
if (iter != m_Files.end()) {
m_Files.erase(iter);
} else {
@@ -745,14 +820,25 @@ void DirectoryEntry::removeFile(FileEntry::Index index) QObject::tr("file \"{}\" not in directory \"{}\", directory empty").toStdString(),
m_FileRegister->getFile(index)->getName(), this->getName());
}
-}
+ if (m_Files.size() != m_FilesLookup.size()) {
+ DebugBreak();
+ }
+}
void DirectoryEntry::removeFiles(const std::set<FileEntry::Index> &indices)
{
for (auto iter = m_Files.begin(); iter != m_Files.end();) {
if (indices.find(iter->second) != indices.end()) {
- m_Files.erase(iter++);
+ iter = m_Files.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+
+ for (auto iter = m_FilesLookup.begin(); iter != m_FilesLookup.end();) {
+ if (indices.find(iter->second) != indices.end()) {
+ iter = m_FilesLookup.erase(iter);
} else {
++iter;
}
@@ -851,7 +937,7 @@ const FileEntry::Ptr DirectoryEntry::searchFile(const std::wstring &path, const if (len == std::string::npos) {
// no more path components
- auto iter = m_Files.find(ToLower(path));
+ auto iter = m_Files.find(ToLowerCopy(path));
if (iter != m_Files.end()) {
return m_FileRegister->getFile(iter->second);
} else if (directory != nullptr) {
@@ -884,7 +970,7 @@ DirectoryEntry *DirectoryEntry::findSubDirectory( if (alreadyLowerCase) {
itor = m_SubDirectoriesMap.find(name);
} else {
- itor = m_SubDirectoriesMap.find(ToLower(name));
+ itor = m_SubDirectoriesMap.find(ToLowerCopy(name));
}
if (itor == m_SubDirectoriesMap.end()) {
@@ -904,15 +990,26 @@ DirectoryEntry *DirectoryEntry::findSubDirectoryRecursive(const std::wstring &pa const FileEntry::Ptr DirectoryEntry::findFile(
const std::wstring &name, bool alreadyLowerCase) const
{
- std::map<std::wstring, FileEntry::Index>::const_iterator iter;
+ FilesLookup::const_iterator iter;
if (alreadyLowerCase) {
- iter = m_Files.find(name);
+ iter = m_FilesLookup.find(FileKey(name));
} else {
- iter = m_Files.find(ToLower(name));
+ iter = m_FilesLookup.find(FileKey(ToLowerCopy(name)));
}
- if (iter != m_Files.end()) {
+ if (iter != m_FilesLookup.end()) {
+ return m_FileRegister->getFile(iter->second);
+ } else {
+ return FileEntry::Ptr();
+ }
+}
+
+const FileEntry::Ptr DirectoryEntry::findFile(const FileKey& key) const
+{
+ auto iter = m_FilesLookup.find(key);
+
+ if (iter != m_FilesLookup.end()) {
return m_FileRegister->getFile(iter->second);
} else {
return FileEntry::Ptr();
@@ -921,7 +1018,7 @@ const FileEntry::Ptr DirectoryEntry::findFile( bool DirectoryEntry::hasFile(const std::wstring& name) const
{
- return m_Files.contains(ToLower(name));
+ return m_Files.contains(ToLowerCopy(name));
}
DirectoryEntry *DirectoryEntry::getSubDirectory(const std::wstring &name, bool create, int originID)
@@ -936,7 +1033,7 @@ DirectoryEntry *DirectoryEntry::getSubDirectory(const std::wstring &name, bool c name, this, originID, m_FileRegister, m_OriginConnection);
m_SubDirectories.push_back(entry);
- m_SubDirectoriesMap.emplace(ToLower(name), entry);
+ m_SubDirectoriesMap.emplace(ToLowerCopy(name), entry);
return entry;
} else {
diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index 79bc5cf2..fc68cae7 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -35,6 +35,20 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #endif
#include "util.h"
+namespace MOShared { struct DirectoryEntryFileKey; }
+
+namespace std
+{
+ template <>
+ struct hash<MOShared::DirectoryEntryFileKey>
+ {
+ using argument_type = MOShared::DirectoryEntryFileKey;
+ using result_type = std::size_t;
+
+ inline result_type operator()(const argument_type& key) const;
+ };
+}
+
namespace MOShared {
@@ -203,9 +217,32 @@ private: };
+struct DirectoryEntryFileKey
+{
+ DirectoryEntryFileKey(std::wstring v)
+ : value(std::move(v)), hash(getHash(value))
+ {
+ }
+
+ bool operator==(const DirectoryEntryFileKey& o) const
+ {
+ return (value == o.value);
+ }
+
+ static std::size_t getHash(const std::wstring& value)
+ {
+ return std::hash<std::wstring>()(value);
+ }
+
+ const std::wstring value;
+ const std::size_t hash;
+};
+
+
class DirectoryEntry
{
public:
+ using FileKey = DirectoryEntryFileKey;
DirectoryEntry(const std::wstring &name, DirectoryEntry *parent, int originID);
@@ -294,6 +331,7 @@ public: * @return fileentry object for the file or nullptr if no file matches
*/
const FileEntry::Ptr findFile(const std::wstring &name, bool alreadyLowerCase=false) const;
+ const FileEntry::Ptr findFile(const FileKey& key) const;
bool hasFile(const std::wstring& name) const;
bool containsArchive(std::wstring archiveName);
@@ -315,21 +353,7 @@ public: */
void removeDir(const std::wstring &path);
- bool remove(const std::wstring &fileName, int *origin) {
- auto iter = m_Files.find(ToLower(fileName));
- if (iter != m_Files.end()) {
- if (origin != nullptr) {
- FileEntry::Ptr entry = m_FileRegister->getFile(iter->second);
- if (entry.get() != nullptr) {
- bool ignore;
- *origin = entry->getOrigin(ignore);
- }
- }
- return m_FileRegister->removeFile(iter->second);
- } else {
- return false;
- }
- }
+ bool remove(const std::wstring &fileName, int *origin);
bool hasContentsFromOrigin(int originID) const;
@@ -342,20 +366,9 @@ private: DirectoryEntry(const DirectoryEntry &reference);
DirectoryEntry &operator=(const DirectoryEntry &reference);
- void insert(const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime, const std::wstring &archive, int order) {
- std::wstring fileNameLower = ToLower(fileName);
- auto iter = m_Files.find(fileNameLower);
- FileEntry::Ptr file;
- if (iter != m_Files.end()) {
- file = m_FileRegister->getFile(iter->second);
- } else {
- file = m_FileRegister->createFile(fileName, this);
- // TODO this has been observed to cause a crash, no clue why
- m_Files[fileNameLower] = file->getIndex();
- }
- file->addOrigin(origin.getID(), fileTime, archive, order);
- origin.addFile(file->getIndex());
- }
+ void insert(
+ const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime,
+ const std::wstring &archive, int order);
void addFiles(FilesOrigin &origin, wchar_t *buffer, int bufferOffset);
void addFiles(FilesOrigin &origin, BSA::Folder::Ptr archiveFolder, FILETIME &fileTime, const std::wstring &archiveName, int order);
@@ -367,13 +380,16 @@ private: void removeDirRecursive();
private:
+ using FilesMap = std::map<std::wstring, FileEntry::Index>;
+ using FilesLookup = std::unordered_map<FileKey, FileEntry::Index>;
using SubDirectoriesMap = std::unordered_map<std::wstring, DirectoryEntry*>;
boost::shared_ptr<FileRegister> m_FileRegister;
boost::shared_ptr<OriginConnection> m_OriginConnection;
std::wstring m_Name;
- std::map<std::wstring, FileEntry::Index> m_Files;
+ FilesMap m_Files;
+ FilesLookup m_FilesLookup;
std::vector<DirectoryEntry*> m_SubDirectories;
SubDirectoriesMap m_SubDirectoriesMap;
@@ -386,7 +402,17 @@ private: };
-
} // namespace MOShared
+
+namespace std
+{
+ hash<MOShared::DirectoryEntryFileKey>::result_type
+ hash<MOShared::DirectoryEntryFileKey>::operator()(
+ const argument_type& key) const
+ {
+ return key.hash;
+ }
+}
+
#endif // DIRECTORYENTRY_H
diff --git a/src/shared/util.cpp b/src/shared/util.cpp index 4ac95465..009aad70 100644 --- a/src/shared/util.cpp +++ b/src/shared/util.cpp @@ -102,32 +102,28 @@ static auto locToLower = [] (char in) -> char { return std::tolower(in, loc);
};
-std::string &ToLower(std::string &text)
+std::string& ToLowerInPlace(std::string& text)
{
- //std::transform(text.begin(), text.end(), text.begin(), locToLower);
CharLowerBuffA(const_cast<CHAR *>(text.c_str()), static_cast<DWORD>(text.size()));
return text;
}
-std::string ToLower(const std::string &text)
+std::string ToLowerCopy(const std::string& text)
{
std::string result(text);
- //std::transform(result.begin(), result.end(), result.begin(), locToLower);
CharLowerBuffA(const_cast<CHAR *>(result.c_str()), static_cast<DWORD>(result.size()));
return result;
}
-std::wstring &ToLower(std::wstring &text)
+std::wstring& ToLowerInPlace(std::wstring& text)
{
- //std::transform(text.begin(), text.end(), text.begin(), locToLowerW);
CharLowerBuffW(const_cast<WCHAR *>(text.c_str()), static_cast<DWORD>(text.size()));
return text;
}
-std::wstring ToLower(const std::wstring &text)
+std::wstring ToLowerCopy(const std::wstring& text)
{
std::wstring result(text);
- //std::transform(result.begin(), result.end(), result.begin(), locToLowerW);
CharLowerBuffW(const_cast<WCHAR *>(result.c_str()), static_cast<DWORD>(result.size()));
return result;
}
diff --git a/src/shared/util.h b/src/shared/util.h index 788d2444..79cadf71 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -36,11 +36,11 @@ bool FileExists(const std::wstring &searchPath, const std::wstring &filename); std::string ToString(const std::wstring &source, bool utf8);
std::wstring ToWString(const std::string &source, bool utf8);
-std::string &ToLower(std::string &text);
-std::string ToLower(const std::string &text);
+std::string& ToLowerInPlace(std::string& text);
+std::string ToLowerCopy(const std::string& text);
-std::wstring &ToLower(std::wstring &text);
-std::wstring ToLower(const std::wstring &text);
+std::wstring& ToLowerInPlace(std::wstring& text);
+std::wstring ToLowerCopy(const std::wstring& text);
bool CaseInsensitiveEqual(const std::wstring &lhs, const std::wstring &rhs);
|
