Skip to content
Permalink
Browse files

DiscIO: Avoid copies when comparing FileSystemGCWii file names

  • Loading branch information...
JosJuice committed May 29, 2019
1 parent d4b069f commit f25367a7a274c351da568e1ce1e09b95ae0f1a60
Showing with 41 additions and 4 deletions.
  1. +39 −4 Source/Core/DiscIO/FileSystemGCWii.cpp
  2. +1 −0 Source/Core/DiscIO/FileSystemGCWii.h
  3. +1 −0 Source/Core/DiscIO/Filesystem.h
@@ -6,6 +6,7 @@
#include <cinttypes>
#include <cstddef>
#include <cstring>
#include <locale>
#include <map>
#include <memory>
#include <optional>
@@ -119,6 +120,43 @@ std::string FileInfoGCWii::GetName() const
return SHIFTJISToUTF8(reinterpret_cast<const char*>(m_fst + GetNameOffset()));
}

bool FileInfoGCWii::NameCaseInsensitiveEquals(std::string_view other) const
{
// For speed, this function avoids allocating new strings, except when we are comparing
// non-ASCII characters with non-ASCII characters, which is a rare case.

const char* this_ptr = reinterpret_cast<const char*>(m_fst + GetNameOffset());
const char* other_ptr = other.data();

for (size_t i = 0; i < other.size(); ++i, ++this_ptr, ++other_ptr)
{
if (*this_ptr == '\0')
{
// A null byte in this is always a terminator and a null byte in other is never a terminator,
// so if we reach this case, this is shorter than other
return false;
}
else if (static_cast<unsigned char>(*this_ptr) >= 0x80 &&
static_cast<unsigned char>(*other_ptr) >= 0x80)
{
// other is in UTF-8 and this is in Shift-JIS, so we convert so that we can compare correctly
const std::string this_utf8 = SHIFTJISToUTF8(this_ptr);
return std::equal(this_utf8.cbegin(), this_utf8.cend(), other.cbegin() + i, other.cend(),
[](char a, char b) {
return std::tolower(a, std::locale::classic()) ==
std::tolower(b, std::locale::classic());
});
}
else if (std::tolower(*this_ptr, std::locale::classic()) !=
std::tolower(*other_ptr, std::locale::classic()))
{
return false;
}
}

return *this_ptr == '\0'; // If we're not at a null byte, this is longer than other
}

std::string FileInfoGCWii::GetPath() const
{
// The root entry doesn't have a name
@@ -283,11 +321,8 @@ std::unique_ptr<FileInfo> FileSystemGCWii::FindFileInfo(std::string_view path,

for (const FileInfo& child : file_info)
{
const std::string child_name = child.GetName();

// We need case insensitive comparison since some games have OPENING.BNR instead of opening.bnr
if (child_name.size() == name.size() &&
!strncasecmp(child_name.data(), name.data(), name.size()))
if (child.NameCaseInsensitiveEquals(name))
{
// A match is found. The rest of the path is passed on to finish the search.
std::unique_ptr<FileInfo> result = FindFileInfo(rest_of_path, child);
@@ -45,6 +45,7 @@ class FileInfoGCWii : public FileInfo
bool IsDirectory() const override;
u32 GetTotalChildren() const override;
std::string GetName() const override;
bool NameCaseInsensitiveEquals(std::string_view other) const override;
std::string GetPath() const override;

bool IsValid(u64 fst_size, const FileInfoGCWii& parent_directory) const;
@@ -94,6 +94,7 @@ class FileInfo
// Not guaranteed to return a meaningful value for files.
virtual u32 GetTotalChildren() const = 0;
virtual std::string GetName() const = 0;
virtual bool NameCaseInsensitiveEquals(std::string_view other) const = 0;
// GetPath will find the parents of the current object and call GetName on them,
// so it's slower than other functions. If you're traversing through folders
// to get a file and its path, building the path while traversing is faster.

0 comments on commit f25367a

Please sign in to comment.
You can’t perform that action at this time.