diff --git a/src/ZString.Unity/Assets/Scripts/ZString/Utf16ValueStringBuilder.cs b/src/ZString.Unity/Assets/Scripts/ZString/Utf16ValueStringBuilder.cs index df6ec3b..b1855b4 100644 --- a/src/ZString.Unity/Assets/Scripts/ZString/Utf16ValueStringBuilder.cs +++ b/src/ZString.Unity/Assets/Scripts/ZString/Utf16ValueStringBuilder.cs @@ -465,23 +465,25 @@ public void Replace(ReadOnlySpan oldValue, ReadOnlySpan newValue, in buffer.AsSpan(0, startIndex).CopyTo(newBuffer); int newBufferIndex = startIndex; - for (int i = startIndex; i < endIndex; i += oldValue.Length) + int readIndex = startIndex; + for (; readIndex < endIndex; readIndex += oldValue.Length) { - var span = readOnlySpan.Slice(i, endIndex - i); + var span = readOnlySpan.Slice(readIndex, endIndex - readIndex); var pos = span.IndexOf(oldValue, StringComparison.Ordinal); if (pos == -1) { - var remain = readOnlySpan.Slice(i); - remain.CopyTo(newBuffer.AsSpan(newBufferIndex)); - newBufferIndex += remain.Length; break; } - readOnlySpan.Slice(i, pos).CopyTo(newBuffer.AsSpan(newBufferIndex)); + readOnlySpan.Slice(readIndex, pos).CopyTo(newBuffer.AsSpan(newBufferIndex)); newValue.CopyTo(newBuffer.AsSpan(newBufferIndex + pos)); newBufferIndex += pos + newValue.Length; - i += pos; + readIndex += pos; } + var remain = readOnlySpan.Slice(readIndex); + remain.CopyTo(newBuffer.AsSpan(newBufferIndex)); + newBufferIndex += remain.Length; + if (buffer!.Length != ThreadStaticBufferSize) { ArrayPool.Shared.Return(buffer); diff --git a/src/ZString/Utf16ValueStringBuilder.cs b/src/ZString/Utf16ValueStringBuilder.cs index df6ec3b..b1855b4 100644 --- a/src/ZString/Utf16ValueStringBuilder.cs +++ b/src/ZString/Utf16ValueStringBuilder.cs @@ -465,23 +465,25 @@ public void Replace(ReadOnlySpan oldValue, ReadOnlySpan newValue, in buffer.AsSpan(0, startIndex).CopyTo(newBuffer); int newBufferIndex = startIndex; - for (int i = startIndex; i < endIndex; i += oldValue.Length) + int readIndex = startIndex; + for (; readIndex < endIndex; readIndex += oldValue.Length) { - var span = readOnlySpan.Slice(i, endIndex - i); + var span = readOnlySpan.Slice(readIndex, endIndex - readIndex); var pos = span.IndexOf(oldValue, StringComparison.Ordinal); if (pos == -1) { - var remain = readOnlySpan.Slice(i); - remain.CopyTo(newBuffer.AsSpan(newBufferIndex)); - newBufferIndex += remain.Length; break; } - readOnlySpan.Slice(i, pos).CopyTo(newBuffer.AsSpan(newBufferIndex)); + readOnlySpan.Slice(readIndex, pos).CopyTo(newBuffer.AsSpan(newBufferIndex)); newValue.CopyTo(newBuffer.AsSpan(newBufferIndex + pos)); newBufferIndex += pos + newValue.Length; - i += pos; + readIndex += pos; } + var remain = readOnlySpan.Slice(readIndex); + remain.CopyTo(newBuffer.AsSpan(newBufferIndex)); + newBufferIndex += remain.Length; + if (buffer!.Length != ThreadStaticBufferSize) { ArrayPool.Shared.Return(buffer); diff --git a/tests/ZString.Tests/ReplaceTest.cs b/tests/ZString.Tests/ReplaceTest.cs index 8b25eb1..70bc269 100644 --- a/tests/ZString.Tests/ReplaceTest.cs +++ b/tests/ZString.Tests/ReplaceTest.cs @@ -87,6 +87,23 @@ public void ReplaceStringTest() } } + [Theory] + [InlineData("abcTAIL", "bc", "X", 1, 2)] + [InlineData("abcTAIL", "bc", "", 1, 2)] + [InlineData("abcTAIL", "bc", "longer", 1, 2)] + [InlineData("abcbcTAIL", "bc", "X", 1, 4)] + [InlineData("abczTAIL", "bc", "X", 1, 3)] + [InlineData("abc", "bc", "X", 1, 2)] + public void ReplacePreservesSuffix(string text, string oldValue, string newValue, int startIndex, int count) + { + using (var zsb = ZString.CreateStringBuilder()) + { + zsb.Append(text); + zsb.Replace(oldValue, newValue, startIndex, count); + zsb.ToString().Should().Be(new StringBuilder(text).Replace(oldValue, newValue, startIndex, count).ToString()); + } + } + [Fact] public void NotMatchTest() {