AK: Implement StringView::to_{lower,upper}case_string

This patch refactors StringImpl::to_{lower,upper}case to use the new
static methods StringImpl::create_{lower,upper}cased if they have to use
to create a new StringImpl. This allows implementing StringView's
to_{lower,upper}case_string using the same methods.

It also replaces the usage of hand-written to_ascii_lowercase() and
similar methods with those from CharacterTypes.h.
This commit is contained in:
Max Wipfli 2021-07-01 13:45:59 +02:00 committed by Andreas Kling
parent 5ce9305c5f
commit 3ea65200d8
Notes: sideshowbarker 2024-07-18 11:06:25 +09:00
4 changed files with 41 additions and 36 deletions

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/CharacterTypes.h>
#include <AK/FlyString.h> #include <AK/FlyString.h>
#include <AK/HashTable.h> #include <AK/HashTable.h>
#include <AK/Memory.h> #include <AK/Memory.h>
@ -90,60 +91,48 @@ RefPtr<StringImpl> StringImpl::create(ReadonlyBytes bytes, ShouldChomp shouldCho
return StringImpl::create(reinterpret_cast<const char*>(bytes.data()), bytes.size(), shouldChomp); return StringImpl::create(reinterpret_cast<const char*>(bytes.data()), bytes.size(), shouldChomp);
} }
static inline bool is_ascii_lowercase(char c) RefPtr<StringImpl> StringImpl::create_lowercased(char const* cstring, size_t length)
{ {
return c >= 'a' && c <= 'z'; if (!cstring)
return nullptr;
if (!length)
return the_empty_stringimpl();
char* buffer;
auto impl = create_uninitialized(length, buffer);
for (size_t i = 0; i < length; ++i)
buffer[i] = (char)to_ascii_lowercase(cstring[i]);
return impl;
} }
static inline bool is_ascii_uppercase(char c) RefPtr<StringImpl> StringImpl::create_uppercased(char const* cstring, size_t length)
{ {
return c >= 'A' && c <= 'Z'; if (!cstring)
} return nullptr;
if (!length)
static inline char to_ascii_lowercase(char c) return the_empty_stringimpl();
{ char* buffer;
if (is_ascii_uppercase(c)) auto impl = create_uninitialized(length, buffer);
return c | 0x20; for (size_t i = 0; i < length; ++i)
return c; buffer[i] = (char)to_ascii_uppercase(cstring[i]);
} return impl;
static inline char to_ascii_uppercase(char c)
{
if (is_ascii_lowercase(c))
return c & ~0x20;
return c;
} }
NonnullRefPtr<StringImpl> StringImpl::to_lowercase() const NonnullRefPtr<StringImpl> StringImpl::to_lowercase() const
{ {
for (size_t i = 0; i < m_length; ++i) { for (size_t i = 0; i < m_length; ++i) {
if (!is_ascii_lowercase(characters()[i])) if (is_ascii_upper_alpha(characters()[i]))
goto slow_path; return create_lowercased(characters(), m_length).release_nonnull();
} }
return const_cast<StringImpl&>(*this); return const_cast<StringImpl&>(*this);
slow_path:
char* buffer;
auto lowercased = create_uninitialized(m_length, buffer);
for (size_t i = 0; i < m_length; ++i)
buffer[i] = to_ascii_lowercase(characters()[i]);
return lowercased;
} }
NonnullRefPtr<StringImpl> StringImpl::to_uppercase() const NonnullRefPtr<StringImpl> StringImpl::to_uppercase() const
{ {
for (size_t i = 0; i < m_length; ++i) { for (size_t i = 0; i < m_length; ++i) {
if (!is_ascii_uppercase(characters()[i])) if (is_ascii_lower_alpha(characters()[i]))
goto slow_path; return create_uppercased(characters(), m_length).release_nonnull();
} }
return const_cast<StringImpl&>(*this); return const_cast<StringImpl&>(*this);
slow_path:
char* buffer;
auto uppercased = create_uninitialized(m_length, buffer);
for (size_t i = 0; i < m_length; ++i)
buffer[i] = to_ascii_uppercase(characters()[i]);
return uppercased;
} }
void StringImpl::compute_hash() const void StringImpl::compute_hash() const

View file

@ -26,6 +26,9 @@ public:
static RefPtr<StringImpl> create(const char* cstring, ShouldChomp = NoChomp); static RefPtr<StringImpl> create(const char* cstring, ShouldChomp = NoChomp);
static RefPtr<StringImpl> create(const char* cstring, size_t length, ShouldChomp = NoChomp); static RefPtr<StringImpl> create(const char* cstring, size_t length, ShouldChomp = NoChomp);
static RefPtr<StringImpl> create(ReadonlyBytes, ShouldChomp = NoChomp); static RefPtr<StringImpl> create(ReadonlyBytes, ShouldChomp = NoChomp);
static RefPtr<StringImpl> create_lowercased(char const* cstring, size_t length);
static RefPtr<StringImpl> create_uppercased(char const* cstring, size_t length);
NonnullRefPtr<StringImpl> to_lowercase() const; NonnullRefPtr<StringImpl> to_lowercase() const;
NonnullRefPtr<StringImpl> to_uppercase() const; NonnullRefPtr<StringImpl> to_uppercase() const;

View file

@ -173,6 +173,16 @@ bool StringView::equals_ignoring_case(const StringView& other) const
return StringUtils::equals_ignoring_case(*this, other); return StringUtils::equals_ignoring_case(*this, other);
} }
String StringView::to_lowercase_string() const
{
return StringImpl::create_lowercased(characters_without_null_termination(), length());
}
String StringView::to_uppercase_string() const
{
return StringImpl::create_uppercased(characters_without_null_termination(), length());
}
StringView StringView::substring_view_starting_from_substring(const StringView& substring) const StringView StringView::substring_view_starting_from_substring(const StringView& substring) const
{ {
const char* remaining_characters = substring.characters_without_null_termination(); const char* remaining_characters = substring.characters_without_null_termination();

View file

@ -83,6 +83,9 @@ public:
[[nodiscard]] StringView trim(const StringView& characters, TrimMode mode = TrimMode::Both) const { return StringUtils::trim(*this, characters, mode); } [[nodiscard]] StringView trim(const StringView& characters, TrimMode mode = TrimMode::Both) const { return StringUtils::trim(*this, characters, mode); }
[[nodiscard]] StringView trim_whitespace(TrimMode mode = TrimMode::Both) const { return StringUtils::trim_whitespace(*this, mode); } [[nodiscard]] StringView trim_whitespace(TrimMode mode = TrimMode::Both) const { return StringUtils::trim_whitespace(*this, mode); }
[[nodiscard]] String to_lowercase_string() const;
[[nodiscard]] String to_uppercase_string() const;
Optional<size_t> find_first_of(char) const; Optional<size_t> find_first_of(char) const;
Optional<size_t> find_first_of(const StringView&) const; Optional<size_t> find_first_of(const StringView&) const;