Skip to content

Commit

Permalink
Use IndexOfAnyValues in Regex.Escape (#78667)
Browse files Browse the repository at this point in the history
* Use IndexOfAnyValues in Regex.Escape

* NET8_0_OR_GREATER => NET7_0_OR_GREATER
  • Loading branch information
MihaZupan authored Nov 22, 2022
1 parent ae70706 commit cce374c
Showing 1 changed file with 44 additions and 28 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Buffers;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
Expand Down Expand Up @@ -149,18 +150,13 @@ public static RegexReplacement ParseReplacement(string pattern, RegexOptions opt
/// </summary>
public static string Escape(string input)
{
for (int i = 0; i < input.Length; i++)
{
if (IsMetachar(input[i]))
{
return EscapeImpl(input, i);
}
}

return input;
int indexOfMetachar = IndexOfMetachar(input.AsSpan());
return indexOfMetachar < 0
? input
: EscapeImpl(input.AsSpan(), indexOfMetachar);
}

private static string EscapeImpl(string input, int i)
private static string EscapeImpl(ReadOnlySpan<char> input, int indexOfMetachar)
{
// For small inputs we allocate on the stack. In most cases a buffer three
// times larger the original string should be sufficient as usually not all
Expand All @@ -171,12 +167,18 @@ private static string EscapeImpl(string input, int i)
new ValueStringBuilder(stackalloc char[EscapeMaxBufferSize]) :
new ValueStringBuilder(input.Length + 200);

char ch = input[i];
vsb.Append(input.AsSpan(0, i));

do
while (true)
{
vsb.Append('\\');
vsb.Append(input.Slice(0, indexOfMetachar));
input = input.Slice(indexOfMetachar);

if (input.IsEmpty)
{
break;
}

char ch = input[0];

switch (ch)
{
case '\n':
Expand All @@ -193,23 +195,16 @@ private static string EscapeImpl(string input, int i)
break;
}

vsb.Append('\\');
vsb.Append(ch);
i++;
int lastpos = i;
input = input.Slice(1);

while (i < input.Length)
indexOfMetachar = IndexOfMetachar(input);
if (indexOfMetachar < 0)
{
ch = input[i];
if (IsMetachar(ch))
{
break;
}

i++;
indexOfMetachar = input.Length;
}

vsb.Append(input.AsSpan(lastpos, i - lastpos));
} while (i < input.Length);
}

return vsb.ToString();
}
Expand Down Expand Up @@ -2081,6 +2076,27 @@ internal static int MapCaptureNumber(int capnum, Hashtable? caps) =>
// ' a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Q, S, 0, 0, 0};

#if NET7_0_OR_GREATER
private static readonly IndexOfAnyValues<char> s_metachars =
IndexOfAnyValues.Create("\t\n\f\r #$()*+.?[\\^{|");

private static int IndexOfMetachar(ReadOnlySpan<char> input) =>
input.IndexOfAny(s_metachars);
#else
private static int IndexOfMetachar(ReadOnlySpan<char> input)
{
for (int i = 0; i < input.Length; i++)
{
if (IsMetachar(input[i]))
{
return i;
}
}

return -1;
}
#endif

/// <summary>Returns true for those characters that terminate a string of ordinary chars.</summary>
private static bool IsSpecial(char ch) => ch <= '|' && Category[ch] >= S;

Expand Down

0 comments on commit cce374c

Please sign in to comment.