AK: Add case insensitive String::ends_with support

FileSystemPath::has_extension was jumping through hoops and allocating
memory to do a case insensitive comparison needlessly. Extend the
existing String::ends_with method to allow the caller to specify the
case sensitivity required.
This commit is contained in:
Brian Gianforcaro 2020-05-26 02:12:18 -07:00 committed by Andreas Kling
parent 8e4b858b3f
commit 332f96e7ca
Notes: sideshowbarker 2024-07-19 06:07:40 +09:00
7 changed files with 53 additions and 12 deletions

View file

@ -105,11 +105,9 @@ void FileSystemPath::canonicalize()
m_string = builder.to_string();
}
bool FileSystemPath::has_extension(StringView extension) const
bool FileSystemPath::has_extension(const StringView& extension) const
{
// FIXME: This is inefficient, expand StringView with enough functionality that we don't need to copy strings here.
String extension_string = extension;
return m_string.to_lowercase().ends_with(extension_string.to_lowercase());
return m_string.ends_with(extension, CaseSensitivity::CaseInsensitive);
}
String canonicalized_path(const StringView& path)

View file

@ -47,7 +47,7 @@ public:
const Vector<String>& parts() const { return m_parts; }
bool has_extension(StringView) const;
bool has_extension(const StringView&) const;
private:
void canonicalize();

View file

@ -278,9 +278,9 @@ bool String::starts_with(char ch) const
return characters()[0] == ch;
}
bool String::ends_with(const StringView& str) const
bool String::ends_with(const StringView& str, CaseSensitivity case_sensitivity) const
{
return StringUtils::ends_with(*this, str);
return StringUtils::ends_with(*this, str, case_sensitivity);
}
bool String::ends_with(char ch) const

View file

@ -146,7 +146,7 @@ public:
ConstIterator end() const { return begin() + length(); }
bool starts_with(const StringView&) const;
bool ends_with(const StringView&) const;
bool ends_with(const StringView&, CaseSensitivity = CaseSensitivity::CaseSensitive) const;
bool starts_with(char) const;
bool ends_with(char) const;

View file

@ -196,7 +196,7 @@ bool equals_ignoring_case(const StringView& a, const StringView& b)
return true;
}
bool ends_with(const StringView& str, const StringView& end)
bool ends_with(const StringView& str, const StringView& end, CaseSensitivity case_sensitivity)
{
if (end.is_empty())
return true;
@ -204,7 +204,19 @@ bool ends_with(const StringView& str, const StringView& end)
return false;
if (end.length() > str.length())
return false;
if (case_sensitivity == CaseSensitivity::CaseSensitive)
return !memcmp(str.characters_without_null_termination() + (str.length() - end.length()), end.characters_without_null_termination(), end.length());
auto str_chars = str.characters_without_null_termination();
auto end_chars = end.characters_without_null_termination();
size_t si = str.length() - end.length();
for (size_t ei = 0; ei < end.length(); ++si, ++ei) {
if (to_lowercase(str_chars[si]) != to_lowercase(end_chars[ei]))
return false;
}
return true;
}
}

View file

@ -43,8 +43,7 @@ int convert_to_int(const StringView&, bool& ok);
unsigned convert_to_uint(const StringView&, bool& ok);
unsigned convert_to_uint_from_hex(const StringView&, bool& ok);
bool equals_ignoring_case(const StringView&, const StringView&);
bool ends_with(const StringView& str, const StringView& end);
bool ends_with(const StringView& a, const StringView& b, CaseSensitivity);
}
}

View file

@ -85,4 +85,36 @@ TEST_CASE(relative_paths)
}
}
TEST_CASE(has_extension)
{
{
FileSystemPath path("/tmp/simple.png");
EXPECT(path.has_extension(".png"));
EXPECT(path.has_extension(".pnG"));
EXPECT(path.has_extension(".PNG"));
}
{
FileSystemPath path("/TMP/SIMPLE.PNG");
EXPECT(path.has_extension(".png"));
EXPECT(path.has_extension(".pnG"));
EXPECT(path.has_extension(".PNG"));
}
{
FileSystemPath path(".png");
EXPECT(path.has_extension(".png"));
}
{
FileSystemPath path;
EXPECT_EQ(path.has_extension(".png"), false);
}
{
FileSystemPath path("png");
EXPECT_EQ(path.has_extension(".png"), false);
}
}
TEST_MAIN(FileSystemPath)