summaryrefslogtreecommitdiff
path: root/AK
diff options
context:
space:
mode:
authorIdan Horowitz <idan.horowitz@gmail.com>2021-09-11 02:15:44 +0300
committerIdan Horowitz <idan.horowitz@gmail.com>2021-09-11 20:36:43 +0300
commit6704961c8250b44dc622e95821fd7e2b6bed673a (patch)
treebd540128659a2556acc51464552091fc3eb2e449 /AK
parentaba4c9579ff8d36f739eb221e330782e5c3a5d9d (diff)
downloadserenity-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.cpp30
-rw-r--r--AK/String.h2
-rw-r--r--AK/StringUtils.cpp28
-rw-r--r--AK/StringUtils.h1
-rw-r--r--AK/StringView.cpp5
-rw-r--r--AK/StringView.h1
-rw-r--r--AK/URLParser.cpp7
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;