using System.Text;
using System.Text.RegularExpressions;
namespace XFESystem.Collections;
/// <summary>A bounded atom replacement fast path for the private Jint runtime.</summary>
public static class LiteralRegexReplacement
{
public static string Replace(Regex regex, string input, string replacement, int count)
{
// Every other regex shape, option, replacement substitution and overload
// retains the original engine and its timeout. No JS accessors or
// callbacks are moved: Jint has already converted these exact arguments.
if (count <= 0 || input.Length > 131_072 || replacement.Length > 4096 || replacement.Contains('$') ||
(regex.Options & (RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace | RegexOptions.RightToLeft)) != 0 ||
!TryLiteral(regex.ToString(), out string literal) ||
replacement.Length > literal.Length && input.Length + (long)(input.Length / literal.Length) * (replacement.Length - literal.Length) > 1_048_576)
return RegexPauseRecovery.Replace(regex, input, replacement, count);
int match = input.IndexOf(literal, StringComparison.Ordinal);
if (match < 0) return input;
var output = new StringBuilder(input.Length); int start = 0, replaced = 0;
do
{
output.Append(input, start, match - start).Append(replacement);
start = match + literal.Length;
if (++replaced == count) break;
match = input.IndexOf(literal, start, StringComparison.Ordinal);
} while (match >= 0);
return output.Append(input, start, input.Length - start).ToString();
}
private static bool TryLiteral(string pattern, out string literal)
{
literal = string.Empty;
if (pattern.Length is < 1 or > 256) return false;
Span<char> decoded = stackalloc char[256]; int count = 0;
const string meta = @"\.^$|?*+()[]{}";
for (int index = 0; index < pattern.Length; index++)
{
char value = pattern[index];
if (value == '\\')
{
if (++index == pattern.Length || !meta.Contains(pattern[index])) return false;
value = pattern[index];
}
else if (value is < ' ' or > '~' || meta.Contains(value)) return false;
decoded[count++] = value;
}
literal = count == pattern.Length ? pattern : new string(decoded[..count]);
return count > 0;
}
}
using System.Text;
using System.Text.RegularExpressions;
namespace XFESystem.Collections;
/// <summary>A bounded atom replacement fast path for the private Jint runtime.</summary>
public static class LiteralRegexReplacement
{
public static string Replace(Regex regex, string input, string replacement, int count)
{
// Every other regex shape, option, replacement substitution and overload
// retains the original engine and its timeout. No JS accessors or
// callbacks are moved: Jint has already converted these exact arguments.
if (count <= 0 || input.Length > 131_072 || replacement.Length > 4096 || replacement.Contains('$') ||
(regex.Options & (RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace | RegexOptions.RightToLeft)) != 0 ||
!TryLiteral(regex.ToString(), out string literal) ||
replacement.Length > literal.Length && input.Length + (long)(input.Length / literal.Length) * (replacement.Length - literal.Length) > 1_048_576)
return RegexPauseRecovery.Replace(regex, input, replacement, count);
int match = input.IndexOf(literal, StringComparison.Ordinal);
if (match < 0) return input;
var output = new StringBuilder(input.Length); int start = 0, replaced = 0;
do
{
output.Append(input, start, match - start).Append(replacement);
start = match + literal.Length;
if (++replaced == count) break;
match = input.IndexOf(literal, start, StringComparison.Ordinal);
} while (match >= 0);
return output.Append(input, start, input.Length - start).ToString();
}
private static bool TryLiteral(string pattern, out string literal)
{
literal = string.Empty;
if (pattern.Length is < 1 or > 256) return false;
Span<char> decoded = stackalloc char[256]; int count = 0;
const string meta = @"\.^$|?*+()[]{}";
for (int index = 0; index < pattern.Length; index++)
{
char value = pattern[index];
if (value == '\\')
{
if (++index == pattern.Length || !meta.Contains(pattern[index])) return false;
value = pattern[index];
}
else if (value is < ' ' or > '~' || meta.Contains(value)) return false;
decoded[count++] = value;
}
literal = count == pattern.Length ? pattern : new string(decoded[..count]);
return count > 0;
}
}