Skip to content

Commit b412729

Browse files
committed
U4-2635 Skip past replaced word when checking
Avoids infinite loop if search and replacement are equal
1 parent 74dd1f9 commit b412729

File tree

2 files changed

+9
-4
lines changed

2 files changed

+9
-4
lines changed

src/Umbraco.Core/StringExtensions.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,11 +1191,13 @@ public static string ToSafeFileName(this string text, CultureInfo culture)
11911191
/// <returns>Updated string</returns>
11921192
public static string Replace(this string source, string oldString, string newString, StringComparison stringComparison)
11931193
{
1194-
// Initialised to zero so it can be used as startIndex on first iteration.
1195-
int index = 0;
1194+
// This initialisation ensures the first check starts at index zero of the source. On successive checks for
1195+
// a match, the source is skipped to immediately after the last replaced occurrence for efficiency
1196+
// and to avoid infinite loops when oldString and newString compare equal.
1197+
int index = -1 * newString.Length;
11961198

1197-
// Determine if there are any matches left in source, starting from last found match.
1198-
while((index = source.IndexOf(oldString, index, stringComparison)) >= 0)
1199+
// Determine if there are any matches left in source, starting from just after the result of replacing the last match.
1200+
while((index = source.IndexOf(oldString, index + newString.Length, stringComparison)) >= 0)
11991201
{
12001202
// Remove the old text.
12011203
source = source.Remove(index, oldString.Length);

src/Umbraco.Tests/CoreStrings/StringExtensionsTests.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ public void TrimStart(string input, string forTrimming, string shouldBe)
8383
[TestCase("Hello this is my string", "hello", "replaced", "replaced this is my string", StringComparison.CurrentCultureIgnoreCase)]
8484
[TestCase("Hello this is hello my string", "hello", "replaced", "replaced this is replaced my string", StringComparison.CurrentCultureIgnoreCase)]
8585
[TestCase("Hello this is my string", "nonexistent", "replaced", "Hello this is my string", StringComparison.CurrentCultureIgnoreCase)]
86+
[TestCase("Hellohello this is my string", "hello", "replaced", "replacedreplaced this is my string", StringComparison.CurrentCultureIgnoreCase)]
87+
// Ensure replacing with the same string doesn't cause infinite loop.
88+
[TestCase("Hello this is my string", "hello", "hello", "hello this is my string", StringComparison.CurrentCultureIgnoreCase)]
8689
public void ReplaceWithStringComparison(string input, string oldString, string newString, string shouldBe, StringComparison stringComparison)
8790
{
8891
var replaced = input.Replace(oldString, newString, stringComparison);

0 commit comments

Comments
 (0)