diff options
author | Idan Horowitz <idan.horowitz@gmail.com> | 2021-09-11 02:15:44 +0300 |
---|---|---|
committer | Idan Horowitz <idan.horowitz@gmail.com> | 2021-09-11 20:36:43 +0300 |
commit | 6704961c8250b44dc622e95821fd7e2b6bed673a (patch) | |
tree | bd540128659a2556acc51464552091fc3eb2e449 /AK | |
parent | aba4c9579ff8d36f739eb221e330782e5c3a5d9d (diff) | |
download | serenity-6704961c8250b44dc622e95821fd7e2b6bed673a.zip |
AK: Replace the mutable String::replace API with an immutable version
This removes the awkward String::replace API which was the only String
API which mutated the String and replaces it with a new immutable
version that returns a new String with the replacements applied. This
also fixes a couple of UAFs that were caused by the use of this API.
As an optimization an equivalent StringView::replace API was also added
to remove an unnecessary String allocations in the format of:
`String { view }.replace(...);`
Diffstat (limited to 'AK')
-rw-r--r-- | AK/String.cpp | 30 | ||||
-rw-r--r-- | AK/String.h | 2 | ||||
-rw-r--r-- | AK/StringUtils.cpp | 28 | ||||
-rw-r--r-- | AK/StringUtils.h | 1 | ||||
-rw-r--r-- | AK/StringView.cpp | 5 | ||||
-rw-r--r-- | AK/StringView.h | 1 | ||||
-rw-r--r-- | AK/URLParser.cpp | 7 |
7 files changed, 38 insertions, 36 deletions
diff --git a/AK/String.cpp b/AK/String.cpp index 1d8a6e844a..df2d0730c6 100644 --- a/AK/String.cpp +++ b/AK/String.cpp @@ -352,36 +352,6 @@ bool String::equals_ignoring_case(const StringView& other) const return StringUtils::equals_ignoring_case(view(), other); } -int String::replace(const String& needle, const String& replacement, bool all_occurrences) -{ - if (is_empty()) - return 0; - - Vector<size_t> positions; - if (all_occurrences) { - positions = find_all(needle); - } else { - auto pos = find(needle); - if (!pos.has_value()) - return 0; - positions.append(pos.value()); - } - - if (!positions.size()) - return 0; - - StringBuilder b; - size_t lastpos = 0; - for (auto& pos : positions) { - b.append(substring_view(lastpos, pos - lastpos)); - b.append(replacement); - lastpos = pos + needle.length(); - } - b.append(substring_view(lastpos, length() - lastpos)); - m_impl = StringImpl::create(b.build().characters()); - return positions.size(); -} - String String::reverse() const { StringBuilder reversed_string(length()); diff --git a/AK/String.h b/AK/String.h index 623c23fbb4..d5c47e1dab 100644 --- a/AK/String.h +++ b/AK/String.h @@ -285,7 +285,7 @@ public: return { characters(), length() }; } - int replace(const String& needle, const String& replacement, bool all_occurrences = false); + [[nodiscard]] String replace(const StringView& needle, const StringView& replacement, bool all_occurrences = false) const { return StringUtils::replace(*this, needle, replacement, all_occurrences); } [[nodiscard]] size_t count(StringView const& needle) const { return StringUtils::count(*this, needle); } [[nodiscard]] String reverse() const; diff --git a/AK/StringUtils.cpp b/AK/StringUtils.cpp index ee0594e7e9..887620c663 100644 --- a/AK/StringUtils.cpp +++ b/AK/StringUtils.cpp @@ -427,6 +427,34 @@ String to_titlecase(StringView const& str) return builder.to_string(); } +String replace(StringView const& str, StringView const& needle, StringView const& replacement, bool all_occurrences) +{ + if (str.is_empty()) + return str; + + Vector<size_t> positions; + if (all_occurrences) { + positions = str.find_all(needle); + if (!positions.size()) + return str; + } else { + auto pos = str.find(needle); + if (!pos.has_value()) + return str; + positions.append(pos.value()); + } + + StringBuilder replaced_string; + size_t last_position = 0; + for (auto& position : positions) { + replaced_string.append(str.substring_view(last_position, position - last_position)); + replaced_string.append(replacement); + last_position = position + needle.length(); + } + replaced_string.append(str.substring_view(last_position, str.length() - last_position)); + return replaced_string.build(); +} + // TODO: Benchmark against KMP (AK/MemMem.h) and switch over if it's faster for short strings too size_t count(StringView const& str, StringView const& needle) { diff --git a/AK/StringUtils.h b/AK/StringUtils.h index 661ce963db..dabf4a1fef 100644 --- a/AK/StringUtils.h +++ b/AK/StringUtils.h @@ -71,6 +71,7 @@ Optional<size_t> find_any_of(StringView const& haystack, StringView const& needl String to_snakecase(const StringView&); String to_titlecase(StringView const&); +String replace(StringView const&, StringView const& needle, StringView const& replacement, bool all_occurrences = false); size_t count(StringView const&, StringView const& needle); } diff --git a/AK/StringView.cpp b/AK/StringView.cpp index 0aeb124676..83f90e2d3c 100644 --- a/AK/StringView.cpp +++ b/AK/StringView.cpp @@ -245,4 +245,9 @@ bool StringView::operator==(const String& string) const String StringView::to_string() const { return String { *this }; } +String StringView::replace(const StringView& needle, const StringView& replacement, bool all_occurrences) const +{ + return StringUtils::replace(*this, needle, replacement, all_occurrences); +} + } diff --git a/AK/StringView.h b/AK/StringView.h index f6868d1b8a..fa1905d885 100644 --- a/AK/StringView.h +++ b/AK/StringView.h @@ -220,6 +220,7 @@ public: [[nodiscard]] bool is_whitespace() const { return StringUtils::is_whitespace(*this); } + [[nodiscard]] String replace(const StringView& needle, const StringView& replacement, bool all_occurrences = false) const; [[nodiscard]] size_t count(StringView const& needle) const { return StringUtils::count(*this, needle); } template<typename... Ts> diff --git a/AK/URLParser.cpp b/AK/URLParser.cpp index 7edf6c65c7..b8db96528f 100644 --- a/AK/URLParser.cpp +++ b/AK/URLParser.cpp @@ -203,15 +203,12 @@ URL URLParser::parse(Badge<URL>, StringView const& raw_input, URL const* base_ur if (start_index >= end_index) return {}; - auto processed_input = raw_input.substring_view(start_index, end_index - start_index); + String processed_input = raw_input.substring_view(start_index, end_index - start_index); // NOTE: This replaces all tab and newline characters with nothing. if (processed_input.contains("\t") || processed_input.contains("\n")) { report_validation_error(); - String processed_input_string(processed_input); - processed_input_string.replace("\t", "", true); - processed_input_string.replace("\n", "", true); - processed_input = processed_input_string; + processed_input = processed_input.replace("\t", "", true).replace("\n", "", true); } State state = State::SchemeStart; |