From c05b586b25a5c263d89bd16e4a044f10fef9d5f1 Mon Sep 17 00:00:00 2001
From: Stephen Toub <stoub@microsoft.com>
Date: Mon, 18 Nov 2019 12:05:52 -0500
Subject: [PATCH 01/21] Avoid generating timeout checks for infinite timeouts

---
 .../Text/RegularExpressions/Regex.Cache.cs    | 91 +++++++++++--------
 .../Text/RegularExpressions/Regex.Timeout.cs  |  7 +-
 .../System/Text/RegularExpressions/Regex.cs   | 11 ++-
 .../Text/RegularExpressions/RegexCompiler.cs  | 35 +++++--
 .../RegularExpressions/RegexLWCGCompiler.cs   |  9 +-
 .../tests/Regex.Ctor.Tests.cs                 | 12 +--
 6 files changed, 96 insertions(+), 69 deletions(-)

diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Cache.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Cache.cs
index b6eb5754e43055..adf4eff205c3ff 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Cache.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Cache.cs
@@ -8,6 +8,7 @@
 using System.Runtime.CompilerServices;
 using System.Threading;
 using System.Diagnostics.CodeAnalysis;
+using System.Runtime.InteropServices;
 
 namespace System.Text.RegularExpressions
 {
@@ -26,10 +27,7 @@ public partial class Regex
 
         public static int CacheSize
         {
-            get
-            {
-                return s_cacheSize;
-            }
+            get => s_cacheSize;
             set
             {
                 if (value < 0)
@@ -76,8 +74,9 @@ public static int CacheSize
         {
             // to avoid lock:
             CachedCodeEntry? first = s_cacheFirst;
-            if (first?.Key == key)
+            if (first != null && first.Key.Equals(key))
                 return first;
+
             if (s_cacheSize == 0)
                 return null;
 
@@ -90,10 +89,12 @@ public static int CacheSize
             {
                 // first look for it in the cache and move it to the head
                 CachedCodeEntry? entry = LookupCachedAndPromote(key);
+
                 // it wasn't in the cache, so we'll add a new one
                 if (entry == null && isToAdd && s_cacheSize != 0) // check cache size again in case it changed
                 {
                     entry = new CachedCodeEntry(key, capnames!, capslist!, _code!, caps!, capsize, _runnerref!, _replref!);
+
                     // put first in linked list:
                     if (s_cacheFirst != null)
                     {
@@ -107,9 +108,14 @@ public static int CacheSize
                     if (s_cacheCount >= CacheDictionarySwitchLimit)
                     {
                         if (s_cacheCount == CacheDictionarySwitchLimit)
+                        {
                             FillCacheDictionary();
+                        }
                         else
+                        {
                             s_cache.Add(key, entry);
+                        }
+
                         SysDebug.Assert(s_cacheCount == s_cache.Count);
                     }
 
@@ -134,8 +140,8 @@ public static int CacheSize
                         s_cacheLast = last.Next;
                         s_cacheCount--;
                     }
-
                 }
+
                 return entry;
             }
         }
@@ -167,24 +173,31 @@ private static bool TryGetCacheValue(CachedCodeEntryKey key, [NotNullWhen(true)]
 
         private static bool TryGetCacheValueSmall(CachedCodeEntryKey key, [NotNullWhen(true)] out CachedCodeEntry? entry)
         {
-            entry = s_cacheFirst?.Previous; // first already checked
-            while (entry != null)
+            CachedCodeEntry? current = s_cacheFirst; // first already checked
+            if (current != null)
             {
-                if (entry.Key == key)
-                    return true;
-                entry = entry.Previous;
+                for (current = current.Previous; current != null; current = current.Previous)
+                {
+                    if (current.Key.Equals(key))
+                    {
+                        entry = current;
+                        return true;
+                    }
+                }
             }
 
+            entry = null;
             return false;
         }
 
         private static CachedCodeEntry? LookupCachedAndPromote(CachedCodeEntryKey key)
         {
             SysDebug.Assert(Monitor.IsEntered(s_cache));
-            if (s_cacheFirst?.Key == key) // again check this as could have been promoted by other thread
-                return s_cacheFirst;
 
-            if (TryGetCacheValue(key, out CachedCodeEntry? entry))
+            CachedCodeEntry? entry = s_cacheFirst;
+            if (entry != null &&
+                !entry.Key.Equals(key) && // again check this as could have been promoted by other thread
+                TryGetCacheValue(key, out entry))
             {
                 // promote:
                 SysDebug.Assert(s_cacheFirst != entry, "key should not get s_livecode_first");
@@ -220,44 +233,42 @@ private static bool TryGetCacheValueSmall(CachedCodeEntryKey key, [NotNullWhen(t
         /// </summary>
         internal readonly struct CachedCodeEntryKey : IEquatable<CachedCodeEntryKey>
         {
-            private readonly RegexOptions _options;
-            private readonly string _cultureKey;
             private readonly string _pattern;
+            private readonly string _cultureKey;
+            private readonly RegexOptions _options;
+            private readonly bool _hasTimeout;
 
-            public CachedCodeEntryKey(RegexOptions options, string cultureKey, string pattern)
+            public CachedCodeEntryKey(string pattern, string cultureKey, RegexOptions options, bool hasTimeout)
             {
-                SysDebug.Assert(cultureKey != null, "Culture must be provided");
                 SysDebug.Assert(pattern != null, "Pattern must be provided");
+                SysDebug.Assert(cultureKey != null, "Culture must be provided");
 
-                _options = options;
-                _cultureKey = cultureKey;
                 _pattern = pattern;
+                _cultureKey = cultureKey;
+                _options = options;
+                _hasTimeout = hasTimeout;
             }
 
-            public override bool Equals(object? obj)
-            {
-                return obj is CachedCodeEntryKey && Equals((CachedCodeEntryKey)obj);
-            }
+            public override bool Equals(object? obj) =>
+                obj is CachedCodeEntryKey other && Equals(other);
 
-            public bool Equals(CachedCodeEntryKey other)
-            {
-                return _pattern.Equals(other._pattern) && _options == other._options && _cultureKey.Equals(other._cultureKey);
-            }
+            public bool Equals(CachedCodeEntryKey other) =>
+                _pattern.Equals(other._pattern) &&
+                _cultureKey.Equals(other._cultureKey) &&
+                _options == other._options &&
+                _hasTimeout == other._hasTimeout;
 
-            public static bool operator ==(CachedCodeEntryKey left, CachedCodeEntryKey right)
-            {
-                return left.Equals(right);
-            }
+            public static bool operator ==(CachedCodeEntryKey left, CachedCodeEntryKey right) =>
+                left.Equals(right);
 
-            public static bool operator !=(CachedCodeEntryKey left, CachedCodeEntryKey right)
-            {
-                return !left.Equals(right);
-            }
+            public static bool operator !=(CachedCodeEntryKey left, CachedCodeEntryKey right) =>
+                !left.Equals(right);
 
-            public override int GetHashCode()
-            {
-                return ((int)_options) ^ _cultureKey.GetHashCode() ^ _pattern.GetHashCode();
-            }
+            public override int GetHashCode() =>
+                _pattern.GetHashCode() ^
+                _cultureKey.GetHashCode() ^
+                ((int)_options);
+                // no need to include timeout in the hashcode; it'll almost always be the same
         }
 
         /// <summary>
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Timeout.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Timeout.cs
index cf571e87d4735c..de101a5641d384 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Timeout.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Timeout.cs
@@ -18,11 +18,10 @@ public partial class Regex
         // InfiniteMatchTimeout specifies that match timeout is switched OFF. It allows for faster code paths
         // compared to simply having a very large timeout.
         // We do not want to ask users to use System.Threading.Timeout.InfiniteTimeSpan as a parameter because:
-        //   (1) We do not want to imply any relation between having using a RegEx timeout and using multi-threading.
+        //   (1) We do not want to imply any relation between using a Regx timeout and using multi-threading.
         //   (2) We do not want to require users to take ref to a contract assembly for threading just to use RegEx.
-        //       There may in theory be a SKU that has RegEx, but no multithreading.
-        // We create a public Regex.InfiniteMatchTimeout constant, which for consistency uses the save underlying
-        // value as Timeout.InfiniteTimeSpan creating an implementation detail dependency only.
+        // We create a public Regex.InfiniteMatchTimeout constant, which for consistency uses the same underlying
+        // value as Timeout.InfiniteTimeSpan, creating an implementation detail dependency only.
         public static readonly TimeSpan InfiniteMatchTimeout = Timeout.InfiniteTimeSpan;
 
         // DefaultMatchTimeout specifies the match timeout to use if no other timeout was specified
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.cs
index 08f744b69877d9..e62d17cd68c59d 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.cs
@@ -14,6 +14,7 @@
 using System.Runtime.CompilerServices;
 #endif
 using System.Runtime.Serialization;
+using System.Threading;
 
 namespace System.Text.RegularExpressions
 {
@@ -115,8 +116,8 @@ private Regex(string pattern, RegexOptions options, TimeSpan matchTimeout, bool
             CultureInfo culture = (options & RegexOptions.CultureInvariant) != 0 ?
                 CultureInfo.InvariantCulture :
                 CultureInfo.CurrentCulture;
-            var key = new CachedCodeEntryKey(options, culture.ToString(), pattern);
-            CachedCodeEntry? cached = GetCachedCode(key, false);
+            var key = new CachedCodeEntryKey(pattern, culture.ToString(), options, matchTimeout != InfiniteMatchTimeout);
+            CachedCodeEntry? cached = GetCachedCode(key, isToAdd: false);
 
             if (cached == null)
             {
@@ -165,7 +166,7 @@ private void InitWithPossibleCompilation(string pattern, RegexOptions options, T
             // if the compile option is set, then compile the code if it's not already
             if (UseOptionC() && factory == null)
             {
-                factory = Compile(_code!, roptions);
+                factory = Compile(_code!, roptions, matchTimeout != InfiniteMatchTimeout);
 
                 if (addToCache && cached != null)
                 {
@@ -222,9 +223,9 @@ protected IDictionary? CapNames
         /// instantiating a non-compiled regex.
         /// </summary>
         [MethodImplAttribute(MethodImplOptions.NoInlining)]
-        private RegexRunnerFactory Compile(RegexCode code, RegexOptions roptions)
+        private RegexRunnerFactory Compile(RegexCode code, RegexOptions roptions, bool hasTimeout)
         {
-            return RegexCompiler.Compile(code!, roptions);
+            return RegexCompiler.Compile(code!, roptions, hasTimeout);
         }
 #endif
 
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs
index bc3339c3452b97..46cb59c2d6af5a 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs
@@ -81,6 +81,7 @@ internal abstract class RegexCompiler
         protected RegexPrefix? _fcPrefix;          // the possible first chars computed by RegexFCD
         protected RegexBoyerMoore? _bmPrefix;          // a prefix as a boyer-moore machine
         protected int _anchors;           // the set of anchors
+        protected bool _hasTimeout; // whether the regex has a non-infinite timeout
 
         private Label[]? _labels;            // a label for every operation in _codes
         private BacktrackNote[]? _notes;             // a list of the backtracking states to be generated
@@ -127,9 +128,9 @@ private static MethodInfo RegexRunnerMethod(string methname)
          * Entry point to dynamically compile a regular expression.  The expression is compiled to
          * an in-memory assembly.
          */
-        internal static RegexRunnerFactory Compile(RegexCode code, RegexOptions options)
+        internal static RegexRunnerFactory Compile(RegexCode code, RegexOptions options, bool hasTimeout)
         {
-            return new RegexLWCGCompiler().FactoryInstanceFromCode(code, options);
+            return new RegexLWCGCompiler().FactoryInstanceFromCode(code, options, hasTimeout);
         }
 
         /*
@@ -1580,7 +1581,10 @@ protected void GenerateGo()
             _tempV = DeclareInt();
             _temp2V = DeclareInt();
             _temp3V = DeclareInt();
-            _loopV = DeclareInt();
+            if (_hasTimeout)
+            {
+                _loopV = DeclareInt();
+            }
             _textbegV = DeclareInt();
             _textendV = DeclareInt();
             _textstartV = DeclareInt();
@@ -1662,8 +1666,11 @@ private void GenerateOneCode()
             // Before executing any RegEx code in the unrolled loop,
             // we try checking for the match timeout:
 
-            Ldthis();
-            Callvirt(s_checkTimeoutM);
+            if (_hasTimeout)
+            {
+                Ldthis();
+                Callvirt(s_checkTimeoutM);
+            }
 
             // Now generate the IL for the RegEx code saved in _regexopcode.
             // We unroll the loop done by the RegexCompiler creating as very long method
@@ -2766,7 +2773,10 @@ private void GenerateOneCode()
 
                         if (Code() == RegexCode.Setrep)
                         {
-                            EmitTimeoutCheck();
+                            if (_hasTimeout)
+                            {
+                                EmitTimeoutCheck();
+                            }
                             Ldstr(_strings![Operand(0)]);
                             Call(s_charInSetM);
 
@@ -2876,7 +2886,10 @@ private void GenerateOneCode()
 
                         if (Code() == RegexCode.Setloop)
                         {
-                            EmitTimeoutCheck();
+                            if (_hasTimeout)
+                            {
+                                EmitTimeoutCheck();
+                            }
                             Ldstr(_strings![Operand(0)]);
                             Call(s_charInSetM);
 
@@ -3089,16 +3102,18 @@ private void GenerateOneCode()
 
         private void EmitTimeoutCheck()
         {
+            Debug.Assert(_hasTimeout && _loopV != null);
+
             Label label = DefineLabel();
 
             // Increment counter for each loop iteration.
-            Ldloc(_loopV!);
+            Ldloc(_loopV);
             Ldc(1);
             Add();
-            Stloc(_loopV!);
+            Stloc(_loopV);
 
             // Emit code to check the timeout every 2000th-iteration.
-            Ldloc(_loopV!);
+            Ldloc(_loopV);
             Ldc(LoopTimeoutCheckCount);
             Rem();
             Ldc(0);
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexLWCGCompiler.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexLWCGCompiler.cs
index 6fc875bac2d167..d15214a171676c 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexLWCGCompiler.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexLWCGCompiler.cs
@@ -17,7 +17,7 @@ internal sealed class RegexLWCGCompiler : RegexCompiler
         /*
          * The top-level driver. Initializes everything then calls the Generate* methods.
          */
-        public RegexRunnerFactory FactoryInstanceFromCode(RegexCode code, RegexOptions options)
+        public RegexRunnerFactory FactoryInstanceFromCode(RegexCode code, RegexOptions options, bool hasTimeout)
         {
             _code = code;
             _codes = code.Codes;
@@ -27,6 +27,7 @@ public RegexRunnerFactory FactoryInstanceFromCode(RegexCode code, RegexOptions o
             _anchors = code.Anchors;
             _trackcount = code.TrackCount;
             _options = options;
+            _hasTimeout = hasTimeout;
 
             // pick a unique number for the methods we generate
             int regexnum = Interlocked.Increment(ref s_regexCount);
@@ -53,10 +54,10 @@ public DynamicMethod DefineDynamicMethod(string methname, Type? returntype, Type
             // By giving them a parameter which represents "this", we're tricking them into
             // being instance methods.
 
-            MethodAttributes attribs = MethodAttributes.Public | MethodAttributes.Static;
-            CallingConventions conventions = CallingConventions.Standard;
+            const MethodAttributes Attribs = MethodAttributes.Public | MethodAttributes.Static;
+            const CallingConventions Conventions = CallingConventions.Standard;
 
-            DynamicMethod dm = new DynamicMethod(methname, attribs, conventions, returntype, s_paramTypes, hostType, false /*skipVisibility*/);
+            DynamicMethod dm = new DynamicMethod(methname, Attribs, Conventions, returntype, s_paramTypes, hostType, false /*skipVisibility*/);
             _ilg = dm.GetILGenerator();
             return dm;
         }
diff --git a/src/libraries/System.Text.RegularExpressions/tests/Regex.Ctor.Tests.cs b/src/libraries/System.Text.RegularExpressions/tests/Regex.Ctor.Tests.cs
index 747e93939c780a..fccd8fc6a4298d 100644
--- a/src/libraries/System.Text.RegularExpressions/tests/Regex.Ctor.Tests.cs
+++ b/src/libraries/System.Text.RegularExpressions/tests/Regex.Ctor.Tests.cs
@@ -15,11 +15,11 @@ public class RegexConstructorTests
     {
         public static IEnumerable<object[]> Ctor_TestData()
         {
-            yield return new object[] { "foo", RegexOptions.None, Timeout.InfiniteTimeSpan };
-            yield return new object[] { "foo", RegexOptions.RightToLeft, Timeout.InfiniteTimeSpan };
-            yield return new object[] { "foo", RegexOptions.Compiled, Timeout.InfiniteTimeSpan };
-            yield return new object[] { "foo", RegexOptions.ECMAScript | RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.CultureInvariant, Timeout.InfiniteTimeSpan };
-            yield return new object[] { "foo", RegexOptions.ECMAScript | RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled, Timeout.InfiniteTimeSpan };
+            yield return new object[] { "foo", RegexOptions.None, Regex.InfiniteMatchTimeout };
+            yield return new object[] { "foo", RegexOptions.RightToLeft, Regex.InfiniteMatchTimeout };
+            yield return new object[] { "foo", RegexOptions.Compiled, Regex.InfiniteMatchTimeout };
+            yield return new object[] { "foo", RegexOptions.ECMAScript | RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.CultureInvariant, Regex.InfiniteMatchTimeout };
+            yield return new object[] { "foo", RegexOptions.ECMAScript | RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.CultureInvariant | RegexOptions.Compiled, Regex.InfiniteMatchTimeout };
             yield return new object[] { "foo", RegexOptions.None, new TimeSpan(1) };
             yield return new object[] { "foo", RegexOptions.None, TimeSpan.FromMilliseconds(int.MaxValue - 1) };
         }
@@ -28,7 +28,7 @@ public static IEnumerable<object[]> Ctor_TestData()
         [MemberData(nameof(Ctor_TestData))]
         public static void Ctor(string pattern, RegexOptions options, TimeSpan matchTimeout)
         {
-            if (matchTimeout == Timeout.InfiniteTimeSpan)
+            if (matchTimeout == Regex.InfiniteMatchTimeout)
             {
                 if (options == RegexOptions.None)
                 {

From 265191aef9062a2a876cac104c340638504185e5 Mon Sep 17 00:00:00 2001
From: Stephen Toub <stoub@microsoft.com>
Date: Mon, 18 Nov 2019 12:06:52 -0500
Subject: [PATCH 02/21] Use Ldc_I4_X for Ldc when applicable

---
 .../Text/RegularExpressions/RegexCompiler.cs  | 26 ++++++++++++++++---
 1 file changed, 23 insertions(+), 3 deletions(-)

diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs
index 46cb59c2d6af5a..f955e1382e3094 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs
@@ -299,10 +299,30 @@ private void Ldstr(string str)
          */
         private void Ldc(int i)
         {
-            if (i <= 127 && i >= -128)
-                _ilg!.Emit(OpCodes.Ldc_I4_S, (byte)i);
+            Debug.Assert(_ilg != null);
+
+            if ((uint)i < 8)
+            {
+                _ilg.Emit(i switch
+                {
+                    0 => OpCodes.Ldc_I4_0,
+                    1 => OpCodes.Ldc_I4_1,
+                    2 => OpCodes.Ldc_I4_2,
+                    3 => OpCodes.Ldc_I4_3,
+                    4 => OpCodes.Ldc_I4_4,
+                    5 => OpCodes.Ldc_I4_5,
+                    6 => OpCodes.Ldc_I4_6,
+                    _ => OpCodes.Ldc_I4_7,
+                });
+            }
+            else if (i <= 127 && i >= -128)
+            {
+                _ilg.Emit(OpCodes.Ldc_I4_S, (byte)i);
+            }
             else
-                _ilg!.Emit(OpCodes.Ldc_I4, i);
+            {
+                _ilg.Emit(OpCodes.Ldc_I4, i);
+            }
         }
 
         private void LdcI8(long i)

From 7c536c3dd374a4dd75dfa88c868fa6403eee15ec Mon Sep 17 00:00:00 2001
From: Stephen Toub <stoub@microsoft.com>
Date: Mon, 18 Nov 2019 12:07:36 -0500
Subject: [PATCH 03/21] Use ToLowerInvariant for invariant mode

---
 .../Text/RegularExpressions/RegexCompiler.cs  | 68 ++++++++++++++-----
 1 file changed, 52 insertions(+), 16 deletions(-)

diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs
index f955e1382e3094..4e8434fda96fd0 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs
@@ -47,11 +47,11 @@ internal abstract class RegexCompiler
         private static readonly MethodInfo s_isboundaryM = RegexRunnerMethod("IsBoundary");
         private static readonly MethodInfo s_isECMABoundaryM = RegexRunnerMethod("IsECMABoundary");
         private static readonly MethodInfo s_chartolowerM = typeof(char).GetMethod("ToLower", new Type[] { typeof(char), typeof(CultureInfo) })!;
+        private static readonly MethodInfo s_chartolowerinvariantM = typeof(char).GetMethod("ToLowerInvariant", new Type[] { typeof(char) })!;
         private static readonly MethodInfo s_getcharM = typeof(string).GetMethod("get_Chars", new Type[] { typeof(int) })!;
         private static readonly MethodInfo s_crawlposM = RegexRunnerMethod("Crawlpos");
         private static readonly MethodInfo s_charInSetM = RegexRunnerMethod("CharInClass");
         private static readonly MethodInfo s_getCurrentCulture = typeof(CultureInfo).GetMethod("get_CurrentCulture")!;
-        private static readonly MethodInfo s_getInvariantCulture = typeof(CultureInfo).GetMethod("get_InvariantCulture")!;
         private static readonly MethodInfo s_checkTimeoutM = RegexRunnerMethod("CheckTimeout");
 #if DEBUG
         private static readonly MethodInfo s_dumpstateM = RegexRunnerMethod("DumpState");
@@ -966,18 +966,22 @@ private void Advance()
 
         private void InitLocalCultureInfo()
         {
-            if ((_options & RegexOptions.CultureInvariant) != 0)
-                Call(s_getInvariantCulture);
-            else
-                Call(s_getCurrentCulture);
-
-            Stloc(_cultureV!);
+            Debug.Assert(_cultureV != null);
+            Call(s_getCurrentCulture);
+            Stloc(_cultureV);
         }
 
         private void CallToLower()
         {
-            Ldloc(_cultureV!);
-            Call(s_chartolowerM);
+            if (_cultureV == null || _options.HasFlag(RegexOptions.CultureInvariant))
+            {
+                Call(s_chartolowerinvariantM);
+            }
+            else
+            {
+                Ldloc(_cultureV!);
+                Call(s_chartolowerM);
+            }
         }
 
         /*
@@ -1097,9 +1101,17 @@ protected void GenerateFindFirstChar()
             _textV = DeclareString();
             _tempV = DeclareInt();
             _temp2V = DeclareInt();
-            _cultureV = DeclareCultureInfo();
-
-            InitLocalCultureInfo();
+            _cultureV = null;
+            if (!_options.HasFlag(RegexOptions.CultureInvariant))
+            {
+                if (_options.HasFlag(RegexOptions.IgnoreCase) ||
+                    _bmPrefix?.CaseInsensitive == true ||
+                    _fcPrefix.GetValueOrDefault().CaseInsensitive)
+                {
+                    _cultureV = DeclareCultureInfo();
+                    InitLocalCultureInfo();
+                }
+            }
 
             if (0 != (_anchors & (RegexFCD.Beginning | RegexFCD.Start | RegexFCD.EndZ | RegexFCD.End)))
             {
@@ -1564,9 +1576,9 @@ private LocalBuilder DeclareInt()
         /*
          * Declares a local CultureInfo
          */
-        private LocalBuilder DeclareCultureInfo()
+        private LocalBuilder? DeclareCultureInfo()
         {
-            return _ilg!.DeclareLocal(typeof(CultureInfo));
+            return _ilg!.DeclareLocal(typeof(CultureInfo)); // cache local variable to avoid unnecessary TLS
         }
 
         /*
@@ -1608,7 +1620,28 @@ protected void GenerateGo()
             _textbegV = DeclareInt();
             _textendV = DeclareInt();
             _textstartV = DeclareInt();
-            _cultureV = DeclareCultureInfo();
+
+            _cultureV = null;
+            if (!_options.HasFlag(RegexOptions.CultureInvariant))
+            {
+                bool needsCulture = _options.HasFlag(RegexOptions.IgnoreCase);
+                if (!needsCulture)
+                {
+                    for (int codepos = 0; codepos < _codes!.Length; codepos += RegexCode.OpcodeSize(_codes[codepos]))
+                    {
+                        if ((_codes[codepos] & RegexCode.Ci) == RegexCode.Ci)
+                        {
+                            needsCulture = true;
+                            break;
+                        }
+                    }
+                }
+
+                if (needsCulture)
+                {
+                    _cultureV = DeclareCultureInfo();
+                }
+            }
 
             // clear some tables
 
@@ -1623,7 +1656,10 @@ protected void GenerateGo()
             // emit the code!
 
             // cache CultureInfo in local variable which saves excessive thread local storage accesses
-            InitLocalCultureInfo();
+            if (_cultureV != null)
+            {
+                InitLocalCultureInfo();
+            }
 
             GenerateForwardSection();
             GenerateMiddleSection();

From 30495bff0209ab831829b96691190123a11c0433 Mon Sep 17 00:00:00 2001
From: Stephen Toub <stoub@microsoft.com>
Date: Mon, 18 Nov 2019 12:08:11 -0500
Subject: [PATCH 04/21] Remove unnecessary virtual-ness of some internal
 methods

---
 .../System/Text/RegularExpressions/Match.cs    | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Match.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Match.cs
index fb0fd7cbe50de9..1fb35fde8199f2 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Match.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Match.cs
@@ -73,7 +73,7 @@ internal Match(Regex? regex, int capcount, string text, int begpos, int len, int
         /// </summary>
         public static Match Empty { get; } = new Match(null, 1, string.Empty, 0, 0, 0);
 
-        internal virtual void Reset(Regex regex, string text, int textbeg, int textend, int textstart)
+        internal void Reset(Regex regex, string text, int textbeg, int textend, int textstart)
         {
             _regex = regex;
             Text = text;
@@ -137,7 +137,7 @@ public virtual string Result(string replacement)
             return vsb.ToString();
         }
 
-        internal virtual ReadOnlySpan<char> GroupToStringImpl(int groupnum)
+        internal ReadOnlySpan<char> GroupToStringImpl(int groupnum)
         {
             int c = _matchcount[groupnum];
             if (c == 0)
@@ -180,7 +180,7 @@ public static Match Synchronized(Match inner)
         /// <summary>
         /// Adds a capture to the group specified by "cap"
         /// </summary>
-        internal virtual void AddMatch(int cap, int start, int len)
+        internal void AddMatch(int cap, int start, int len)
         {
             int capcount;
 
@@ -210,7 +210,7 @@ balanced match construct. (?<foo-foo2>...)
            If there were no such thing as backtracking, this would be as simple as calling RemoveMatch(cap).
            However, since we have backtracking, we need to keep track of everything.
          */
-        internal virtual void BalanceMatch(int cap)
+        internal void BalanceMatch(int cap)
         {
             _balancing = true;
 
@@ -236,7 +236,7 @@ internal virtual void BalanceMatch(int cap)
         /// <summary>
         /// Removes a group match by capnum
         /// </summary>
-        internal virtual void RemoveMatch(int cap)
+        internal void RemoveMatch(int cap)
         {
             _matchcount[cap]--;
         }
@@ -244,7 +244,7 @@ internal virtual void RemoveMatch(int cap)
         /// <summary>
         /// Tells if a group was matched by capnum
         /// </summary>
-        internal virtual bool IsMatched(int cap)
+        internal bool IsMatched(int cap)
         {
             return cap < _matchcount.Length && _matchcount[cap] > 0 && _matches[cap][_matchcount[cap] * 2 - 1] != (-3 + 1);
         }
@@ -252,7 +252,7 @@ internal virtual bool IsMatched(int cap)
         /// <summary>
         /// Returns the index of the last specified matched group by capnum
         /// </summary>
-        internal virtual int MatchIndex(int cap)
+        internal int MatchIndex(int cap)
         {
             int i = _matches[cap][_matchcount[cap] * 2 - 2];
             if (i >= 0)
@@ -264,7 +264,7 @@ internal virtual int MatchIndex(int cap)
         /// <summary>
         /// Returns the length of the last specified matched group by capnum
         /// </summary>
-        internal virtual int MatchLength(int cap)
+        internal int MatchLength(int cap)
         {
             int i = _matches[cap][_matchcount[cap] * 2 - 1];
             if (i >= 0)
@@ -276,7 +276,7 @@ internal virtual int MatchLength(int cap)
         /// <summary>
         /// Tidy the match so that it can be used as an immutable result
         /// </summary>
-        internal virtual void Tidy(int textpos)
+        internal void Tidy(int textpos)
         {
             int[] interval = _matches[0];
             Index = interval[0];

From 118dea99e6312941672f3ed89792f9234d1a239e Mon Sep 17 00:00:00 2001
From: Stephen Toub <stoub@microsoft.com>
Date: Mon, 18 Nov 2019 12:09:54 -0500
Subject: [PATCH 05/21] Clean up CompiledRegexRunner

---
 .../RegularExpressions/CompiledRegexRunner.cs | 25 ++++++-------------
 .../CompiledRegexRunnerFactory.cs             | 20 ++++++---------
 2 files changed, 15 insertions(+), 30 deletions(-)

diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/CompiledRegexRunner.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/CompiledRegexRunner.cs
index 098b311b14b1e2..a579e8a3d5556b 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/CompiledRegexRunner.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/CompiledRegexRunner.cs
@@ -6,32 +6,21 @@ namespace System.Text.RegularExpressions
 {
     internal sealed class CompiledRegexRunner : RegexRunner
     {
-        private Action<RegexRunner>? _goMethod;
-        private Func<RegexRunner, bool>? _findFirstCharMethod;
-        private Action<RegexRunner>? _initTrackCountMethod;
+        private readonly Action<RegexRunner> _goMethod;
+        private readonly Func<RegexRunner, bool> _findFirstCharMethod;
+        private readonly Action<RegexRunner> _initTrackCountMethod;
 
-        public CompiledRegexRunner() { }
-
-        public void SetDelegates(Action<RegexRunner> go, Func<RegexRunner, bool> firstChar, Action<RegexRunner> trackCount)
+        public CompiledRegexRunner(Action<RegexRunner> go, Func<RegexRunner, bool> firstChar, Action<RegexRunner> trackCount)
         {
             _goMethod = go;
             _findFirstCharMethod = firstChar;
             _initTrackCountMethod = trackCount;
         }
 
-        protected override void Go()
-        {
-            _goMethod!(this);
-        }
+        protected override void Go() => _goMethod(this);
 
-        protected override bool FindFirstChar()
-        {
-            return _findFirstCharMethod!(this);
-        }
+        protected override bool FindFirstChar() => _findFirstCharMethod(this);
 
-        protected override void InitTrackCount()
-        {
-            _initTrackCountMethod!(this);
-        }
+        protected override void InitTrackCount() => _initTrackCountMethod(this);
     }
 }
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/CompiledRegexRunnerFactory.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/CompiledRegexRunnerFactory.cs
index 7bf4c0e3f8d106..0b5a5504adaf5a 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/CompiledRegexRunnerFactory.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/CompiledRegexRunnerFactory.cs
@@ -15,21 +15,17 @@ internal sealed class CompiledRegexRunnerFactory : RegexRunnerFactory
         private readonly DynamicMethod _findFirstCharMethod;
         private readonly DynamicMethod _initTrackCountMethod;
 
-        public CompiledRegexRunnerFactory(DynamicMethod go, DynamicMethod firstChar, DynamicMethod trackCount)
+        public CompiledRegexRunnerFactory(DynamicMethod go, DynamicMethod findFirstChar, DynamicMethod initTrackCount)
         {
             _goMethod = go;
-            _findFirstCharMethod = firstChar;
-            _initTrackCountMethod = trackCount;
+            _findFirstCharMethod = findFirstChar;
+            _initTrackCountMethod = initTrackCount;
         }
 
-        protected internal override RegexRunner CreateInstance()
-        {
-            CompiledRegexRunner runner = new CompiledRegexRunner();
-            runner.SetDelegates((Action<RegexRunner>)_goMethod.CreateDelegate(typeof(Action<RegexRunner>)),
-                                (Func<RegexRunner, bool>)_findFirstCharMethod.CreateDelegate(typeof(Func<RegexRunner, bool>)),
-                                (Action<RegexRunner>)_initTrackCountMethod.CreateDelegate(typeof(Action<RegexRunner>)));
-
-            return runner;
-        }
+        protected internal override RegexRunner CreateInstance() =>
+            new CompiledRegexRunner(
+                (Action<RegexRunner>)_goMethod.CreateDelegate(typeof(Action<RegexRunner>)),
+                (Func<RegexRunner, bool>)_findFirstCharMethod.CreateDelegate(typeof(Func<RegexRunner, bool>)),
+                (Action<RegexRunner>)_initTrackCountMethod.CreateDelegate(typeof(Action<RegexRunner>)));
     }
 }

From dc3cb47239331a613edc2536912eb41f8893dd3f Mon Sep 17 00:00:00 2001
From: Stephen Toub <stoub@microsoft.com>
Date: Mon, 18 Nov 2019 12:11:47 -0500
Subject: [PATCH 06/21] Lazily allocate RegexCharClass._categories

---
 .../Text/RegularExpressions/RegexCharClass.cs | 27 ++++++++++++-------
 1 file changed, 18 insertions(+), 9 deletions(-)

diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
index 4ee4a049a2179f..ac30755c194639 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
@@ -398,7 +398,7 @@ these intervals. It follows from the nature of the L on each interval.
         };
 
         private readonly List<SingleRange> _rangelist;
-        private readonly StringBuilder _categories;
+        private StringBuilder? _categories;
         private bool _canonical;
         private bool _negate;
         private RegexCharClass? _subtractor;
@@ -428,7 +428,6 @@ public RegexCharClass()
         {
             _rangelist = new List<SingleRange>(6);
             _canonical = true;
-            _categories = new StringBuilder();
         }
 
         private RegexCharClass(bool negate, List<SingleRange> ranges, StringBuilder categories, RegexCharClass? subtraction)
@@ -480,9 +479,15 @@ public void AddCharClass(RegexCharClass cc)
                 _rangelist.Add(cc.GetRangeAt(i));
             }
 
-            _categories.Append(cc._categories.ToString());
+            if (cc._categories != null)
+            {
+                EnsureCategories().Append(cc._categories);
+            }
         }
 
+        private StringBuilder EnsureCategories() =>
+            _categories ??= new StringBuilder();
+
         /// <summary>
         /// Adds a set (specified by its string representation) to the class.
         /// </summary>
@@ -537,7 +542,7 @@ public void AddCategoryFromName(string categoryName, bool invert, bool caseInsen
                 if (invert)
                     category = NegateCategory(category); // negate the category
 
-                _categories.Append(category);
+                EnsureCategories().Append(category);
             }
             else
                 AddSet(SetFromProperty(categoryName, invert, pattern, currentPos));
@@ -545,7 +550,7 @@ public void AddCategoryFromName(string categoryName, bool invert, bool caseInsen
 
         private void AddCategory(string category)
         {
-            _categories.Append(category);
+            EnsureCategories().Append(category);
         }
 
         /// <summary>
@@ -1026,8 +1031,9 @@ public string ToStringClass()
             // make a guess about the length of the ranges.  We'll update this at the end.
             // This is important because if the last range ends in LastChar, we won't append
             // LastChar to the list.
+            int categoriesLength = _categories?.Length ?? 0;
             int rangeLen = _rangelist.Count * 2;
-            int strGuessCount = rangeLen + _categories.Length + 3;
+            int strGuessCount = rangeLen + categoriesLength + 3;
 
             Span<char> buffer = strGuessCount <= 256 ? stackalloc char[256] : null;
             ValueStringBuilder vsb = buffer != null ?
@@ -1038,7 +1044,7 @@ public string ToStringClass()
 
             vsb.Append((char)flags);
             vsb.Append((char)rangeLen);
-            vsb.Append((char)_categories.Length);
+            vsb.Append((char)categoriesLength);
 
             for (int i = 0; i < _rangelist.Count; i++)
             {
@@ -1052,9 +1058,12 @@ public string ToStringClass()
             vsb[SETLENGTH] = (char)(vsb.Length - SETSTART);
 
             // Append the categories string
-            foreach (ReadOnlyMemory<char> chunk in _categories.GetChunks())
+            if (_categories != null)
             {
-                vsb.Append(chunk.Span);
+                foreach (ReadOnlyMemory<char> chunk in _categories.GetChunks())
+                {
+                    vsb.Append(chunk.Span);
+                }
             }
 
             if (_subtractor != null)

From 3d976b22e7cce91cdb582c6c81808620125174b5 Mon Sep 17 00:00:00 2001
From: Stephen Toub <stoub@microsoft.com>
Date: Mon, 18 Nov 2019 12:12:01 -0500
Subject: [PATCH 07/21] Avoid negative numbers in generated names

---
 .../src/System/Text/RegularExpressions/RegexLWCGCompiler.cs   | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexLWCGCompiler.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexLWCGCompiler.cs
index d15214a171676c..f4950ccb0dbc50 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexLWCGCompiler.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexLWCGCompiler.cs
@@ -5,7 +5,6 @@
 using System.Threading;
 using System.Reflection;
 using System.Reflection.Emit;
-using System.Globalization;
 
 namespace System.Text.RegularExpressions
 {
@@ -30,8 +29,7 @@ public RegexRunnerFactory FactoryInstanceFromCode(RegexCode code, RegexOptions o
             _hasTimeout = hasTimeout;
 
             // pick a unique number for the methods we generate
-            int regexnum = Interlocked.Increment(ref s_regexCount);
-            string regexnumString = regexnum.ToString(CultureInfo.InvariantCulture);
+            string regexnumString = ((uint)Interlocked.Increment(ref s_regexCount)).ToString();
 
             DynamicMethod goMethod = DefineDynamicMethod("Go" + regexnumString, null, typeof(CompiledRegexRunner));
             GenerateGo();

From ba2a5d3f307453b901a184d8b1401bf066f55bb5 Mon Sep 17 00:00:00 2001
From: Stephen Toub <stoub@microsoft.com>
Date: Mon, 18 Nov 2019 12:12:21 -0500
Subject: [PATCH 08/21] Optimize Is{ECMA}WordChar

---
 .../Text/RegularExpressions/RegexCharClass.cs | 40 ++++++++++++++++++-
 1 file changed, 38 insertions(+), 2 deletions(-)

diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
index ac30755c194639..42c65ca3a254e1 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
@@ -773,7 +773,11 @@ public static bool IsECMAWordChar(char ch)
             // interpretations of digit, word character, and word boundary.  In other words,
             // no special treatment of Unicode ZERO WIDTH NON-JOINER (ZWNJ U+200C) and
             // ZERO WIDTH JOINER (ZWJ U+200D) is required for ECMA word boundaries.
-            return CharInClass(ch, ECMAWordClass);
+            return
+                ((((uint)ch - 'A') & ~0x20) < 26) || // ASCII letter
+                (((uint)ch - '0') < 10) || // digit
+                ch == '_' || // underscore
+                ch == '\u0130'; // latin capital letter I with dot above
         }
 
         public static bool IsWordChar(char ch)
@@ -782,7 +786,39 @@ public static bool IsWordChar(char ch)
             // RL 1.4 Simple Word Boundaries  The class of <word_character> includes all Alphabetic
             // values from the Unicode character database, from UnicodeData.txt [UData], plus the U+200C
             // ZERO WIDTH NON-JOINER and U+200D ZERO WIDTH JOINER.
-            return CharInClass(ch, WordClass) || ch == ZeroWidthJoiner || ch == ZeroWidthNonJoiner;
+
+            // 16 bytes, representing the chars 0 through 127, with a 1 for a bit where that char is a word char
+            static ReadOnlySpan<byte> AsciiLookup() => new byte[]
+            {
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x03,
+                0xFE, 0xFF, 0xFF, 0x87, 0xFE, 0xFF, 0xFF, 0x07
+            };
+
+            // Fast lookup in our lookup table for ASCII characters.  This is purely an optimization, and has the
+            // behavior as if we fell through to the switch below (which was actually used to produce the lookup table).
+            ReadOnlySpan<byte> asciiLookup = AsciiLookup();
+            int chDiv8 = ch >> 3;
+            if ((uint)chDiv8 < asciiLookup.Length)
+            {
+                return (asciiLookup[chDiv8] & (1 << (ch & 0x7))) != 0;
+            }
+
+            // For non-ASCII, fall back to checking the Unicode category.
+            switch (CharUnicodeInfo.GetUnicodeCategory(ch))
+            {
+                case UnicodeCategory.UppercaseLetter:
+                case UnicodeCategory.LowercaseLetter:
+                case UnicodeCategory.TitlecaseLetter:
+                case UnicodeCategory.ModifierLetter:
+                case UnicodeCategory.OtherLetter:
+                case UnicodeCategory.NonSpacingMark:
+                case UnicodeCategory.DecimalDigitNumber:
+                case UnicodeCategory.ConnectorPunctuation:
+                    return true;
+
+                default:
+                    return ch == ZeroWidthJoiner || ch == ZeroWidthNonJoiner;
+            }
         }
 
         public static bool CharInClass(char ch, string set)

From 77d4537d84de4d3cc2dc93af41ba94d274659276 Mon Sep 17 00:00:00 2001
From: Stephen Toub <stoub@microsoft.com>
Date: Mon, 18 Nov 2019 12:12:39 -0500
Subject: [PATCH 09/21] Optimize common CharInClass calls

---
 .../Text/RegularExpressions/RegexCharClass.cs |  13 +-
 .../Text/RegularExpressions/RegexCompiler.cs  | 237 ++++++++++++++++--
 2 files changed, 228 insertions(+), 22 deletions(-)

diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
index 42c65ca3a254e1..a4e6513d5bcd2b 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
@@ -29,10 +29,10 @@ namespace System.Text.RegularExpressions
     internal sealed class RegexCharClass
     {
         // Constants
-        private const int FLAGS = 0;
-        private const int SETLENGTH = 1;
-        private const int CATEGORYLENGTH = 2;
-        private const int SETSTART = 3;
+        internal const int FLAGS = 0;
+        internal const int SETLENGTH = 1;
+        internal const int CATEGORYLENGTH = 2;
+        internal const int SETSTART = 3;
 
         private const string NullCharString = "\0";
         private const char NullChar = '\0';
@@ -72,7 +72,6 @@ internal sealed class RegexCharClass
         internal const string NotECMADigitClass = "\x01\x02\x00" + ECMADigitSet;
 
         internal const string AnyClass = "\x00\x01\x00\x00";
-        internal const string EmptyClass = "\x00\x00\x00";
 
         // UnicodeCategory is zero based, so we add one to each value and subtract it off later
         private const int DefinedCategoriesCapacity = 38;
@@ -756,12 +755,12 @@ public static bool IsSingletonInverse(string set)
                 return false;
         }
 
-        private static bool IsSubtraction(string charClass)
+        internal static bool IsSubtraction(string charClass)
         {
             return (charClass.Length > SETSTART + charClass[SETLENGTH] + charClass[CATEGORYLENGTH]);
         }
 
-        private static bool IsNegated(string set)
+        internal static bool IsNegated(string set)
         {
             return (set != null && set[FLAGS] == 1);
         }
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs
index 4e8434fda96fd0..b00eb41f0978e8 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs
@@ -10,6 +10,7 @@
 using System.Globalization;
 using System.Reflection;
 using System.Reflection.Emit;
+using System.Runtime.InteropServices;
 
 namespace System.Text.RegularExpressions
 {
@@ -48,9 +49,11 @@ internal abstract class RegexCompiler
         private static readonly MethodInfo s_isECMABoundaryM = RegexRunnerMethod("IsECMABoundary");
         private static readonly MethodInfo s_chartolowerM = typeof(char).GetMethod("ToLower", new Type[] { typeof(char), typeof(CultureInfo) })!;
         private static readonly MethodInfo s_chartolowerinvariantM = typeof(char).GetMethod("ToLowerInvariant", new Type[] { typeof(char) })!;
+        private static readonly MethodInfo s_charIsDigitM = typeof(char).GetMethod("IsDigit", new Type[] { typeof(char) })!;
+        private static readonly MethodInfo s_charIsWhiteSpaceM = typeof(char).GetMethod("IsWhiteSpace", new Type[] { typeof(char) })!;
         private static readonly MethodInfo s_getcharM = typeof(string).GetMethod("get_Chars", new Type[] { typeof(int) })!;
         private static readonly MethodInfo s_crawlposM = RegexRunnerMethod("Crawlpos");
-        private static readonly MethodInfo s_charInSetM = RegexRunnerMethod("CharInClass");
+        private static readonly MethodInfo s_charInClassM = RegexRunnerMethod("CharInClass");
         private static readonly MethodInfo s_getCurrentCulture = typeof(CultureInfo).GetMethod("get_CurrentCulture")!;
         private static readonly MethodInfo s_checkTimeoutM = RegexRunnerMethod("CheckTimeout");
 #if DEBUG
@@ -370,6 +373,22 @@ private void Ceq()
             _ilg!.Emit(OpCodes.Ceq);
         }
 
+        /*
+         * A macro for _ilg.Emit(OpCodes.Clt_Un)
+         */
+        private void CgtUn()
+        {
+            _ilg!.Emit(OpCodes.Cgt_Un);
+        }
+
+        /*
+         * A macro for _ilg.Emit(OpCodes.Clt_Un)
+         */
+        private void CltUn()
+        {
+            _ilg!.Emit(OpCodes.Clt_Un);
+        }
+
         /*
          * A macro for _ilg.Emit(OpCodes.Pop)
          */
@@ -417,13 +436,35 @@ private void Sub(bool negate)
         }
 
         /*
-         * A macro for _ilg.Emit(OpCodes.Ldloc);
+         * A macro for _ilg.Emit(OpCodes.Div)
          */
-        private void Ldloc(LocalBuilder lt)
+        private void Div()
         {
-            _ilg!.Emit(OpCodes.Ldloc_S, lt);
+            _ilg!.Emit(OpCodes.Div);
+        }
+
+        /*
+         * A macro for _ilg.Emit(OpCodes.And)
+         */
+        private void And()
+        {
+            _ilg!.Emit(OpCodes.And);
         }
 
+        /*
+         * A macro for _ilg.Emit(OpCodes.Shl)
+         */
+        private void Shl()
+        {
+            _ilg!.Emit(OpCodes.Shl);
+        }
+
+        /// <summary>A macro for _ilg.Emit(OpCodes.Shr).</summary>
+        private void Shr() => _ilg!.Emit(OpCodes.Shr);
+
+        /// <summary>A macro for _ilg.Emit(OpCodes.Ldloc_S).</summary>
+        private void Ldloc(LocalBuilder lt) => _ilg!.Emit(OpCodes.Ldloc_S, lt);
+
         /*
          * A macro for _ilg.Emit(OpCodes.Stloc);
          */
@@ -1430,6 +1471,7 @@ protected void GenerateFindFirstChar()
             }
             else
             {
+                LocalBuilder charInClassV = _tempV;
                 LocalBuilder cV = _temp2V;
                 Label l1 = DefineLabel();
                 Label l2 = DefineLabel();
@@ -1474,8 +1516,7 @@ protected void GenerateFindFirstChar()
 
                 if (!RegexCharClass.IsSingleton(_fcPrefix.GetValueOrDefault().Prefix))
                 {
-                    Ldstr(_fcPrefix.GetValueOrDefault().Prefix);
-                    Call(s_charInSetM);
+                    EmitCallCharInClass(_fcPrefix.GetValueOrDefault().Prefix, charInClassV);
 
                     BrtrueFar(l2);
                 }
@@ -1718,6 +1759,7 @@ private void GenerateOneCode()
                 Message(sb.ToString());
             }
 #endif
+            LocalBuilder charInClassV;
 
             // Before executing any RegEx code in the unrolled loop,
             // we try checking for the match timeout:
@@ -2520,6 +2562,9 @@ private void GenerateOneCode()
 
                     //: if (Rightchars() < 1 || Rightcharnext() != (char)Operand(0))
                     //:    break Backward;
+
+                    charInClassV = _tempV!;
+
                     Ldloc(_textposV!);
 
                     if (!IsRtl())
@@ -2540,9 +2585,7 @@ private void GenerateOneCode()
 
                     if (Code() == RegexCode.Set)
                     {
-
-                        Ldstr(_strings![Operand(0)]);
-                        Call(s_charInSetM);
+                        EmitCallCharInClass(_strings![Operand(0)], charInClassV);
 
                         BrfalseFar(_backtrack);
                     }
@@ -2774,6 +2817,7 @@ private void GenerateOneCode()
                     //:         break Backward;
                     {
                         LocalBuilder lenV = _tempV!;
+                        charInClassV = _temp2V!;
                         Label l1 = DefineLabel();
 
                         int c = Operand(1);
@@ -2833,8 +2877,7 @@ private void GenerateOneCode()
                             {
                                 EmitTimeoutCheck();
                             }
-                            Ldstr(_strings![Operand(0)]);
-                            Call(s_charInSetM);
+                            EmitCallCharInClass(_strings![Operand(0)], charInClassV);
 
                             BrfalseFar(_backtrack);
                         }
@@ -2887,6 +2930,7 @@ private void GenerateOneCode()
                     {
                         LocalBuilder cV = _tempV!;
                         LocalBuilder lenV = _temp2V!;
+                        charInClassV = _temp3V!;
                         Label l1 = DefineLabel();
                         Label l2 = DefineLabel();
 
@@ -2946,8 +2990,7 @@ private void GenerateOneCode()
                             {
                                 EmitTimeoutCheck();
                             }
-                            Ldstr(_strings![Operand(0)]);
-                            Call(s_charInSetM);
+                            EmitCallCharInClass(_strings![Operand(0)], charInClassV);
 
                             BrtrueFar(l1);
                         }
@@ -3109,6 +3152,8 @@ private void GenerateOneCode()
                     //: if (i > 0)
                     //:     Track(i - 1, pos + 1);
 
+                    charInClassV = _tempV!;
+
                     PopTrack();
                     Stloc(_textposV!);
                     PopTrack();
@@ -3124,8 +3169,7 @@ private void GenerateOneCode()
 
                     if (Code() == RegexCode.Setlazy)
                     {
-                        Ldstr(_strings![Operand(0)]);
-                        Call(s_charInSetM);
+                        EmitCallCharInClass(_strings![Operand(0)], charInClassV);
 
                         BrfalseFar(_backtrack);
                     }
@@ -3156,6 +3200,169 @@ private void GenerateOneCode()
             }
         }
 
+        private void EmitCallCharInClass(string charClass, LocalBuilder tempLocal)
+        {
+            // We need to perform the equivalent of calling RegexRunner.CharInClass(ch, charClass),
+            // but that call is relatively expensive.  Before we fall back to it, we try to optimize
+            // some common cases for which we can do much better, such as known character classes
+            // for which we can call a dedicated method, or a fast-path for ASCII using a lookup table.
+
+            // First, see if the char class is a built-in one for which there's a better function
+            // we can just call directly.
+            switch (charClass)
+            {
+                case RegexCharClass.AnyClass:
+                    // true
+                    Pop();
+                    Ldc(1);
+                    return;
+
+                case RegexCharClass.DigitClass:
+                    // char.IsDigit(ch)
+                    Call(s_charIsDigitM);
+                    return;
+
+                case RegexCharClass.NotDigitClass:
+                    // !char.IsDigit(ch)
+                    Call(s_charIsDigitM);
+                    Ldc(0);
+                    Ceq();
+                    return;
+
+                case RegexCharClass.SpaceClass:
+                    // char.IsWhiteSpace(ch)
+                    Call(s_charIsWhiteSpaceM);
+                    return;
+
+                case RegexCharClass.NotSpaceClass:
+                    // !char.IsWhiteSpace(ch)
+                    Call(s_charIsWhiteSpaceM);
+                    Ldc(0);
+                    Ceq();
+                    return;
+            }
+
+            // Next, handle simple sets of one range, e.g. [A-Z], [0-9], etc.  This includes some built-in classes, like ECMADigitClass.
+            if (charClass.Length == RegexCharClass.SETSTART + 2 && // one set of two values
+                charClass[RegexCharClass.SETLENGTH] == 2 && // validate we have the right number of ranges
+                charClass[RegexCharClass.CATEGORYLENGTH] == 0 && // must not have any categories
+                charClass[RegexCharClass.SETSTART] < charClass[RegexCharClass.SETSTART + 1]) // valid range
+            {
+                // (uint)ch - charClass[3] < charClass[4] - charClass[3]
+                Ldc(charClass[RegexCharClass.SETSTART]);
+                Sub();
+                Ldc(charClass[RegexCharClass.SETSTART + 1] - charClass[RegexCharClass.SETSTART]);
+                CltUn();
+
+                // Negate the answer if the negation flag was set
+                if (RegexCharClass.IsNegated(charClass))
+                {
+                    Ldc(0);
+                    Ceq();
+                }
+
+                return;
+            }
+
+            // Next, special-case ASCII inputs.  If the character class contains only ASCII inputs, then we
+            // can satisfy the entire operation via a small lookup table, e.g.
+            //     ch < 128 && lookup(ch)
+            // If the character class contains values outside of the ASCII range, we can still optimize for
+            // ASCII inputs, using the table for values < 128, and falling back to calling CharInClass
+            // for anything outside of the ASCII range, e.g.
+            //     if (ch < 128) lookup(ch)
+            //     else ...
+            // Either way, we need to generate the lookup table for the ASCII range.
+            // We use a const string instead of a byte[] / static data property because
+            // it lets IL emit handle all the gory details for us.  It also is ok from an
+            // endianness perspective because the compilation happens on the same machine
+            // that runs the compiled code.  If that were to ever change, this would need
+            // to be revisited. String length is 8 chars == 16 bytes == 128 bits.
+            string bitVectorString = string.Create(8, charClass, (dest, charClass) =>
+            {
+                for (int i = 0; i < 128; i++)
+                {
+                    if (RegexCharClass.CharInClass((char)i, charClass))
+                    {
+                        dest[i >> 4] |= (char)(1 << (i & 0xF));
+                    }
+                }
+            });
+
+            // In order to determine whether we need the non-ASCII fallback, we have a few options:
+            // 1. Interpret the char class.  This would require fully understanding all of the ins and outs of the design,
+            //    and is a lot of code (in the future it's possible the parser could pass this information along).
+            // 2. Employ a heuristic to approximate (1), allowing for false positives (saying we need the fallback when
+            //    we don't) but no false negatives (saying we don't need the fallback when we do).
+            // 3. Evaluate CharInClass on all ~65K inputs.  This is relatively expensive, impacting startup costs.
+            // We currently go with (2).  We may sometimes generate a fallback when we don't need one, but the cost of
+            // doing so once in a while is minimal.
+            bool asciiOnly =
+                charClass.Length > RegexCharClass.SETSTART &&
+                charClass[RegexCharClass.CATEGORYLENGTH] == 0 && // if there are any categories, assume there's unicode
+                charClass[RegexCharClass.SETLENGTH] % 2 == 0 && // range limits must come in pairs
+                !RegexCharClass.IsNegated(charClass) && // if there's negation, assume there's unicode
+                !RegexCharClass.IsSubtraction(charClass); // if it's subtraction, assume there's unicode
+            if (asciiOnly)
+            {
+                for (int i = RegexCharClass.SETSTART; i < charClass.Length; i++)
+                {
+                    if (charClass[i] >= 128) // validate all characters in the set are ASCII
+                    {
+                        asciiOnly = false;
+                        break;
+                    }
+                }
+            }
+
+            Label nonAsciiLabel = DefineLabel(); // jumped to when input is >= 128
+            Label doneLabel = DefineLabel(); // jumped to when answer has been computed
+
+            // Store the input character so we can read it multiple times.
+            Stloc(tempLocal);
+
+            // ch < 128
+            Ldloc(tempLocal);
+            Ldc(128);
+            Bge(nonAsciiLabel);
+
+            // (bitVectorString[ch >> 4] & (1 << (ch & 0xF))) != 0
+            Ldstr(bitVectorString);
+            Ldloc(tempLocal);
+            Ldc(4);
+            Shr();
+            Call(s_getcharM);
+            Ldc(1);
+            Ldloc(tempLocal);
+            Ldc(15);
+            And();
+            Ldc(31);
+            And();
+            Shl();
+            And();
+            Ldc(0);
+            CgtUn();
+            Br(doneLabel);
+
+            MarkLabel(nonAsciiLabel);
+            if (asciiOnly)
+            {
+                // The whole class was ASCII, so if the character is >= 128, it's not in the class:
+                // false
+                Ldc(0);
+            }
+            else
+            {
+                // The whole class wasn't ASCII, so if the character is >= 128, we need to fall back to calling:
+                // CharInClass(ch, charClass)
+                Ldloc(tempLocal);
+                Ldstr(charClass);
+                Call(s_charInClassM);
+            }
+
+            MarkLabel(doneLabel);
+        }
+
         private void EmitTimeoutCheck()
         {
             Debug.Assert(_hasTimeout && _loopV != null);

From 41a23700968ec9149367849c67cb6edb319f3e12 Mon Sep 17 00:00:00 2001
From: Stephen Toub <stoub@microsoft.com>
Date: Mon, 18 Nov 2019 14:08:42 -0500
Subject: [PATCH 10/21] Clean up RegexCompiler.cs

---
 .../Text/RegularExpressions/RegexCompiler.cs  | 1210 ++++++-----------
 .../RegularExpressions/RegexLWCGCompiler.cs   |    8 +-
 2 files changed, 427 insertions(+), 791 deletions(-)

diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs
index b00eb41f0978e8..51eeb86934a8ae 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs
@@ -2,10 +2,6 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-// The RegexCompiler class is internal to the Regex package.
-// It translates a block of RegexCode to MSIL, and creates a
-// subclass of the RegexRunner type.
-
 using System.Diagnostics;
 using System.Globalization;
 using System.Reflection;
@@ -14,19 +10,12 @@
 
 namespace System.Text.RegularExpressions
 {
-    /*
-     * RegexDynamicModule
-     *
-     * Because dynamic modules are expensive and not thread-safe, we create
-     * one dynamic module per-thread, and cache as much information about it
-     * as we can.
-     *
-     * While we're at it, we just create one RegexCompiler per thread
-     * as well, and have RegexCompiler inherit from RegexDynamicModule.
-     */
+    /// <summary>
+    /// RegexCompiler translates a block of RegexCode to MSIL, and creates a
+    /// subclass of the RegexRunner type.
+    /// </summary>
     internal abstract class RegexCompiler
     {
-        // fields that never change
         private static readonly FieldInfo s_textbegF = RegexRunnerField("runtextbeg");
         private static readonly FieldInfo s_textendF = RegexRunnerField("runtextend");
         private static readonly FieldInfo s_textstartF = RegexRunnerField("runtextstart");
@@ -78,68 +67,59 @@ internal abstract class RegexCompiler
         private LocalBuilder? _cultureV;      // current culture is cached in local variable to prevent many thread local storage accesses for CultureInfo.CurrentCulture
         private LocalBuilder? _loopV;         // counter for setrep and setloop
 
-        protected RegexCode? _code;              // the RegexCode object (used for debugging only)
-        protected int[]? _codes;             // the RegexCodes being translated
-        protected string[]? _strings;           // the stringtable associated with the RegexCodes
-        protected RegexPrefix? _fcPrefix;          // the possible first chars computed by RegexFCD
-        protected RegexBoyerMoore? _bmPrefix;          // a prefix as a boyer-moore machine
-        protected int _anchors;           // the set of anchors
-        protected bool _hasTimeout; // whether the regex has a non-infinite timeout
+        protected RegexCode? _code;           // the RegexCode object (used for debugging only)
+        protected int[]? _codes;              // the RegexCodes being translated
+        protected string[]? _strings;         // the stringtable associated with the RegexCodes
+        protected RegexPrefix? _fcPrefix;     // the possible first chars computed by RegexFCD
+        protected RegexBoyerMoore? _bmPrefix; // a prefix as a boyer-moore machine
+        protected int _anchors;               // the set of anchors
+        protected bool _hasTimeout;           // whether the regex has a non-infinite timeout
 
-        private Label[]? _labels;            // a label for every operation in _codes
-        private BacktrackNote[]? _notes;             // a list of the backtracking states to be generated
-        private int _notecount;         // true count of _notes (allocation grows exponentially)
-        protected int _trackcount;        // count of backtracking states (used to reduce allocations)
+        private Label[]? _labels;             // a label for every operation in _codes
+        private BacktrackNote[]? _notes;      // a list of the backtracking states to be generated
+        private int _notecount;               // true count of _notes (allocation grows exponentially)
+        protected int _trackcount;            // count of backtracking states (used to reduce allocations)
 
-        private Label _backtrack;         // label for backtracking
+        private Label _backtrack;             // label for backtracking
 
 
-        private int _regexopcode;       // the current opcode being processed
-        private int _codepos;           // the current code being translated
-        private int _backpos;           // the current backtrack-note being translated
+        private int _regexopcode;             // the current opcode being processed
+        private int _codepos;                 // the current code being translated
+        private int _backpos;                 // the current backtrack-note being translated
 
-        protected RegexOptions _options;           // options
+        protected RegexOptions _options;      // options
 
         // special code fragments
-        private int[]? _uniquenote;        // _notes indices for code that should be emitted <= once
-        private int[]? _goto;              // indices for forward-jumps-through-switch (for allocations)
+        private int[]? _uniquenote;           // _notes indices for code that should be emitted <= once
+        private int[]? _goto;                 // indices for forward-jumps-through-switch (for allocations)
 
         // indices for unique code fragments
-        private const int Stackpop = 0;    // pop one
-        private const int Stackpop2 = 1;    // pop two
-        private const int Capback = 3;    // uncapture
-        private const int Capback2 = 4;    // uncapture 2
-        private const int Branchmarkback2 = 5;    // back2 part of branchmark
-        private const int Lazybranchmarkback2 = 6;    // back2 part of lazybranchmark
-        private const int Branchcountback2 = 7;    // back2 part of branchcount
-        private const int Lazybranchcountback2 = 8;    // back2 part of lazybranchcount
-        private const int Forejumpback = 9;    // back part of forejump
+        private const int Stackpop = 0;       // pop one
+        private const int Stackpop2 = 1;      // pop two
+        private const int Capback = 3;        // uncapture
+        private const int Capback2 = 4;       // uncapture 2
+        private const int Branchmarkback2 = 5;      // back2 part of branchmark
+        private const int Lazybranchmarkback2 = 6;  // back2 part of lazybranchmark
+        private const int Branchcountback2 = 7;     // back2 part of branchcount
+        private const int Lazybranchcountback2 = 8; // back2 part of lazybranchcount
+        private const int Forejumpback = 9;         // back part of forejump
         private const int Uniquecount = 10;
         private const int LoopTimeoutCheckCount = 2048; // A conservative value to guarantee the correct timeout handling.
 
-        private static FieldInfo RegexRunnerField(string fieldname)
-        {
-            return typeof(RegexRunner).GetField(fieldname, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)!;
-        }
+        private static FieldInfo RegexRunnerField(string fieldname) => typeof(RegexRunner).GetField(fieldname, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)!;
 
-        private static MethodInfo RegexRunnerMethod(string methname)
-        {
-            return typeof(RegexRunner).GetMethod(methname, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)!;
-        }
+        private static MethodInfo RegexRunnerMethod(string methname) => typeof(RegexRunner).GetMethod(methname, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)!;
 
-        /*
-         * Entry point to dynamically compile a regular expression.  The expression is compiled to
-         * an in-memory assembly.
-         */
-        internal static RegexRunnerFactory Compile(RegexCode code, RegexOptions options, bool hasTimeout)
-        {
-            return new RegexLWCGCompiler().FactoryInstanceFromCode(code, options, hasTimeout);
-        }
+        /// <summary>
+        /// Entry point to dynamically compile a regular expression.  The expression is compiled to
+        /// an in-memory assembly.
+        /// </summary>
+        internal static RegexRunnerFactory Compile(RegexCode code, RegexOptions options, bool hasTimeout) => new RegexLWCGCompiler().FactoryInstanceFromCode(code, options, hasTimeout);
 
-        /*
-         * Keeps track of an operation that needs to be referenced in the backtrack-jump
-         * switch table, and that needs backtracking code to be emitted (if flags != 0)
-         */
+        /// <summary>
+        /// Keeps track of an operation that needs to be referenced in the backtrack-jump
+        /// switch table, and that needs backtracking code to be emitted (if flags != 0)
+        /// </summary>
         private sealed class BacktrackNote
         {
             internal int _codepos;
@@ -154,17 +134,19 @@ public BacktrackNote(int flags, Label label, int codepos)
             }
         }
 
-        /*
-         * Adds a backtrack note to the list of them, and returns the index of the new
-         * note (which is also the index for the jump used by the switch table)
-         */
+        /// <summary>
+        /// Adds a backtrack note to the list of them, and returns the index of the new
+        /// note (which is also the index for the jump used by the switch table)
+        /// </summary>
         private int AddBacktrackNote(int flags, Label l, int codepos)
         {
             if (_notes == null || _notecount >= _notes.Length)
             {
-                BacktrackNote[] newnotes = new BacktrackNote[_notes == null ? 16 : _notes.Length * 2];
+                var newnotes = new BacktrackNote[_notes == null ? 16 : _notes.Length * 2];
                 if (_notes != null)
-                    System.Array.Copy(_notes, newnotes, _notecount);
+                {
+                    Array.Copy(_notes, newnotes, _notecount);
+                }
                 _notes = newnotes;
             }
 
@@ -173,133 +155,84 @@ private int AddBacktrackNote(int flags, Label l, int codepos)
             return _notecount++;
         }
 
-        /*
-         * Adds a backtrack note for the current operation; creates a new label for
-         * where the code will be, and returns the switch index.
-         */
-        private int AddTrack()
-        {
-            return AddTrack(RegexCode.Back);
-        }
+        /// <summary>
+        /// Adds a backtrack note for the current operation; creates a new label for
+        /// where the code will be, and returns the switch index.
+        /// </summary>
+        private int AddTrack() => AddTrack(RegexCode.Back);
 
-        /*
-         * Adds a backtrack note for the current operation; creates a new label for
-         * where the code will be, and returns the switch index.
-         */
-        private int AddTrack(int flags)
-        {
-            return AddBacktrackNote(flags, DefineLabel(), _codepos);
-        }
+        /// <summary>
+        /// Adds a backtrack note for the current operation; creates a new label for
+        /// where the code will be, and returns the switch index.
+        /// </summary>
+        private int AddTrack(int flags) => AddBacktrackNote(flags, DefineLabel(), _codepos);
 
-        /*
-         * Adds a switchtable entry for the specified position (for the forward
-         * logic; does not cause backtracking logic to be generated)
-         */
+        /// <summary>
+        /// Adds a switchtable entry for the specified position (for the forward
+        /// logic; does not cause backtracking logic to be generated)
+        /// </summary>
         private int AddGoto(int destpos)
         {
             if (_goto![destpos] == -1)
+            {
                 _goto[destpos] = AddBacktrackNote(0, _labels![destpos], destpos);
+            }
 
             return _goto[destpos];
         }
 
-        /*
-         * Adds a note for backtracking code that only needs to be generated once;
-         * if it's already marked to be generated, returns the switch index
-         * for the unique piece of code.
-         */
-        private int AddUniqueTrack(int i)
-        {
-            return AddUniqueTrack(i, RegexCode.Back);
-        }
+        /// <summary>
+        /// Adds a note for backtracking code that only needs to be generated once;
+        /// if it's already marked to be generated, returns the switch index
+        /// for the unique piece of code.
+        /// </summary>
+        private int AddUniqueTrack(int i) => AddUniqueTrack(i, RegexCode.Back);
 
-        /*
-         * Adds a note for backtracking code that only needs to be generated once;
-         * if it's already marked to be generated, returns the switch index
-         * for the unique piece of code.
-         */
+        /// <summary>
+        /// Adds a note for backtracking code that only needs to be generated once;
+        /// if it's already marked to be generated, returns the switch index
+        /// for the unique piece of code.
+        /// </summary>
         private int AddUniqueTrack(int i, int flags)
         {
             if (_uniquenote![i] == -1)
+            {
                 _uniquenote[i] = AddTrack(flags);
+            }
 
             return _uniquenote[i];
         }
 
-        /*
-         * A macro for _ilg.DefineLabel
-         */
-        private Label DefineLabel()
-        {
-            return _ilg!.DefineLabel();
-        }
+        /// <summary>A macro for _ilg.DefineLabel</summary>
+        private Label DefineLabel() => _ilg!.DefineLabel();
 
-        /*
-         * A macro for _ilg.MarkLabel
-         */
-        private void MarkLabel(Label l)
-        {
-            _ilg!.MarkLabel(l);
-        }
+        /// <summary>A macro for _ilg.MarkLabel</summary>
+        private void MarkLabel(Label l) => _ilg!.MarkLabel(l);
 
-        /*
-         * Returns the ith operand of the current operation
-         */
-        private int Operand(int i)
-        {
-            return _codes![_codepos + i + 1];
-        }
+        /// <summary>Returns the ith operand of the current operation.</summary>
+        private int Operand(int i) => _codes![_codepos + i + 1];
 
-        /*
-         * True if the current operation is marked for the leftward direction
-         */
-        private bool IsRtl()
-        {
-            return (_regexopcode & RegexCode.Rtl) != 0;
-        }
+        /// <summary>True if the current operation is marked for the leftward direction.</summary>
+        private bool IsRtl() => (_regexopcode & RegexCode.Rtl) != 0;
 
-        /*
-         * True if the current operation is marked for case insensitive operation
-         */
-        private bool IsCi()
-        {
-            return (_regexopcode & RegexCode.Ci) != 0;
-        }
+        /// <summary>True if the current operation is marked for case insensitive operation.</summary>
+        private bool IsCi() => (_regexopcode & RegexCode.Ci) != 0;
 
 #if DEBUG
-        /*
-         * True if we need to do the backtrack logic for the current operation
-         */
-        private bool IsBack()
-        {
-            return (_regexopcode & RegexCode.Back) != 0;
-        }
+        /// <summary>True if we need to do the backtrack logic for the current operation.</summary>
+        private bool IsBack() => (_regexopcode & RegexCode.Back) != 0;
 
-        /*
-         * True if we need to do the second-backtrack logic for the current operation
-         */
-        private bool IsBack2()
-        {
-            return (_regexopcode & RegexCode.Back2) != 0;
-        }
+        /// <summary>True if we need to do the second-backtrack logic for the current operation.</summary>
+        private bool IsBack2() => (_regexopcode & RegexCode.Back2) != 0;
 #endif
 
-        /*
-         * Returns the raw regex opcode (masking out Back and Rtl)
-         */
-        private int Code()
-        {
-            return _regexopcode & RegexCode.Mask;
-        }
+        /// <summary>Returns the raw regex opcode (masking out Back and Rtl).</summary>
+        private int Code() => _regexopcode & RegexCode.Mask;
 
-        private void Ldstr(string str)
-        {
-            _ilg!.Emit(OpCodes.Ldstr, str);
-        }
+        /// <summary>A macro for _ilg.Emit(Opcodes.Ldstr, str)</summary>
+        private void Ldstr(string str) => _ilg!.Emit(OpCodes.Ldstr, str);
 
-        /*
-         * A macro for the various forms of Ldc
-         */
+        /// <summary>A macro for the various forms of Ldc.</summary>
         private void Ldc(int i)
         {
             Debug.Assert(_ilg != null);
@@ -328,6 +261,7 @@ private void Ldc(int i)
             }
         }
 
+        /// <summary>A macro for the various forms of LdcI8.</summary>
         private void LdcI8(long i)
         {
             if (i <= int.MaxValue && i >= int.MinValue)
@@ -341,123 +275,47 @@ private void LdcI8(long i)
             }
         }
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Dup)
-         */
-        private void Dup()
-        {
-            _ilg!.Emit(OpCodes.Dup);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Dup).</summary>
+        private void Dup() => _ilg!.Emit(OpCodes.Dup);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Ret)
-         */
-        private void Ret()
-        {
-            _ilg!.Emit(OpCodes.Ret);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Ret).</summary>
+        private void Ret() => _ilg!.Emit(OpCodes.Ret);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Rem)
-         */
-        private void Rem()
-        {
-            _ilg!.Emit(OpCodes.Rem);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Rem).</summary>
+        private void Rem() => _ilg!.Emit(OpCodes.Rem);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Ceq)
-         */
-        private void Ceq()
-        {
-            _ilg!.Emit(OpCodes.Ceq);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Ceq).</summary>
+        private void Ceq() => _ilg!.Emit(OpCodes.Ceq);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Clt_Un)
-         */
-        private void CgtUn()
-        {
-            _ilg!.Emit(OpCodes.Cgt_Un);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Cgt_Un).</summary>
+        private void CgtUn() => _ilg!.Emit(OpCodes.Cgt_Un);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Clt_Un)
-         */
-        private void CltUn()
-        {
-            _ilg!.Emit(OpCodes.Clt_Un);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Clt_Un).</summary>
+        private void CltUn() => _ilg!.Emit(OpCodes.Clt_Un);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Pop)
-         */
-        private void Pop()
-        {
-            _ilg!.Emit(OpCodes.Pop);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Pop).</summary>
+        private void Pop() => _ilg!.Emit(OpCodes.Pop);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Add)
-         */
-        private void Add()
-        {
-            _ilg!.Emit(OpCodes.Add);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Add).</summary>
+        private void Add() => _ilg!.Emit(OpCodes.Add);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Add); a true flag can turn it into a Sub
-         */
-        private void Add(bool negate)
-        {
-            if (negate)
-                _ilg!.Emit(OpCodes.Sub);
-            else
-                _ilg!.Emit(OpCodes.Add);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Add); a true flag can turn it into a Sub.</summary>
+        private void Add(bool negate) => _ilg!.Emit(negate ? OpCodes.Sub : OpCodes.Add);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Sub)
-         */
-        private void Sub()
-        {
-            _ilg!.Emit(OpCodes.Sub);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Sub).</summary>
+        private void Sub() => _ilg!.Emit(OpCodes.Sub);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Sub); a true flag can turn it into a Add
-         */
-        private void Sub(bool negate)
-        {
-            if (negate)
-                _ilg!.Emit(OpCodes.Add);
-            else
-                _ilg!.Emit(OpCodes.Sub);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Sub) or _ilg.Emit(OpCodes.Add).</summary>
+        private void Sub(bool negate) => _ilg!.Emit(negate ? OpCodes.Add : OpCodes.Sub);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Div)
-         */
-        private void Div()
-        {
-            _ilg!.Emit(OpCodes.Div);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Div).</summary>
+        private void Div() => _ilg!.Emit(OpCodes.Div);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.And)
-         */
-        private void And()
-        {
-            _ilg!.Emit(OpCodes.And);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.And).</summary>
+        private void And() => _ilg!.Emit(OpCodes.And);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Shl)
-         */
-        private void Shl()
-        {
-            _ilg!.Emit(OpCodes.Shl);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Shl).</summary>
+        private void Shl() => _ilg!.Emit(OpCodes.Shl);
 
         /// <summary>A macro for _ilg.Emit(OpCodes.Shr).</summary>
         private void Shr() => _ilg!.Emit(OpCodes.Shr);
@@ -465,43 +323,27 @@ private void Shl()
         /// <summary>A macro for _ilg.Emit(OpCodes.Ldloc_S).</summary>
         private void Ldloc(LocalBuilder lt) => _ilg!.Emit(OpCodes.Ldloc_S, lt);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Stloc);
-         */
-        private void Stloc(LocalBuilder lt)
-        {
-            _ilg!.Emit(OpCodes.Stloc_S, lt);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Stloc).</summary>
+        private void Stloc(LocalBuilder lt) => _ilg!.Emit(OpCodes.Stloc_S, lt);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Ldarg_0);
-         */
-        private void Ldthis()
-        {
-            _ilg!.Emit(OpCodes.Ldarg_0);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Ldarg_0).</summary>
+        private void Ldthis() => _ilg!.Emit(OpCodes.Ldarg_0);
 
-        /*
-         * A macro for Ldthis(); Ldfld();
-         */
+        /// <summary>A macro for Ldthis(); Ldfld();</summary>
         private void Ldthisfld(FieldInfo ft)
         {
             Ldthis();
             _ilg!.Emit(OpCodes.Ldfld, ft);
         }
 
-        /*
-         * A macro for Ldthis(); Ldfld(); Stloc();
-         */
+        /// <summary>A macro for Ldthis(); Ldfld(); Stloc();</summary>
         private void Mvfldloc(FieldInfo ft, LocalBuilder lt)
         {
             Ldthisfld(ft);
             Stloc(lt);
         }
 
-        /*
-         * A macro for Ldthis(); Ldthisfld(); Stloc();
-         */
+        /// <summary>A macro for Ldthis(); Ldloc(); Stfld();</summary>
         private void Mvlocfld(LocalBuilder lt, FieldInfo ft)
         {
             Ldthis();
@@ -509,193 +351,76 @@ private void Mvlocfld(LocalBuilder lt, FieldInfo ft)
             Stfld(ft);
         }
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Stfld);
-         */
-        private void Stfld(FieldInfo ft)
-        {
-            _ilg!.Emit(OpCodes.Stfld, ft);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Stfld).</summary>
+        private void Stfld(FieldInfo ft) => _ilg!.Emit(OpCodes.Stfld, ft);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Callvirt);
-         */
-        private void Callvirt(MethodInfo mt)
-        {
-            _ilg!.Emit(OpCodes.Callvirt, mt);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Callvirt, mt).</summary>
+        private void Callvirt(MethodInfo mt) => _ilg!.Emit(OpCodes.Callvirt, mt);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Call);
-         */
-        private void Call(MethodInfo mt)
-        {
-            _ilg!.Emit(OpCodes.Call, mt);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Call, mt).</summary>
+        private void Call(MethodInfo mt) => _ilg!.Emit(OpCodes.Call, mt);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Newobj);
-         */
-        private void Newobj(ConstructorInfo ct)
-        {
-            _ilg!.Emit(OpCodes.Newobj, ct);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Newobj, ct).</summary>
+        private void Newobj(ConstructorInfo ct) => _ilg!.Emit(OpCodes.Newobj, ct);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Brfalse) (long form)
-         */
-        private void BrfalseFar(Label l)
-        {
-            _ilg!.Emit(OpCodes.Brfalse, l);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Brfalse) (long form).</summary>
+        private void BrfalseFar(Label l) => _ilg!.Emit(OpCodes.Brfalse, l);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Brtrue) (long form)
-         */
-        private void BrtrueFar(Label l)
-        {
-            _ilg!.Emit(OpCodes.Brtrue, l);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Brtrue) (long form).</summary>
+        private void BrtrueFar(Label l) => _ilg!.Emit(OpCodes.Brtrue, l);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Br) (long form)
-         */
-        private void BrFar(Label l)
-        {
-            _ilg!.Emit(OpCodes.Br, l);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Br) (long form).</summary>
+        private void BrFar(Label l) => _ilg!.Emit(OpCodes.Br, l);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Ble) (long form)
-         */
-        private void BleFar(Label l)
-        {
-            _ilg!.Emit(OpCodes.Ble, l);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Ble) (long form).</summary>
+        private void BleFar(Label l) => _ilg!.Emit(OpCodes.Ble, l);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Blt) (long form)
-         */
-        private void BltFar(Label l)
-        {
-            _ilg!.Emit(OpCodes.Blt, l);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Blt) (long form).</summary>
+        private void BltFar(Label l) => _ilg!.Emit(OpCodes.Blt, l);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Bge) (long form)
-         */
-        private void BgeFar(Label l)
-        {
-            _ilg!.Emit(OpCodes.Bge, l);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Bge) (long form).</summary>
+        private void BgeFar(Label l) => _ilg!.Emit(OpCodes.Bge, l);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Bgt) (long form)
-         */
-        private void BgtFar(Label l)
-        {
-            _ilg!.Emit(OpCodes.Bgt, l);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Bgt) (long form).</summary>
+        private void BgtFar(Label l) => _ilg!.Emit(OpCodes.Bgt, l);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Bne) (long form)
-         */
-        private void BneFar(Label l)
-        {
-            _ilg!.Emit(OpCodes.Bne_Un, l);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Bne) (long form).</summary>
+        private void BneFar(Label l) => _ilg!.Emit(OpCodes.Bne_Un, l);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Beq) (long form)
-         */
-        private void BeqFar(Label l)
-        {
-            _ilg!.Emit(OpCodes.Beq, l);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Beq) (long form).</summary>
+        private void BeqFar(Label l) => _ilg!.Emit(OpCodes.Beq, l);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Brfalse_S) (short jump)
-         */
-        private void Brfalse(Label l)
-        {
-            _ilg!.Emit(OpCodes.Brfalse_S, l);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Brfalse_S) (short jump).</summary>
+        private void Brfalse(Label l) => _ilg!.Emit(OpCodes.Brfalse_S, l);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Br_S) (short jump)
-         */
-        private void Br(Label l)
-        {
-            _ilg!.Emit(OpCodes.Br_S, l);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Br_S) (short jump).</summary>
+        private void Br(Label l) => _ilg!.Emit(OpCodes.Br_S, l);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Ble_S) (short jump)
-         */
-        private void Ble(Label l)
-        {
-            _ilg!.Emit(OpCodes.Ble_S, l);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Ble_S) (short jump).</summary>
+        private void Ble(Label l) => _ilg!.Emit(OpCodes.Ble_S, l);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Blt_S) (short jump)
-         */
-        private void Blt(Label l)
-        {
-            _ilg!.Emit(OpCodes.Blt_S, l);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Blt_S) (short jump).</summary>
+        private void Blt(Label l) => _ilg!.Emit(OpCodes.Blt_S, l);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Bge_S) (short jump)
-         */
-        private void Bge(Label l)
-        {
-            _ilg!.Emit(OpCodes.Bge_S, l);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Bge_S) (short jump).</summary>
+        private void Bge(Label l) => _ilg!.Emit(OpCodes.Bge_S, l);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Bgt_S) (short jump)
-         */
-        private void Bgt(Label l)
-        {
-            _ilg!.Emit(OpCodes.Bgt_S, l);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Bgt_S) (short jump).</summary>
+        private void Bgt(Label l) => _ilg!.Emit(OpCodes.Bgt_S, l);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Bleun_S) (short jump)
-         */
-        private void Bgtun(Label l)
-        {
-            _ilg!.Emit(OpCodes.Bgt_Un_S, l);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Bleun_S) (short jump).</summary>
+        private void Bgtun(Label l) => _ilg!.Emit(OpCodes.Bgt_Un_S, l);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Bne_S) (short jump)
-         */
-        private void Bne(Label l)
-        {
-            _ilg!.Emit(OpCodes.Bne_Un_S, l);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Bne_S) (short jump).</summary>
+        private void Bne(Label l) => _ilg!.Emit(OpCodes.Bne_Un_S, l);
 
-        /*
-         * A macro for _ilg.Emit(OpCodes.Beq_S) (short jump)
-         */
-        private void Beq(Label l)
-        {
-            _ilg!.Emit(OpCodes.Beq_S, l);
-        }
+        /// <summary>A macro for _ilg.Emit(OpCodes.Beq_S) (short jump).</summary>
+        private void Beq(Label l) => _ilg!.Emit(OpCodes.Beq_S, l);
 
-        /*
-         * A macro for the Ldlen instruction
-         */
-        private void Ldlen()
-        {
-            _ilg!.Emit(OpCodes.Ldlen);
-        }
+        /// <summary>A macro for the Ldlen instruction).</summary>
+        private void Ldlen() => _ilg!.Emit(OpCodes.Ldlen);
 
-        /*
-         * Loads the char to the right of the current position
-         */
+        /// <summary>Loads the char to the right of the current position.</summary>
         private void Rightchar()
         {
             Ldloc(_textV!);
@@ -703,9 +428,7 @@ private void Rightchar()
             Callvirt(s_getcharM);
         }
 
-        /*
-         * Loads the char to the right of the current position and advances the current position
-         */
+        /// <summary>Loads the char to the right of the current position and advances the current position.</summary>
         private void Rightcharnext()
         {
             Ldloc(_textV!);
@@ -717,9 +440,7 @@ private void Rightcharnext()
             Callvirt(s_getcharM);
         }
 
-        /*
-         * Loads the char to the left of the current position
-         */
+        /// <summary>Loads the char to the left of the current position.</summary>
         private void Leftchar()
         {
             Ldloc(_textV!);
@@ -729,9 +450,7 @@ private void Leftchar()
             Callvirt(s_getcharM);
         }
 
-        /*
-         * Loads the char to the left of the current position and advances (leftward)
-         */
+        /// <summary>Loads the char to the left of the current position and advances (leftward).</summary>
         private void Leftcharnext()
         {
             Ldloc(_textV!);
@@ -743,9 +462,7 @@ private void Leftcharnext()
             Callvirt(s_getcharM);
         }
 
-        /*
-         * Creates a backtrack note and pushes the switch index it on the tracking stack
-         */
+        /// <summary>Creates a backtrack note and pushes the switch index it on the tracking stack.</summary>
         private void Track()
         {
             ReadyPushTrack();
@@ -753,12 +470,10 @@ private void Track()
             DoPush();
         }
 
-        /*
-         * Pushes the current switch index on the tracking stack so the backtracking
-         * logic will be repeated again next time we backtrack here.
-         *
-         * <
-         */
+        /// <summary>
+        /// Pushes the current switch index on the tracking stack so the backtracking
+        /// logic will be repeated again next time we backtrack here.
+        /// </summary>
         private void Trackagain()
         {
             ReadyPushTrack();
@@ -766,9 +481,7 @@ private void Trackagain()
             DoPush();
         }
 
-        /*
-         * Saves the value of a local variable on the tracking stack
-         */
+        /// <summary>Saves the value of a local variable on the tracking stack.</summary>
         private void PushTrack(LocalBuilder lt)
         {
             ReadyPushTrack();
@@ -776,10 +489,10 @@ private void PushTrack(LocalBuilder lt)
             DoPush();
         }
 
-        /*
-         * Creates a backtrack note for a piece of code that should only be generated once,
-         * and emits code that pushes the switch index on the backtracking stack.
-         */
+        /// <summary>
+        /// Creates a backtrack note for a piece of code that should only be generated once,
+        /// and emits code that pushes the switch index on the backtracking stack.
+        /// </summary>
         private void TrackUnique(int i)
         {
             ReadyPushTrack();
@@ -787,11 +500,11 @@ private void TrackUnique(int i)
             DoPush();
         }
 
-        /*
-         * Creates a second-backtrack note for a piece of code that should only be
-         * generated once, and emits code that pushes the switch index on the
-         * backtracking stack.
-         */
+        /// <summary>
+        /// Creates a second-backtrack note for a piece of code that should only be
+        /// generated once, and emits code that pushes the switch index on the
+        /// backtracking stack.
+        /// </summary>
         private void TrackUnique2(int i)
         {
             ReadyPushTrack();
@@ -799,9 +512,7 @@ private void TrackUnique2(int i)
             DoPush();
         }
 
-        /*
-         * Prologue to code that will push an element on the tracking stack
-         */
+        /// <summary>Prologue to code that will push an element on the tracking stack.</summary>
         private void ReadyPushTrack()
         {
             _ilg!.Emit(OpCodes.Ldloc_S, _trackV!);
@@ -812,9 +523,7 @@ private void ReadyPushTrack()
             _ilg.Emit(OpCodes.Stloc_S, _trackposV!);
         }
 
-        /*
-         * Pops an element off the tracking stack (leave it on the operand stack)
-         */
+        /// <summary>Pops an element off the tracking stack (leave it on the operand stack).</summary>
         private void PopTrack()
         {
             _ilg!.Emit(OpCodes.Ldloc_S, _trackV!);
@@ -826,9 +535,7 @@ private void PopTrack()
             _ilg.Emit(OpCodes.Ldelem_I4);
         }
 
-        /*
-         * Retrieves the top entry on the tracking stack without popping
-         */
+        /// <summary>Retrieves the top entry on the tracking stack without popping.</summary>
         private void TopTrack()
         {
             _ilg!.Emit(OpCodes.Ldloc_S, _trackV!);
@@ -836,9 +543,7 @@ private void TopTrack()
             _ilg.Emit(OpCodes.Ldelem_I4);
         }
 
-        /*
-         * Saves the value of a local variable on the grouping stack
-         */
+        /// <summary>Saves the value of a local variable on the grouping stack.</summary>
         private void PushStack(LocalBuilder lt)
         {
             ReadyPushStack();
@@ -846,9 +551,7 @@ private void PushStack(LocalBuilder lt)
             DoPush();
         }
 
-        /*
-         * Prologue to code that will replace the ith element on the grouping stack
-         */
+        /// <summary>Prologue to code that will replace the ith element on the grouping stack.</summary>
         internal void ReadyReplaceStack(int i)
         {
             _ilg!.Emit(OpCodes.Ldloc_S, _stackV!);
@@ -860,9 +563,7 @@ internal void ReadyReplaceStack(int i)
             }
         }
 
-        /*
-         * Prologue to code that will push an element on the grouping stack
-         */
+        /// <summary>Prologue to code that will push an element on the grouping stack.</summary>
         private void ReadyPushStack()
         {
             _ilg!.Emit(OpCodes.Ldloc_S, _stackV!);
@@ -873,9 +574,7 @@ private void ReadyPushStack()
             _ilg.Emit(OpCodes.Stloc_S, _stackposV!);
         }
 
-        /*
-         * Retrieves the top entry on the stack without popping
-         */
+        /// <summary>Retrieves the top entry on the stack without popping.</summary>
         private void TopStack()
         {
             _ilg!.Emit(OpCodes.Ldloc_S, _stackV!);
@@ -883,9 +582,7 @@ private void TopStack()
             _ilg.Emit(OpCodes.Ldelem_I4);
         }
 
-        /*
-         * Pops an element off the grouping stack (leave it on the operand stack)
-         */
+        /// <summary>Pops an element off the grouping stack (leave it on the operand stack).</summary>
         private void PopStack()
         {
             _ilg!.Emit(OpCodes.Ldloc_S, _stackV!);
@@ -897,17 +594,10 @@ private void PopStack()
             _ilg.Emit(OpCodes.Ldelem_I4);
         }
 
-        /*
-         * Pops 1 element off the grouping stack and discards it
-         */
-        private void PopDiscardStack()
-        {
-            PopDiscardStack(1);
-        }
+        /// <summary>Pops 1 element off the grouping stack and discards it.</summary>
+        private void PopDiscardStack() => PopDiscardStack(1);
 
-        /*
-         * Pops i elements off the grouping stack and discards them
-         */
+        /// <summary>Pops i elements off the grouping stack and discards them.</summary>
         private void PopDiscardStack(int i)
         {
             _ilg!.Emit(OpCodes.Ldloc_S, _stackposV!);
@@ -916,45 +606,31 @@ private void PopDiscardStack(int i)
             _ilg.Emit(OpCodes.Stloc_S, _stackposV!);
         }
 
-        /*
-         * Epilogue to code that will replace an element on a stack (use Ld* in between)
-         */
-        private void DoReplace()
-        {
-            _ilg!.Emit(OpCodes.Stelem_I4);
-        }
-
-        /*
-         * Epilogue to code that will push an element on a stack (use Ld* in between)
-         */
-        private void DoPush()
-        {
-            _ilg!.Emit(OpCodes.Stelem_I4);
-        }
-
-        /*
-         * Jump to the backtracking switch
-         */
-        private void Back()
-        {
-            _ilg!.Emit(OpCodes.Br, _backtrack);
-        }
-
-        /*
-         * Branch to the MSIL corresponding to the regex code at i
-         *
-         * A trick: since track and stack space is gobbled up unboundedly
-         * only as a result of branching backwards, this is where we check
-         * for sufficient space and trigger reallocations.
-         *
-         * If the "goto" is backwards, we generate code that checks
-         * available space against the amount of space that would be needed
-         * in the worst case by code that will only go forward; if there's
-         * not enough, we push the destination on the tracking stack, then
-         * we jump to the place where we invoke the allocator.
-         *
-         * Since forward gotos pose no threat, they just turn into a Br.
-         */
+        /// <summary>Epilogue to code that will replace an element on a stack (use Ld* in between).</summary>
+        private void DoReplace() => _ilg!.Emit(OpCodes.Stelem_I4);
+
+        /// <summary>Epilogue to code that will push an element on a stack (use Ld* in between).</summary>
+        private void DoPush() => _ilg!.Emit(OpCodes.Stelem_I4);
+
+        /// <summary>Jump to the backtracking switch.</summary>
+        private void Back() => _ilg!.Emit(OpCodes.Br, _backtrack);
+
+        /// <summary>
+        /// Branch to the MSIL corresponding to the regex code at i
+        /// </summary>
+        /// <remarks>
+        /// A trick: since track and stack space is gobbled up unboundedly
+        /// only as a result of branching backwards, this is where we check
+        /// for sufficient space and trigger reallocations.
+        ///
+        /// If the "goto" is backwards, we generate code that checks
+        /// available space against the amount of space that would be needed
+        /// in the worst case by code that will only go forward; if there's
+        /// not enough, we push the destination on the tracking stack, then
+        /// we jump to the place where we invoke the allocator.
+        ///
+        /// Since forward gotos pose no threat, they just turn into a Br.
+        /// </remarks>
         private void Goto(int i)
         {
             if (i < _codepos)
@@ -980,31 +656,19 @@ private void Goto(int i)
             }
         }
 
-        /*
-         * Returns the position of the next operation in the regex code, taking
-         * into account the different numbers of arguments taken by operations
-         */
-        private int NextCodepos()
-        {
-            return _codepos + RegexCode.OpcodeSize(_codes![_codepos]);
-        }
+        /// <summary>
+        /// Returns the position of the next operation in the regex code, taking
+        /// into account the different numbers of arguments taken by operations
+        /// </summary>
+        private int NextCodepos() => _codepos + RegexCode.OpcodeSize(_codes![_codepos]);
 
-        /*
-         * The label for the next (forward) operation
-         */
-        private Label AdvanceLabel()
-        {
-            return _labels![NextCodepos()];
-        }
+        /// <summary>The label for the next (forward) operation.</summary>
+        private Label AdvanceLabel() => _labels![NextCodepos()];
 
-        /*
-         * Goto the next (forward) operation
-         */
-        private void Advance()
-        {
-            _ilg!.Emit(OpCodes.Br, AdvanceLabel());
-        }
+        /// <summary>Goto the next (forward) operation.</summary>
+        private void Advance() => _ilg!.Emit(OpCodes.Br, AdvanceLabel());
 
+        /// <summary>Sets the culture local to CultureInfo.CurrentCulture.</summary>
         private void InitLocalCultureInfo()
         {
             Debug.Assert(_cultureV != null);
@@ -1012,6 +676,7 @@ private void InitLocalCultureInfo()
             Stloc(_cultureV);
         }
 
+        /// <summary>Invokes either char.ToLower(..., _culture) or char.ToLowerInvariant(...).</summary>
         private void CallToLower()
         {
             if (_cultureV == null || _options.HasFlag(RegexOptions.CultureInvariant))
@@ -1025,21 +690,19 @@ private void CallToLower()
             }
         }
 
-        /*
-         * Generates the first section of the MSIL. This section contains all
-         * the forward logic, and corresponds directly to the regex codes.
-         *
-         * In the absence of backtracking, this is all we would need.
-         */
+        /// <summary>
+        /// Generates the first section of the MSIL. This section contains all
+        /// the forward logic, and corresponds directly to the regex codes.
+        /// In the absence of backtracking, this is all we would need.
+        /// </summary>
         private void GenerateForwardSection()
         {
-            int codepos;
-
             _labels = new Label[_codes!.Length];
             _goto = new int[_codes.Length];
 
             // initialize
 
+            int codepos;
             for (codepos = 0; codepos < _codes.Length; codepos += RegexCode.OpcodeSize(_codes[codepos]))
             {
                 _goto[codepos] = -1;
@@ -1047,8 +710,7 @@ private void GenerateForwardSection()
             }
 
             _uniquenote = new int[Uniquecount];
-            for (int i = 0; i < Uniquecount; i++)
-                _uniquenote[i] = -1;
+            Array.Fill(_uniquenote, -1);
 
             // emit variable initializers
 
@@ -1073,17 +735,14 @@ private void GenerateForwardSection()
             }
         }
 
-        /*
-         * Generates the middle section of the MSIL. This section contains the
-         * big switch jump that allows us to simulate a stack of addresses,
-         * and it also contains the calls that expand the tracking and the
-         * grouping stack when they get too full.
-         */
+        /// <summary>
+        /// Generates the middle section of the MSIL. This section contains the
+        /// big switch jump that allows us to simulate a stack of addresses,
+        /// and it also contains the calls that expand the tracking and the
+        /// grouping stack when they get too full.
+        /// </summary>
         private void GenerateMiddleSection()
         {
-            Label[] table;
-            int i;
-
             // Backtrack switch
             MarkLabel(_backtrack);
 
@@ -1097,25 +756,24 @@ private void GenerateMiddleSection()
             Mvfldloc(s_trackF, _trackV!);
             Mvfldloc(s_stackF, _stackV!);
 
-
             PopTrack();
 
-            table = new Label[_notecount];
-            for (i = 0; i < _notecount; i++)
+            var table = new Label[_notecount];
+            for (int i = 0; i < _notecount; i++)
+            {
                 table[i] = _notes![i]._label;
+            }
 
             _ilg!.Emit(OpCodes.Switch, table);
         }
 
-        /*
-         * Generates the last section of the MSIL. This section contains all of
-         * the backtracking logic.
-         */
+        /// <summary>
+        /// Generates the last section of the MSIL. This section contains all of
+        /// the backtracking logic.
+        /// </summary>
         private void GenerateBacktrackSection()
         {
-            int i;
-
-            for (i = 0; i < _notecount; i++)
+            for (int i = 0; i < _notecount; i++)
             {
                 BacktrackNote n = _notes![i];
                 if (n._flags != 0)
@@ -1129,13 +787,13 @@ private void GenerateBacktrackSection()
             }
         }
 
-        /*
-         * Generates FindFirstChar
-         */
         // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
         // !!!! This function must be kept synchronized with FindFirstChar in      !!!!
         // !!!! RegexInterpreter.cs                                                !!!!
         // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+        /// <summary>
+        /// Generates FindFirstChar.
+        /// </summary>
         protected void GenerateFindFirstChar()
         {
             _textposV = DeclareInt();
@@ -1154,11 +812,11 @@ protected void GenerateFindFirstChar()
                 }
             }
 
-            if (0 != (_anchors & (RegexFCD.Beginning | RegexFCD.Start | RegexFCD.EndZ | RegexFCD.End)))
+            if ((_anchors & (RegexFCD.Beginning | RegexFCD.Start | RegexFCD.EndZ | RegexFCD.End)) != 0)
             {
                 if (!_code!.RightToLeft)
                 {
-                    if (0 != (_anchors & RegexFCD.Beginning))
+                    if ((_anchors & RegexFCD.Beginning) != 0)
                     {
                         Label l1 = DefineLabel();
                         Ldthisfld(s_textposF);
@@ -1172,7 +830,7 @@ protected void GenerateFindFirstChar()
                         MarkLabel(l1);
                     }
 
-                    if (0 != (_anchors & RegexFCD.Start))
+                    if ((_anchors & RegexFCD.Start) != 0)
                     {
                         Label l1 = DefineLabel();
                         Ldthisfld(s_textposF);
@@ -1186,7 +844,7 @@ protected void GenerateFindFirstChar()
                         MarkLabel(l1);
                     }
 
-                    if (0 != (_anchors & RegexFCD.EndZ))
+                    if ((_anchors & RegexFCD.EndZ) != 0)
                     {
                         Label l1 = DefineLabel();
                         Ldthisfld(s_textposF);
@@ -1202,7 +860,7 @@ protected void GenerateFindFirstChar()
                         MarkLabel(l1);
                     }
 
-                    if (0 != (_anchors & RegexFCD.End))
+                    if ((_anchors & RegexFCD.End) != 0)
                     {
                         Label l1 = DefineLabel();
                         Ldthisfld(s_textposF);
@@ -1216,7 +874,7 @@ protected void GenerateFindFirstChar()
                 }
                 else
                 {
-                    if (0 != (_anchors & RegexFCD.End))
+                    if ((_anchors & RegexFCD.End) != 0)
                     {
                         Label l1 = DefineLabel();
                         Ldthisfld(s_textposF);
@@ -1230,7 +888,7 @@ protected void GenerateFindFirstChar()
                         MarkLabel(l1);
                     }
 
-                    if (0 != (_anchors & RegexFCD.EndZ))
+                    if ((_anchors & RegexFCD.EndZ) != 0)
                     {
                         Label l1 = DefineLabel();
                         Label l2 = DefineLabel();
@@ -1245,7 +903,7 @@ protected void GenerateFindFirstChar()
                         Ldthisfld(s_textF);
                         Ldthisfld(s_textposF);
                         Callvirt(s_getcharM);
-                        Ldc((int)'\n');
+                        Ldc('\n');
                         Beq(l2);
                         MarkLabel(l1);
                         Ldthis();
@@ -1256,7 +914,7 @@ protected void GenerateFindFirstChar()
                         MarkLabel(l2);
                     }
 
-                    if (0 != (_anchors & RegexFCD.Start))
+                    if ((_anchors & RegexFCD.Start) != 0)
                     {
                         Label l1 = DefineLabel();
                         Ldthisfld(s_textposF);
@@ -1270,7 +928,7 @@ protected void GenerateFindFirstChar()
                         MarkLabel(l1);
                     }
 
-                    if (0 != (_anchors & RegexFCD.Beginning))
+                    if ((_anchors & RegexFCD.Beginning) != 0)
                     {
                         Label l1 = DefineLabel();
                         Ldthisfld(s_textposF);
@@ -1283,17 +941,12 @@ protected void GenerateFindFirstChar()
                     }
                 }
 
-                // <
-
-
                 Ldc(1);
                 Ret();
             }
             else if (_bmPrefix != null && _bmPrefix.NegativeUnicode == null)
             {
                 // Compiled Boyer-Moore string matching
-                // <
-
 
                 LocalBuilder chV = _tempV;
                 LocalBuilder testV = _tempV;
@@ -1304,13 +957,8 @@ protected void GenerateFindFirstChar()
                 Label lStart = DefineLabel();
                 Label lPartialMatch = DefineLabel();
 
-
-                int chLast;
-                int i;
                 int beforefirst;
                 int last;
-                Label[] table;
-
                 if (!_code!.RightToLeft)
                 {
                     beforefirst = -1;
@@ -1322,13 +970,10 @@ protected void GenerateFindFirstChar()
                     last = 0;
                 }
 
-                chLast = _bmPrefix.Pattern[last];
+                int chLast = _bmPrefix.Pattern[last];
 
                 Mvfldloc(s_textF, _textV);
-                if (!_code.RightToLeft)
-                    Ldthisfld(s_textendF);
-                else
-                    Ldthisfld(s_textbegF);
+                Ldthisfld(_code.RightToLeft ? s_textbegF : s_textendF);
                 Stloc(limitV);
 
                 Ldthisfld(s_textposF);
@@ -1347,10 +992,7 @@ protected void GenerateFindFirstChar()
 
                 MarkLabel(lDefaultAdvance);
 
-                if (!_code.RightToLeft)
-                    Ldc(_bmPrefix.Pattern.Length);
-                else
-                    Ldc(-_bmPrefix.Pattern.Length);
+                Ldc(_code.RightToLeft ? -_bmPrefix.Pattern.Length : _bmPrefix.Pattern.Length);
 
                 MarkLabel(lAdvance);
 
@@ -1363,13 +1005,19 @@ protected void GenerateFindFirstChar()
                 Ldloc(_textposV);
                 Ldloc(limitV);
                 if (!_code.RightToLeft)
+                {
                     BgeFar(lFail);
+                }
                 else
+                {
                     BltFar(lFail);
+                }
 
                 Rightchar();
                 if (_bmPrefix.CaseInsensitive)
+                {
                     CallToLower();
+                }
 
                 Dup();
                 Stloc(chV);
@@ -1384,23 +1032,24 @@ protected void GenerateFindFirstChar()
                 Ldc(_bmPrefix.HighASCII - _bmPrefix.LowASCII);
                 Bgtun(lDefaultAdvance);
 
-                table = new Label[_bmPrefix.HighASCII - _bmPrefix.LowASCII + 1];
+                var table = new Label[_bmPrefix.HighASCII - _bmPrefix.LowASCII + 1];
 
-                for (i = _bmPrefix.LowASCII; i <= _bmPrefix.HighASCII; i++)
+                for (int i = _bmPrefix.LowASCII; i <= _bmPrefix.HighASCII; i++)
                 {
-                    if (_bmPrefix.NegativeASCII[i] == beforefirst)
-                        table[i - _bmPrefix.LowASCII] = lDefaultAdvance;
-                    else
-                        table[i - _bmPrefix.LowASCII] = DefineLabel();
+                    table[i - _bmPrefix.LowASCII] = (_bmPrefix.NegativeASCII[i] == beforefirst) ?
+                        lDefaultAdvance :
+                        DefineLabel();
                 }
 
                 Ldloc(chV);
                 _ilg!.Emit(OpCodes.Switch, table);
 
-                for (i = _bmPrefix.LowASCII; i <= _bmPrefix.HighASCII; i++)
+                for (int i = _bmPrefix.LowASCII; i <= _bmPrefix.HighASCII; i++)
                 {
                     if (_bmPrefix.NegativeASCII[i] == beforefirst)
+                    {
                         continue;
+                    }
 
                     MarkLabel(table[i - _bmPrefix.LowASCII]);
 
@@ -1413,15 +1062,12 @@ protected void GenerateFindFirstChar()
                 Ldloc(_textposV);
                 Stloc(testV);
 
-                for (i = _bmPrefix.Pattern.Length - 2; i >= 0; i--)
+                for (int i = _bmPrefix.Pattern.Length - 2; i >= 0; i--)
                 {
                     Label lNext = DefineLabel();
-                    int charindex;
-
-                    if (!_code.RightToLeft)
-                        charindex = i;
-                    else
-                        charindex = _bmPrefix.Pattern.Length - 1 - i;
+                    int charindex = _code.RightToLeft ?
+                        _bmPrefix.Pattern.Length - 1 - i :
+                        i;
 
                     Ldloc(_textV);
                     Ldloc(testV);
@@ -1431,7 +1077,9 @@ protected void GenerateFindFirstChar()
                     Stloc(testV);
                     Callvirt(s_getcharM);
                     if (_bmPrefix.CaseInsensitive)
+                    {
                         CallToLower();
+                    }
 
                     Ldc(_bmPrefix.Pattern[charindex]);
                     Beq(lNext);
@@ -1439,7 +1087,6 @@ protected void GenerateFindFirstChar()
                     BrFar(lAdvance);
 
                     MarkLabel(lNext);
-
                 }
 
                 Ldthis();
@@ -1456,10 +1103,7 @@ protected void GenerateFindFirstChar()
                 MarkLabel(lFail);
 
                 Ldthis();
-                if (!_code.RightToLeft)
-                    Ldthisfld(s_textendF);
-                else
-                    Ldthisfld(s_textbegF);
+                Ldthisfld(_code.RightToLeft ? s_textbegF : s_textendF);
                 Stfld(s_textposF);
                 Ldc(0);
                 Ret();
@@ -1507,17 +1151,22 @@ protected void GenerateFindFirstChar()
                 Stloc(cV);
 
                 if (_code.RightToLeft)
+                {
                     Leftcharnext();
+                }
                 else
+                {
                     Rightcharnext();
+                }
 
                 if (_fcPrefix.GetValueOrDefault().CaseInsensitive)
+                {
                     CallToLower();
+                }
 
                 if (!RegexCharClass.IsSingleton(_fcPrefix.GetValueOrDefault().Prefix))
                 {
                     EmitCallCharInClass(_fcPrefix.GetValueOrDefault().Prefix, charInClassV);
-
                     BrtrueFar(l2);
                 }
                 else
@@ -1531,52 +1180,19 @@ protected void GenerateFindFirstChar()
                 Ldloc(cV);
                 Ldc(0);
                 if (!RegexCharClass.IsSingleton(_fcPrefix.GetValueOrDefault().Prefix))
+                {
                     BgtFar(l1);
+                }
                 else
+                {
                     Bgt(l1);
+                }
 
                 Ldc(0);
                 BrFar(l3);
 
                 MarkLabel(l2);
 
-                /*          // CURRENTLY DISABLED
-                            // If for some reason we have a prefix we didn't use, use it now.
-
-                            if (_bmPrefix != null) {
-                                if (!_code._rightToLeft) {
-                                    Ldthisfld(_textendF);
-                                    Ldloc(_textposV);
-                                }
-                                else {
-                                    Ldloc(_textposV);
-                                    Ldthisfld(_textbegF);
-                                }
-                                Sub();
-                                Ldc(_bmPrefix._pattern.Length - 1);
-                                BltFar(l5);
-
-                                for (int i = 1; i < _bmPrefix._pattern.Length; i++) {
-                                    Ldloc(_textV);
-                                    Ldloc(_textposV);
-                                    if (!_code._rightToLeft) {
-                                        Ldc(i - 1);
-                                        Add();
-                                    }
-                                    else {
-                                        Ldc(i);
-                                        Sub();
-                                    }
-                                    Callvirt(_getcharM);
-                                    if (!_code._rightToLeft)
-                                        Ldc(_bmPrefix._pattern[i]);
-                                    else
-                                        Ldc(_bmPrefix._pattern[_bmPrefix._pattern.Length - 1 - i]);
-                                    BneFar(l5);
-                                }
-                            }
-                */
-
                 Ldloc(_textposV);
                 Ldc(1);
                 Sub(_code.RightToLeft);
@@ -1595,9 +1211,7 @@ protected void GenerateFindFirstChar()
 
         }
 
-        /*
-         * Generates a very simple method that sets the _trackcount field.
-         */
+        /// <summary>Generates a very simple method that sets the _trackcount field.</summary>
         protected void GenerateInitTrackCount()
         {
             Ldthis();
@@ -1606,41 +1220,19 @@ protected void GenerateInitTrackCount()
             Ret();
         }
 
-        /*
-         * Declares a local int
-         */
-        private LocalBuilder DeclareInt()
-        {
-            return _ilg!.DeclareLocal(typeof(int));
-        }
+        /// <summary>Declares a local int.</summary>
+        private LocalBuilder DeclareInt() => _ilg!.DeclareLocal(typeof(int));
 
-        /*
-         * Declares a local CultureInfo
-         */
-        private LocalBuilder? DeclareCultureInfo()
-        {
-            return _ilg!.DeclareLocal(typeof(CultureInfo)); // cache local variable to avoid unnecessary TLS
-        }
+        /// <summary>Declares a local CultureInfo.</summary>
+        private LocalBuilder? DeclareCultureInfo() => _ilg!.DeclareLocal(typeof(CultureInfo)); // cache local variable to avoid unnecessary TLS
 
-        /*
-         * Declares a local int array
-         */
-        private LocalBuilder DeclareIntArray()
-        {
-            return _ilg!.DeclareLocal(typeof(int[]));
-        }
+        /// <summary>Declares a local int[].</summary>
+        private LocalBuilder DeclareIntArray() => _ilg!.DeclareLocal(typeof(int[]));
 
-        /*
-         * Declares a local string
-         */
-        private LocalBuilder DeclareString()
-        {
-            return _ilg!.DeclareLocal(typeof(string));
-        }
+        /// <summary>Declares a local string.</summary>
+        private LocalBuilder DeclareString() => _ilg!.DeclareLocal(typeof(string));
 
-        /*
-         * Generates the code for "RegexRunner.Go"
-         */
+        /// <summary>Generates the code for "RegexRunner.Go".</summary>
         protected void GenerateGo()
         {
             // declare some locals
@@ -1708,14 +1300,10 @@ protected void GenerateGo()
         }
 
 #if DEBUG
-        /*
-         * Some simple debugging stuff
-         */
+        /// <summary>Debug.WriteLine</summary>
         private static readonly MethodInfo? s_debugWriteLine = typeof(Debug).GetMethod("WriteLine", new Type[] { typeof(string) });
 
-        /*
-         * Debug only: emit code to print out a message
-         */
+        /// <summary>Debug only: emit code to print out a message.</summary>
         private void Message(string str)
         {
             Ldstr(str);
@@ -1724,18 +1312,19 @@ private void Message(string str)
 
 #endif
 
-        /*
-         * The main translation function. It translates the logic for a single opcode at
-         * the current position. The structure of this function exactly mirrors
-         * the structure of the inner loop of RegexInterpreter.Go().
-         *
-         * The C# code from RegexInterpreter.Go() that corresponds to each case is
-         * included as a comment.
-         *
-         * Note that since we're generating code, we can collapse many cases that are
-         * dealt with one-at-a-time in RegexIntepreter. We can also unroll loops that
-         * iterate over constant strings or sets.
-         */
+        /// <summary>
+        /// The main translation function. It translates the logic for a single opcode at
+        /// the current position. The structure of this function exactly mirrors
+        /// the structure of the inner loop of RegexInterpreter.Go().
+        /// </summary>
+        /// <remarks>
+        /// The C# code from RegexInterpreter.Go() that corresponds to each case is
+        /// included as a comment.
+        ///
+        /// Note that since we're generating code, we can collapse many cases that are
+        /// dealt with one-at-a-time in RegexIntepreter. We can also unroll loops that
+        /// iterate over constant strings or sets.
+        /// </remarks>
         private void GenerateOneCode()
         {
 #if DEBUG
@@ -1746,16 +1335,28 @@ private void GenerateOneCode()
                 Mvlocfld(_stackposV!, s_stackposF);
                 Ldthis();
                 Callvirt(s_dumpstateM);
-                StringBuilder sb = new StringBuilder();
+
+                var sb = new StringBuilder();
                 if (_backpos > 0)
+                {
                     sb.AppendFormat("{0:D6} ", _backpos);
+                }
                 else
+                {
                     sb.Append("       ");
+                }
                 sb.Append(_code!.OpcodeDescription(_codepos));
+
                 if (IsBack())
+                {
                     sb.Append(" Back");
+                }
+
                 if (IsBack2())
+                {
                     sb.Append(" Back2");
+                }
+
                 Message(sb.ToString());
             }
 #endif
@@ -1908,11 +1509,7 @@ private void GenerateOneCode()
 
                     PushTrack(_tempV!);
 
-                    if (Operand(0) != -1 && Operand(1) != -1)
-                        TrackUnique(Capback2);
-                    else
-                        TrackUnique(Capback);
-
+                    TrackUnique(Operand(0) != -1 && Operand(1) != -1 ? Capback2 : Capback);
                     break;
 
 
@@ -2001,7 +1598,6 @@ private void GenerateOneCode()
                     Back();
                     break;
 
-
                 case RegexCode.Lazybranchmark:
                     //: StackPop();
                     //: int oldMarkPos = StackPeek();
@@ -2105,7 +1701,6 @@ private void GenerateOneCode()
                     TrackUnique(Stackpop2);
                     break;
 
-
                 case RegexCode.Nullcount | RegexCode.Back:
                 case RegexCode.Setcount | RegexCode.Back:
                     //: Stackframe(2);
@@ -2114,7 +1709,6 @@ private void GenerateOneCode()
                     Back();
                     break;
 
-
                 case RegexCode.Branchcount:
                     //: Stackframe(2);
                     //: int mark = Stacked(0);
@@ -2329,11 +1923,6 @@ private void GenerateOneCode()
 
                 case RegexCode.Lazybranchcount | RegexCode.Back2:
                     // <
-
-
-
-
-
                     ReadyReplaceStack(1);
                     PopTrack();
                     DoReplace();
@@ -2368,7 +1957,6 @@ private void GenerateOneCode()
                     Back();
                     break;
 
-
                 case RegexCode.Backjump:
                     //: Stackframe(2);
                     //: Trackto(Stacked(0));
@@ -2458,7 +2046,7 @@ private void GenerateOneCode()
                         Ldloc(_textbegV!);
                         Ble(l1);
                         Leftchar();
-                        Ldc((int)'\n');
+                        Ldc('\n');
                         BneFar(_backtrack);
                         break;
                     }
@@ -2472,7 +2060,7 @@ private void GenerateOneCode()
                         Ldloc(_textendV!);
                         Bge(l1);
                         Rightchar();
-                        Ldc((int)'\n');
+                        Ldc('\n');
                         BneFar(_backtrack);
                         break;
                     }
@@ -2487,9 +2075,13 @@ private void GenerateOneCode()
                     Ldloc(_textendV!);
                     Callvirt(s_isboundaryM);
                     if (Code() == RegexCode.Boundary)
+                    {
                         BrfalseFar(_backtrack);
+                    }
                     else
+                    {
                         BrtrueFar(_backtrack);
+                    }
                     break;
 
                 case RegexCode.ECMABoundary:
@@ -2502,9 +2094,13 @@ private void GenerateOneCode()
                     Ldloc(_textendV!);
                     Callvirt(s_isECMABoundaryM);
                     if (Code() == RegexCode.ECMABoundary)
+                    {
                         BrfalseFar(_backtrack);
+                    }
                     else
+                    {
                         BrtrueFar(_backtrack);
+                    }
                     break;
 
                 case RegexCode.Beginning:
@@ -2535,7 +2131,7 @@ private void GenerateOneCode()
                     Ldloc(_textendV!);
                     Bge(_labels![NextCodepos()]);
                     Rightchar();
-                    Ldc((int)'\n');
+                    Ldc('\n');
                     BneFar(_backtrack);
                     break;
 
@@ -2581,28 +2177,31 @@ private void GenerateOneCode()
                     }
 
                     if (IsCi())
+                    {
                         CallToLower();
+                    }
 
                     if (Code() == RegexCode.Set)
                     {
                         EmitCallCharInClass(_strings![Operand(0)], charInClassV);
-
                         BrfalseFar(_backtrack);
                     }
                     else
                     {
                         Ldc(Operand(0));
                         if (Code() == RegexCode.One)
+                        {
                             BneFar(_backtrack);
+                        }
                         else
+                        {
                             BeqFar(_backtrack);
+                        }
                     }
                     break;
 
                 case RegexCode.Multi:
                 case RegexCode.Multi | RegexCode.Ci:
-                    //
-                    // <
                     //: String Str = _strings[Operand(0)];
                     //: int i, c;
                     //: if (Rightchars() < (c = Str.Length))
@@ -2634,9 +2233,11 @@ private void GenerateOneCode()
                             }
                             Callvirt(s_getcharM);
                             if (IsCi())
+                            {
                                 CallToLower();
+                            }
 
-                            Ldc((int)str[i]);
+                            Ldc(str[i]);
                             BneFar(_backtrack);
                         }
 
@@ -2682,7 +2283,7 @@ private void GenerateOneCode()
                             {
                                 CallToLower();
                             }
-                            Ldc((int)str[i]);
+                            Ldc(str[i]);
                             BneFar(_backtrack);
                         }
 
@@ -2719,9 +2320,13 @@ private void GenerateOneCode()
                         Ldc(Operand(0));
                         Callvirt(s_ismatchedM);
                         if ((_options & RegexOptions.ECMAScript) != 0)
+                        {
                             Brfalse(AdvanceLabel());
+                        }
                         else
+                        {
                             BrfalseFar(_backtrack); // !IsMatched() -> back
+                        }
 
                         Ldthis();
                         Ldc(Operand(0));
@@ -2773,7 +2378,9 @@ private void GenerateOneCode()
                         Sub(IsRtl());
                         Callvirt(s_getcharM);
                         if (IsCi())
+                        {
                             CallToLower();
+                        }
 
                         Ldloc(_textV!);
                         Ldloc(_textposV!);
@@ -2788,14 +2395,15 @@ private void GenerateOneCode()
                         Sub(IsRtl());
                         Callvirt(s_getcharM);
                         if (IsCi())
+                        {
                             CallToLower();
+                        }
 
                         Beq(l1);
                         Back();
                         break;
                     }
 
-
                 case RegexCode.Onerep:
                 case RegexCode.Notonerep:
                 case RegexCode.Setrep:
@@ -2869,7 +2477,9 @@ private void GenerateOneCode()
                         }
                         Callvirt(s_getcharM);
                         if (IsCi())
+                        {
                             CallToLower();
+                        }
 
                         if (Code() == RegexCode.Setrep)
                         {
@@ -2878,27 +2488,33 @@ private void GenerateOneCode()
                                 EmitTimeoutCheck();
                             }
                             EmitCallCharInClass(_strings![Operand(0)], charInClassV);
-
                             BrfalseFar(_backtrack);
                         }
                         else
                         {
                             Ldc(Operand(0));
                             if (Code() == RegexCode.Onerep)
+                            {
                                 BneFar(_backtrack);
+                            }
                             else
+                            {
                                 BeqFar(_backtrack);
+                            }
                         }
                         Ldloc(lenV);
                         Ldc(0);
                         if (Code() == RegexCode.Setrep)
+                        {
                             BgtFar(l1);
+                        }
                         else
+                        {
                             Bgt(l1);
+                        }
                         break;
                     }
 
-
                 case RegexCode.Oneloop:
                 case RegexCode.Notoneloop:
                 case RegexCode.Setloop:
@@ -2926,7 +2542,6 @@ private void GenerateOneCode()
                     //: }
                     //: if (c > i)
                     //:     Track(c - i - 1, Textpos() - 1);
-
                     {
                         LocalBuilder cV = _tempV!;
                         LocalBuilder lenV = _temp2V!;
@@ -2935,9 +2550,11 @@ private void GenerateOneCode()
                         Label l2 = DefineLabel();
 
                         int c = Operand(1);
-
                         if (c == 0)
+                        {
                             break;
+                        }
+
                         if (!IsRtl())
                         {
                             Ldloc(_textendV!);
@@ -2973,16 +2590,26 @@ private void GenerateOneCode()
                         Stloc(cV);
                         Ldc(0);
                         if (Code() == RegexCode.Setloop)
+                        {
                             BleFar(l2);
+                        }
                         else
+                        {
                             Ble(l2);
+                        }
 
                         if (IsRtl())
+                        {
                             Leftcharnext();
+                        }
                         else
+                        {
                             Rightcharnext();
+                        }
                         if (IsCi())
+                        {
                             CallToLower();
+                        }
 
                         if (Code() == RegexCode.Setloop)
                         {
@@ -2991,16 +2618,19 @@ private void GenerateOneCode()
                                 EmitTimeoutCheck();
                             }
                             EmitCallCharInClass(_strings![Operand(0)], charInClassV);
-
                             BrtrueFar(l1);
                         }
                         else
                         {
                             Ldc(Operand(0));
                             if (Code() == RegexCode.Oneloop)
+                            {
                                 Beq(l1);
+                            }
                             else
+                            {
                                 Bne(l1);
+                            }
                         }
 
                         Ldloc(_textposV!);
@@ -3092,9 +2722,10 @@ private void GenerateOneCode()
                         LocalBuilder cV = _tempV!;
 
                         int c = Operand(1);
-
                         if (c == 0)
+                        {
                             break;
+                        }
 
                         if (!IsRtl())
                         {
@@ -3160,26 +2791,35 @@ private void GenerateOneCode()
                     Stloc(_temp2V!);
 
                     if (!IsRtl())
+                    {
                         Rightcharnext();
+                    }
                     else
+                    {
                         Leftcharnext();
+                    }
 
                     if (IsCi())
+                    {
                         CallToLower();
+                    }
 
                     if (Code() == RegexCode.Setlazy)
                     {
                         EmitCallCharInClass(_strings![Operand(0)], charInClassV);
-
                         BrfalseFar(_backtrack);
                     }
                     else
                     {
                         Ldc(Operand(0));
                         if (Code() == RegexCode.Onelazy)
+                        {
                             BneFar(_backtrack);
+                        }
                         else
+                        {
                             BeqFar(_backtrack);
+                        }
                     }
 
                     Ldloc(_temp2V!);
@@ -3200,6 +2840,7 @@ private void GenerateOneCode()
             }
         }
 
+        /// <summary>Emits a call to RegexRunner.CharInClass or a functional equivalent.</summary>
         private void EmitCallCharInClass(string charClass, LocalBuilder tempLocal)
         {
             // We need to perform the equivalent of calling RegexRunner.CharInClass(ch, charClass),
@@ -3363,12 +3004,11 @@ private void EmitCallCharInClass(string charClass, LocalBuilder tempLocal)
             MarkLabel(doneLabel);
         }
 
+        /// <summary>Emits a timeout check.</summary>
         private void EmitTimeoutCheck()
         {
             Debug.Assert(_hasTimeout && _loopV != null);
 
-            Label label = DefineLabel();
-
             // Increment counter for each loop iteration.
             Ldloc(_loopV);
             Ldc(1);
@@ -3376,6 +3016,7 @@ private void EmitTimeoutCheck()
             Stloc(_loopV);
 
             // Emit code to check the timeout every 2000th-iteration.
+            Label label = DefineLabel();
             Ldloc(_loopV);
             Ldc(LoopTimeoutCheckCount);
             Rem();
@@ -3384,7 +3025,6 @@ private void EmitTimeoutCheck()
             Brfalse(label);
             Ldthis();
             Callvirt(s_checkTimeoutM);
-
             MarkLabel(label);
         }
     }
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexLWCGCompiler.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexLWCGCompiler.cs
index f4950ccb0dbc50..6c12c2f5d4db01 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexLWCGCompiler.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexLWCGCompiler.cs
@@ -13,9 +13,7 @@ internal sealed class RegexLWCGCompiler : RegexCompiler
         private static int s_regexCount = 0;
         private static readonly Type[] s_paramTypes = new Type[] { typeof(RegexRunner) };
 
-        /*
-         * The top-level driver. Initializes everything then calls the Generate* methods.
-         */
+        /// <summary>The top-level driver. Initializes everything then calls the Generate* methods.</summary>
         public RegexRunnerFactory FactoryInstanceFromCode(RegexCode code, RegexOptions options, bool hasTimeout)
         {
             _code = code;
@@ -43,9 +41,7 @@ public RegexRunnerFactory FactoryInstanceFromCode(RegexCode code, RegexOptions o
             return new CompiledRegexRunnerFactory(goMethod, firstCharMethod, trackCountMethod);
         }
 
-        /*
-         * Begins the definition of a new method (no args) with a specified return value
-         */
+        /// <summary>Begins the definition of a new method (no args) with a specified return value.</summary>
         public DynamicMethod DefineDynamicMethod(string methname, Type? returntype, Type hostType)
         {
             // We're claiming that these are static methods, but really they are instance methods.

From df9af6a147714770c92bf5551103ce06655bb05d Mon Sep 17 00:00:00 2001
From: Stephen Toub <stoub@microsoft.com>
Date: Tue, 19 Nov 2019 10:27:41 -0500
Subject: [PATCH 11/21] Add more RegexOptions.Compiled tests

---
 .../System.Text.RegularExpressions/tests/Regex.Groups.Tests.cs   | 1 +
 .../System.Text.RegularExpressions/tests/Regex.Match.Tests.cs    | 1 +
 .../tests/Regex.MultipleMatches.Tests.cs                         | 1 +
 3 files changed, 3 insertions(+)

diff --git a/src/libraries/System.Text.RegularExpressions/tests/Regex.Groups.Tests.cs b/src/libraries/System.Text.RegularExpressions/tests/Regex.Groups.Tests.cs
index 45e11e28b5577f..3a510b5d5a4621 100644
--- a/src/libraries/System.Text.RegularExpressions/tests/Regex.Groups.Tests.cs
+++ b/src/libraries/System.Text.RegularExpressions/tests/Regex.Groups.Tests.cs
@@ -672,6 +672,7 @@ private static void Groups(string pattern, string input, RegexOptions options, s
         private void GroupsTest(object[] testCase)
         {
             Groups((string)testCase[0], (string)testCase[1], (RegexOptions)testCase[2], (string[])testCase[3]);
+            Groups((string)testCase[0], (string)testCase[1], RegexOptions.Compiled | (RegexOptions)testCase[2], (string[])testCase[3]);
         }
 
 
diff --git a/src/libraries/System.Text.RegularExpressions/tests/Regex.Match.Tests.cs b/src/libraries/System.Text.RegularExpressions/tests/Regex.Match.Tests.cs
index f4135d31d0a368..1666c3eba67bec 100644
--- a/src/libraries/System.Text.RegularExpressions/tests/Regex.Match.Tests.cs
+++ b/src/libraries/System.Text.RegularExpressions/tests/Regex.Match.Tests.cs
@@ -684,6 +684,7 @@ public static IEnumerable<object[]> Match_Advanced_TestData()
 
         [Theory]
         [MemberData(nameof(Match_Advanced_TestData))]
+        [MemberData(nameof(RegexCompilationHelper.TransformRegexOptions), nameof(Match_Advanced_TestData), 2, MemberType = typeof(RegexCompilationHelper))]
         public void Match(string pattern, string input, RegexOptions options, int beginning, int length, CaptureData[] expected)
         {
             bool isDefaultStart = RegexHelpers.IsDefaultStart(input, options, beginning);
diff --git a/src/libraries/System.Text.RegularExpressions/tests/Regex.MultipleMatches.Tests.cs b/src/libraries/System.Text.RegularExpressions/tests/Regex.MultipleMatches.Tests.cs
index 743404048b61c2..da031415df9c05 100644
--- a/src/libraries/System.Text.RegularExpressions/tests/Regex.MultipleMatches.Tests.cs
+++ b/src/libraries/System.Text.RegularExpressions/tests/Regex.MultipleMatches.Tests.cs
@@ -144,6 +144,7 @@ public static IEnumerable<object[]> Matches_TestData()
 
         [Theory]
         [MemberData(nameof(Matches_TestData))]
+        [MemberData(nameof(RegexCompilationHelper.TransformRegexOptions), nameof(Matches_TestData), 2, MemberType = typeof(RegexCompilationHelper))]
         public void Matches(string pattern, string input, RegexOptions options, CaptureData[] expected)
         {
             if (options == RegexOptions.None)

From 80fd3181021732a975f40134c932f5b14eb5db96 Mon Sep 17 00:00:00 2001
From: Stephen Toub <stoub@microsoft.com>
Date: Tue, 19 Nov 2019 15:20:33 -0500
Subject: [PATCH 12/21] Replace RegexParser.s_category byte[] with a span

---
 .../System/Text/RegularExpressions/RegexParser.cs  | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs
index a1a41b0e2c0075..4a062f119b3110 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs
@@ -1948,7 +1948,7 @@ private bool UseOptionE()
         /*
          * For categorizing ASCII characters.
         */
-        private static readonly byte[] s_category = new byte[] {
+        private static ReadOnlySpan<byte> Category => new byte[] {
             // 0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
                0, 0, 0, 0, 0, 0, 0, 0, 0, X, X, 0, X, X, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
             //    !  "  #  $  %  &  '  (  )  *  +  ,  -  .  /  0  1  2  3  4  5  6  7  8  9  :  ;  <  =  >  ?
@@ -1963,7 +1963,7 @@ private bool UseOptionE()
          */
         private static bool IsSpecial(char ch)
         {
-            return (ch <= '|' && s_category[ch] >= S);
+            return (ch <= '|' && Category[ch] >= S);
         }
 
         /*
@@ -1971,7 +1971,7 @@ private static bool IsSpecial(char ch)
          */
         private static bool IsStopperX(char ch)
         {
-            return (ch <= '|' && s_category[ch] >= X);
+            return (ch <= '|' && Category[ch] >= X);
         }
 
         /*
@@ -1979,7 +1979,7 @@ private static bool IsStopperX(char ch)
          */
         private static bool IsQuantifier(char ch)
         {
-            return (ch <= '{' && s_category[ch] >= Q);
+            return (ch <= '{' && Category[ch] >= Q);
         }
 
         private bool IsTrueQuantifier()
@@ -1989,7 +1989,7 @@ private bool IsTrueQuantifier()
             int startpos = Textpos();
             char ch = CharAt(startpos);
             if (ch != '{')
-                return ch <= '{' && s_category[ch] >= Q;
+                return ch <= '{' && Category[ch] >= Q;
 
             int pos = startpos;
             int nChars = CharsRight();
@@ -2014,7 +2014,7 @@ private bool IsTrueQuantifier()
          */
         private static bool IsSpace(char ch)
         {
-            return (ch <= ' ' && s_category[ch] == X);
+            return (ch <= ' ' && Category[ch] == X);
         }
 
         /*
@@ -2022,7 +2022,7 @@ private static bool IsSpace(char ch)
          */
         private static bool IsMetachar(char ch)
         {
-            return (ch <= '|' && s_category[ch] >= E);
+            return (ch <= '|' && Category[ch] >= E);
         }
 
 

From 53030ee313f488ef255683ba8f18485ff0823974 Mon Sep 17 00:00:00 2001
From: Stephen Toub <stoub@microsoft.com>
Date: Tue, 19 Nov 2019 15:35:49 -0500
Subject: [PATCH 13/21] Avoid delegate allocations each time CreateInstance is
 called

---
 .../CompiledRegexRunnerFactory.cs             | 22 +++++++------------
 .../RegularExpressions/RegexLWCGCompiler.cs   |  7 ++++--
 2 files changed, 13 insertions(+), 16 deletions(-)

diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/CompiledRegexRunnerFactory.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/CompiledRegexRunnerFactory.cs
index 0b5a5504adaf5a..257300e6e3f520 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/CompiledRegexRunnerFactory.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/CompiledRegexRunnerFactory.cs
@@ -5,27 +5,21 @@
 // This is the only concrete implementation of RegexRunnerFactory,
 // but we cannot combine them due to RegexRunnerFactory having shipped public.
 
-using System.Reflection.Emit;
-
 namespace System.Text.RegularExpressions
 {
     internal sealed class CompiledRegexRunnerFactory : RegexRunnerFactory
     {
-        private readonly DynamicMethod _goMethod;
-        private readonly DynamicMethod _findFirstCharMethod;
-        private readonly DynamicMethod _initTrackCountMethod;
+        private readonly Action<RegexRunner> _go;
+        private readonly Func<RegexRunner, bool> _findFirstChar;
+        private readonly Action<RegexRunner> _initTrackCount;
 
-        public CompiledRegexRunnerFactory(DynamicMethod go, DynamicMethod findFirstChar, DynamicMethod initTrackCount)
+        public CompiledRegexRunnerFactory(Action<RegexRunner> go, Func<RegexRunner, bool> findFirstChar, Action<RegexRunner> initTrackCount)
         {
-            _goMethod = go;
-            _findFirstCharMethod = findFirstChar;
-            _initTrackCountMethod = initTrackCount;
+            _go = go;
+            _findFirstChar = findFirstChar;
+            _initTrackCount = initTrackCount;
         }
 
-        protected internal override RegexRunner CreateInstance() =>
-            new CompiledRegexRunner(
-                (Action<RegexRunner>)_goMethod.CreateDelegate(typeof(Action<RegexRunner>)),
-                (Func<RegexRunner, bool>)_findFirstCharMethod.CreateDelegate(typeof(Func<RegexRunner, bool>)),
-                (Action<RegexRunner>)_initTrackCountMethod.CreateDelegate(typeof(Action<RegexRunner>)));
+        protected internal override RegexRunner CreateInstance() => new CompiledRegexRunner(_go, _findFirstChar, _initTrackCount);
     }
 }
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexLWCGCompiler.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexLWCGCompiler.cs
index 6c12c2f5d4db01..3963cbc9a1f9d8 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexLWCGCompiler.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexLWCGCompiler.cs
@@ -38,7 +38,10 @@ public RegexRunnerFactory FactoryInstanceFromCode(RegexCode code, RegexOptions o
             DynamicMethod trackCountMethod = DefineDynamicMethod("InitTrackCount" + regexnumString, null, typeof(CompiledRegexRunner));
             GenerateInitTrackCount();
 
-            return new CompiledRegexRunnerFactory(goMethod, firstCharMethod, trackCountMethod);
+            return new CompiledRegexRunnerFactory(
+                (Action<RegexRunner>)goMethod.CreateDelegate(typeof(Action<RegexRunner>)),
+                (Func<RegexRunner, bool>)firstCharMethod.CreateDelegate(typeof(Func<RegexRunner, bool>)),
+                (Action<RegexRunner>)trackCountMethod.CreateDelegate(typeof(Action<RegexRunner>)));
         }
 
         /// <summary>Begins the definition of a new method (no args) with a specified return value.</summary>
@@ -51,7 +54,7 @@ public DynamicMethod DefineDynamicMethod(string methname, Type? returntype, Type
             const MethodAttributes Attribs = MethodAttributes.Public | MethodAttributes.Static;
             const CallingConventions Conventions = CallingConventions.Standard;
 
-            DynamicMethod dm = new DynamicMethod(methname, Attribs, Conventions, returntype, s_paramTypes, hostType, false /*skipVisibility*/);
+            var dm = new DynamicMethod(methname, Attribs, Conventions, returntype, s_paramTypes, hostType, skipVisibility: false);
             _ilg = dm.GetILGenerator();
             return dm;
         }

From f7327bca4726b8fa83d3db699c34143eb69ccf2c Mon Sep 17 00:00:00 2001
From: Stephen Toub <stoub@microsoft.com>
Date: Tue, 19 Nov 2019 15:55:08 -0500
Subject: [PATCH 14/21] Replace CharUnicodeInfo.GetUnicodeCategory w/ char's
 version

It has a fast path for ASCII.
---
 .../src/System/Text/RegularExpressions/RegexCharClass.cs        | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
index a4e6513d5bcd2b..7b35be5e215029 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
@@ -889,7 +889,7 @@ private static bool CharInClassInternal(char ch, string set, int start, int mySe
 
         private static bool CharInCategory(char ch, string set, int start, int mySetLength, int myCategoryLength)
         {
-            UnicodeCategory chcategory = CharUnicodeInfo.GetUnicodeCategory(ch);
+            UnicodeCategory chcategory = char.GetUnicodeCategory(ch);
 
             int i = start + SETSTART + mySetLength;
             int end = i + myCategoryLength;

From 65b0f802c61fe9ab726c3293a59f9d240c109474 Mon Sep 17 00:00:00 2001
From: Stephen Toub <stoub@microsoft.com>
Date: Tue, 19 Nov 2019 16:02:23 -0500
Subject: [PATCH 15/21] Clean up stackallocs

---
 .../System/Text/RegularExpressions/Match.cs   |  3 +--
 .../Text/RegularExpressions/Regex.Replace.cs  |  3 +--
 .../Text/RegularExpressions/RegexCharClass.cs |  5 ++---
 .../Text/RegularExpressions/RegexCompiler.cs  |  2 --
 .../Text/RegularExpressions/RegexFCD.cs       |  5 +----
 .../Text/RegularExpressions/RegexParser.cs    | 19 +++++++------------
 .../RegularExpressions/RegexReplacement.cs    |  6 ++----
 .../Text/RegularExpressions/RegexWriter.cs    |  5 +----
 8 files changed, 15 insertions(+), 33 deletions(-)

diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Match.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Match.cs
index 1fb35fde8199f2..3e84da0e7e87a2 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Match.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Match.cs
@@ -129,8 +129,7 @@ public virtual string Result(string replacement)
             // Gets the weakly cached replacement helper or creates one if there isn't one already.
             RegexReplacement repl = RegexReplacement.GetOrCreate(_regex._replref!, replacement, _regex.caps!, _regex.capsize,
                 _regex.capnames!, _regex.roptions);
-            Span<char> charInitSpan = stackalloc char[ReplaceBufferSize];
-            var vsb = new ValueStringBuilder(charInitSpan);
+            var vsb = new ValueStringBuilder(stackalloc char[ReplaceBufferSize]);
 
             repl.ReplacementImpl(ref vsb, this);
 
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Replace.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Replace.cs
index 13b88748674cf4..a213908acdb5cf 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Replace.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.Replace.cs
@@ -161,8 +161,7 @@ private static string Replace(MatchEvaluator evaluator, Regex regex, string inpu
             }
             else
             {
-                Span<char> charInitSpan = stackalloc char[ReplaceBufferSize];
-                var vsb = new ValueStringBuilder(charInitSpan);
+                var vsb = new ValueStringBuilder(stackalloc char[ReplaceBufferSize]);
 
                 if (!regex.RightToLeft)
                 {
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
index 7b35be5e215029..6198cd72f18f07 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
@@ -1070,9 +1070,8 @@ public string ToStringClass()
             int rangeLen = _rangelist.Count * 2;
             int strGuessCount = rangeLen + categoriesLength + 3;
 
-            Span<char> buffer = strGuessCount <= 256 ? stackalloc char[256] : null;
-            ValueStringBuilder vsb = buffer != null ?
-                new ValueStringBuilder(buffer) :
+            ValueStringBuilder vsb = strGuessCount <= 256 ?
+                new ValueStringBuilder(stackalloc char[256]) :
                 new ValueStringBuilder(strGuessCount);
 
             int flags = _negate ? 1 : 0;
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs
index 51eeb86934a8ae..2322c72f82d254 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs
@@ -1934,7 +1934,6 @@ private void GenerateOneCode()
                     Back();
                     break;
 
-
                 case RegexCode.Setjump:
                     //: Stack(Trackpos(), Crawlpos());
                     //: Track();
@@ -2248,7 +2247,6 @@ private void GenerateOneCode()
                         break;
                     }
 
-
                 case RegexCode.Multi | RegexCode.Rtl:
                 case RegexCode.Multi | RegexCode.Ci | RegexCode.Rtl:
                     //: String Str = _strings[Operand(0)];
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexFCD.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexFCD.cs
index d67dfa95353620..1dd3e27db5ea36 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexFCD.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexFCD.cs
@@ -54,10 +54,7 @@ private RegexFCD(Span<int> intStack)
         /// </summary>
         public static RegexPrefix? FirstChars(RegexTree t)
         {
-            // Create/rent buffers
-            Span<int> intSpan = stackalloc int[StackBufferSize];
-
-            RegexFCD s = new RegexFCD(intSpan);
+            RegexFCD s = new RegexFCD(stackalloc int[StackBufferSize]);
             RegexFC? fc = s.RegexFCFromRegexTree(t);
             s.Dispose();
 
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs
index 4a062f119b3110..23d679978c1d50 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs
@@ -84,8 +84,7 @@ private RegexParser(string pattern, RegexOptions options, CultureInfo culture, S
 
         public static RegexTree Parse(string pattern, RegexOptions options, CultureInfo culture)
         {
-            Span<RegexOptions> optionSpan = stackalloc RegexOptions[OptionStackDefaultSize];
-            var parser = new RegexParser(pattern, options, culture, optionSpan);
+            var parser = new RegexParser(pattern, options, culture, stackalloc RegexOptions[OptionStackDefaultSize]);
 
             parser.CountCaptures();
             parser.Reset(options);
@@ -103,8 +102,7 @@ public static RegexTree Parse(string pattern, RegexOptions options, CultureInfo
         public static RegexReplacement ParseReplacement(string pattern, RegexOptions options, Hashtable caps, int capsize, Hashtable capnames)
         {
             CultureInfo culture = (options & RegexOptions.CultureInvariant) != 0 ? CultureInfo.InvariantCulture : CultureInfo.CurrentCulture;
-            Span<RegexOptions> optionSpan = stackalloc RegexOptions[OptionStackDefaultSize];
-            var parser = new RegexParser(pattern, options, culture, caps, capsize, capnames, optionSpan);
+            var parser = new RegexParser(pattern, options, culture, caps, capsize, capnames, stackalloc RegexOptions[OptionStackDefaultSize]);
 
             RegexNode root = parser.ScanReplacement();
             var regexReplacement = new RegexReplacement(pattern, root, caps);
@@ -136,9 +134,8 @@ private static string EscapeImpl(string input, int i)
             // characters need to be encoded.
             // For larger string we rent the input string's length plus a fixed
             // conservative amount of chars from the ArrayPool.
-            Span<char> buffer = input.Length <= (EscapeMaxBufferSize / 3) ? stackalloc char[EscapeMaxBufferSize] : default;
-            ValueStringBuilder vsb = !buffer.IsEmpty ?
-                new ValueStringBuilder(buffer) :
+            ValueStringBuilder vsb = input.Length <= (EscapeMaxBufferSize / 3) ?
+                new ValueStringBuilder(stackalloc char[EscapeMaxBufferSize]) :
                 new ValueStringBuilder(input.Length + 200);
 
             char ch = input[i];
@@ -199,14 +196,12 @@ public static string Unescape(string input)
 
         private static string UnescapeImpl(string input, int i)
         {
-            Span<RegexOptions> optionSpan = stackalloc RegexOptions[OptionStackDefaultSize];
-            var parser = new RegexParser(input, RegexOptions.None, CultureInfo.InvariantCulture, optionSpan);
+            var parser = new RegexParser(input, RegexOptions.None, CultureInfo.InvariantCulture, stackalloc RegexOptions[OptionStackDefaultSize]);
 
             // In the worst case the escaped string has the same length.
             // For small inputs we use stack allocation.
-            Span<char> buffer = input.Length <= EscapeMaxBufferSize ? stackalloc char[EscapeMaxBufferSize] : default;
-            ValueStringBuilder vsb = !buffer.IsEmpty ?
-                new ValueStringBuilder(buffer) :
+            ValueStringBuilder vsb = input.Length <= EscapeMaxBufferSize ?
+                new ValueStringBuilder(stackalloc char[EscapeMaxBufferSize]) :
                 new ValueStringBuilder(input.Length);
 
             vsb.Append(input.AsSpan(0, i));
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexReplacement.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexReplacement.cs
index 28d61f89d560f9..6dd81aa687e228 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexReplacement.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexReplacement.cs
@@ -33,8 +33,7 @@ public RegexReplacement(string rep, RegexNode concat, Hashtable _caps)
             if (concat.Type() != RegexNode.Concatenate)
                 throw new ArgumentException(SR.ReplacementError);
 
-            Span<char> buffer = stackalloc char[256];
-            ValueStringBuilder vsb = new ValueStringBuilder(buffer);
+            ValueStringBuilder vsb = new ValueStringBuilder(stackalloc char[256]);
             List<string> strings = new List<string>();
             List<int> rules = new List<int>();
 
@@ -203,8 +202,7 @@ public string Replace(Regex regex, string input, int count, int startat)
             }
             else
             {
-                Span<char> charInitSpan = stackalloc char[256];
-                var vsb = new ValueStringBuilder(charInitSpan);
+                var vsb = new ValueStringBuilder(stackalloc char[256]);
 
                 if (!regex.RightToLeft)
                 {
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexWriter.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexWriter.cs
index 130b26a3a0fba3..917379bfed9e44 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexWriter.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexWriter.cs
@@ -49,10 +49,7 @@ private RegexWriter(Span<int> emittedSpan, Span<int> intStackSpan)
         /// </summary>
         public static RegexCode Write(RegexTree tree)
         {
-            Span<int> emittedSpan = stackalloc int[EmittedSize];
-            Span<int> intStackSpan = stackalloc int[IntStackSize];
-
-            var writer = new RegexWriter(emittedSpan, intStackSpan);
+            var writer = new RegexWriter(stackalloc int[EmittedSize], stackalloc int[IntStackSize]);
             RegexCode code = writer.RegexCodeFromRegexTree(tree);
             writer.Dispose();
 

From 38de90a44c3a4db28d3db7db1b40d76b20e61b5b Mon Sep 17 00:00:00 2001
From: Stephen Toub <stoub@microsoft.com>
Date: Wed, 20 Nov 2019 16:59:58 -0500
Subject: [PATCH 16/21] Remove an unnecessary box

---
 .../src/System/Text/RegularExpressions/RegexParser.cs      | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs
index 23d679978c1d50..59e38d0b1945e7 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs
@@ -1746,11 +1746,12 @@ private void CountCaptures()
          */
         private void NoteCaptureSlot(int i, int pos)
         {
-            if (!_caps.ContainsKey(i))
+            object boxedI = i; // workaround to remove a boxed int when adding to the hashtable
+            if (!_caps.ContainsKey(boxedI))
             {
                 // the rhs of the hashtable isn't used in the parser
 
-                _caps.Add(i, pos);
+                _caps.Add(boxedI, pos);
                 _capcount++;
 
                 if (_captop <= i)
@@ -1812,7 +1813,7 @@ private void AssignNameSlots()
                     _capnumlist[i++] = (int)de.Key;
                 }
 
-                Array.Sort(_capnumlist, Comparer<int>.Default);
+                Array.Sort(_capnumlist);
             }
 
             // merge capsnumlist into capnamelist

From eef9aad9b75ec88d8b5d9180ace85f1ae8ec962c Mon Sep 17 00:00:00 2001
From: Stephen Toub <stoub@microsoft.com>
Date: Thu, 21 Nov 2019 23:33:29 -0500
Subject: [PATCH 17/21] Avoid string allocation for negated category

---
 .../Text/RegularExpressions/RegexCharClass.cs | 38 +++++++++----------
 1 file changed, 19 insertions(+), 19 deletions(-)

diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
index 6198cd72f18f07..feccd94c951b30 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
@@ -529,22 +529,37 @@ public void AddRange(char first, char last)
 
         public void AddCategoryFromName(string categoryName, bool invert, bool caseInsensitive, string pattern, int currentPos)
         {
-            if (s_definedCategories.TryGetValue(categoryName, out string? category) && !categoryName.Equals(InternalRegexIgnoreCase))
+            if (s_definedCategories.TryGetValue(categoryName, out string? category) &&
+                !categoryName.Equals(InternalRegexIgnoreCase))
             {
                 if (caseInsensitive)
                 {
                     if (categoryName.Equals("Ll") || categoryName.Equals("Lu") || categoryName.Equals("Lt"))
+                    {
                         // when RegexOptions.IgnoreCase is specified then {Ll}, {Lu}, and {Lt} cases should all match
                         category = s_definedCategories[InternalRegexIgnoreCase];
+                    }
                 }
 
+                StringBuilder categories = EnsureCategories();
                 if (invert)
-                    category = NegateCategory(category); // negate the category
-
-                EnsureCategories().Append(category);
+                {
+                    // Negate category
+                    for (int i = 0; i < category.Length; i++)
+                    {
+                        short ch = (short)category[i];
+                        categories.Append(unchecked((char)-ch));
+                    }
+                }
+                else
+                {
+                    categories.Append(category);
+                }
             }
             else
+            {
                 AddSet(SetFromProperty(categoryName, invert, pattern, currentPos));
+            }
         }
 
         private void AddCategory(string category)
@@ -997,21 +1012,6 @@ private static bool CharInCategoryGroup(char ch, UnicodeCategory chcategory, str
             }
         }
 
-        private static string? NegateCategory(string category)
-        {
-            if (category == null)
-                return null;
-
-            return string.Create(category.Length, category, (span, _category) =>
-            {
-                for (int i = 0; i < _category.Length; i++)
-                {
-                    short ch = (short)_category[i];
-                    span[i] = unchecked((char)-ch);
-                }
-            });
-        }
-
         public static RegexCharClass Parse(string charClass)
         {
             return ParseRecursive(charClass, 0);

From 8349787f23437cca9dff6a4dc37aac76047fada4 Mon Sep 17 00:00:00 2001
From: Stephen Toub <stoub@microsoft.com>
Date: Fri, 22 Nov 2019 11:10:03 -0500
Subject: [PATCH 18/21] Simplify tests

---
 .../tests/Regex.Groups.Tests.cs               | 877 ++++++++----------
 1 file changed, 400 insertions(+), 477 deletions(-)

diff --git a/src/libraries/System.Text.RegularExpressions/tests/Regex.Groups.Tests.cs b/src/libraries/System.Text.RegularExpressions/tests/Regex.Groups.Tests.cs
index 3a510b5d5a4621..5b3fe7254436e0 100644
--- a/src/libraries/System.Text.RegularExpressions/tests/Regex.Groups.Tests.cs
+++ b/src/libraries/System.Text.RegularExpressions/tests/Regex.Groups.Tests.cs
@@ -14,124 +14,117 @@ namespace System.Text.RegularExpressions.Tests
 {
     public class RegexGroupTests
     {
-        private static readonly CultureInfo s_enUSCulture = new CultureInfo("en-US");
-        private static readonly CultureInfo s_invariantCulture = new CultureInfo("");
-        private static readonly CultureInfo s_czechCulture = new CultureInfo("cs-CZ");
-        private static readonly CultureInfo s_danishCulture = new CultureInfo("da-DK");
-        private static readonly CultureInfo s_turkishCulture = new CultureInfo("tr-TR");
-        private static readonly CultureInfo s_azeriLatinCulture = new CultureInfo("az-Latn-AZ");
-
         public static IEnumerable<object[]> Groups_Basic_TestData()
         {
             // (A - B) B is a subset of A(ie B only contains chars that are in A)
-            yield return new object[] { "[abcd-[d]]+", "dddaabbccddd", RegexOptions.None, new string[] { "aabbcc" } };
+            yield return new object[] { null, "[abcd-[d]]+", "dddaabbccddd", RegexOptions.None, new string[] { "aabbcc" } };
 
-            yield return new object[] { @"[\d-[357]]+", "33312468955", RegexOptions.None, new string[] { "124689" } };
-            yield return new object[] { @"[\d-[357]]+", "51246897", RegexOptions.None, new string[] { "124689" } };
-            yield return new object[] { @"[\d-[357]]+", "3312468977", RegexOptions.None, new string[] { "124689" } };
+            yield return new object[] { null, @"[\d-[357]]+", "33312468955", RegexOptions.None, new string[] { "124689" } };
+            yield return new object[] { null, @"[\d-[357]]+", "51246897", RegexOptions.None, new string[] { "124689" } };
+            yield return new object[] { null, @"[\d-[357]]+", "3312468977", RegexOptions.None, new string[] { "124689" } };
 
-            yield return new object[] { @"[\w-[b-y]]+", "bbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "aaaABCD09zzz" } };
+            yield return new object[] { null, @"[\w-[b-y]]+", "bbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "aaaABCD09zzz" } };
 
-            yield return new object[] { @"[\w-[\d]]+", "0AZaz9", RegexOptions.None, new string[] { "AZaz" } };
-            yield return new object[] { @"[\w-[\p{Ll}]]+", "a09AZz", RegexOptions.None, new string[] { "09AZ" } };
+            yield return new object[] { null, @"[\w-[\d]]+", "0AZaz9", RegexOptions.None, new string[] { "AZaz" } };
+            yield return new object[] { null, @"[\w-[\p{Ll}]]+", "a09AZz", RegexOptions.None, new string[] { "09AZ" } };
 
-            yield return new object[] { @"[\d-[13579]]+", "1024689", RegexOptions.ECMAScript, new string[] { "02468" } };
-            yield return new object[] { @"[\d-[13579]]+", "\x066102468\x0660", RegexOptions.ECMAScript, new string[] { "02468" } };
-            yield return new object[] { @"[\d-[13579]]+", "\x066102468\x0660", RegexOptions.None, new string[] { "\x066102468\x0660" } };
+            yield return new object[] { null, @"[\d-[13579]]+", "1024689", RegexOptions.ECMAScript, new string[] { "02468" } };
+            yield return new object[] { null, @"[\d-[13579]]+", "\x066102468\x0660", RegexOptions.ECMAScript, new string[] { "02468" } };
+            yield return new object[] { null, @"[\d-[13579]]+", "\x066102468\x0660", RegexOptions.None, new string[] { "\x066102468\x0660" } };
 
-            yield return new object[] { @"[\w-[b-y]]+", "bbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "aaaABCD09zzz" } };
+            yield return new object[] { null, @"[\w-[b-y]]+", "bbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "aaaABCD09zzz" } };
 
-            yield return new object[] { @"[\w-[b-y]]+", "bbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "aaaABCD09zzz" } };
-            yield return new object[] { @"[\w-[b-y]]+", "bbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "aaaABCD09zzz" } };
+            yield return new object[] { null, @"[\w-[b-y]]+", "bbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "aaaABCD09zzz" } };
+            yield return new object[] { null, @"[\w-[b-y]]+", "bbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "aaaABCD09zzz" } };
 
-            yield return new object[] { @"[\p{Ll}-[ae-z]]+", "aaabbbcccdddeee", RegexOptions.None, new string[] { "bbbcccddd" } };
-            yield return new object[] { @"[\p{Nd}-[2468]]+", "20135798", RegexOptions.None, new string[] { "013579" } };
+            yield return new object[] { null, @"[\p{Ll}-[ae-z]]+", "aaabbbcccdddeee", RegexOptions.None, new string[] { "bbbcccddd" } };
+            yield return new object[] { null, @"[\p{Nd}-[2468]]+", "20135798", RegexOptions.None, new string[] { "013579" } };
 
-            yield return new object[] { @"[\P{Lu}-[ae-z]]+", "aaabbbcccdddeee", RegexOptions.None, new string[] { "bbbcccddd" } };
-            yield return new object[] { @"[\P{Nd}-[\p{Ll}]]+", "az09AZ'[]", RegexOptions.None, new string[] { "AZ'[]" } };
+            yield return new object[] { null, @"[\P{Lu}-[ae-z]]+", "aaabbbcccdddeee", RegexOptions.None, new string[] { "bbbcccddd" } };
+            yield return new object[] { null, @"[\P{Nd}-[\p{Ll}]]+", "az09AZ'[]", RegexOptions.None, new string[] { "AZ'[]" } };
 
             // (A - B) B is a superset of A (ie B contains chars that are in A plus other chars that are not in A)
-            yield return new object[] { "[abcd-[def]]+", "fedddaabbccddd", RegexOptions.None, new string[] { "aabbcc" } };
+            yield return new object[] { null, "[abcd-[def]]+", "fedddaabbccddd", RegexOptions.None, new string[] { "aabbcc" } };
 
-            yield return new object[] { @"[\d-[357a-z]]+", "az33312468955", RegexOptions.None, new string[] { "124689" } };
-            yield return new object[] { @"[\d-[de357fgA-Z]]+", "AZ51246897", RegexOptions.None, new string[] { "124689" } };
-            yield return new object[] { @"[\d-[357\p{Ll}]]+", "az3312468977", RegexOptions.None, new string[] { "124689" } };
+            yield return new object[] { null, @"[\d-[357a-z]]+", "az33312468955", RegexOptions.None, new string[] { "124689" } };
+            yield return new object[] { null, @"[\d-[de357fgA-Z]]+", "AZ51246897", RegexOptions.None, new string[] { "124689" } };
+            yield return new object[] { null, @"[\d-[357\p{Ll}]]+", "az3312468977", RegexOptions.None, new string[] { "124689" } };
 
-            yield return new object[] { @"[\w-[b-y\s]]+", " \tbbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "aaaABCD09zzz" } };
+            yield return new object[] { null, @"[\w-[b-y\s]]+", " \tbbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "aaaABCD09zzz" } };
 
-            yield return new object[] { @"[\w-[\d\p{Po}]]+", "!#0AZaz9", RegexOptions.None, new string[] { "AZaz" } };
-            yield return new object[] { @"[\w-[\p{Ll}\s]]+", "a09AZz", RegexOptions.None, new string[] { "09AZ" } };
+            yield return new object[] { null, @"[\w-[\d\p{Po}]]+", "!#0AZaz9", RegexOptions.None, new string[] { "AZaz" } };
+            yield return new object[] { null, @"[\w-[\p{Ll}\s]]+", "a09AZz", RegexOptions.None, new string[] { "09AZ" } };
 
-            yield return new object[] { @"[\d-[13579a-zA-Z]]+", "AZ1024689", RegexOptions.ECMAScript, new string[] { "02468" } };
-            yield return new object[] { @"[\d-[13579abcd]]+", "abcd\x066102468\x0660", RegexOptions.ECMAScript, new string[] { "02468" } };
-            yield return new object[] { @"[\d-[13579\s]]+", " \t\x066102468\x0660", RegexOptions.None, new string[] { "\x066102468\x0660" } };
+            yield return new object[] { null, @"[\d-[13579a-zA-Z]]+", "AZ1024689", RegexOptions.ECMAScript, new string[] { "02468" } };
+            yield return new object[] { null, @"[\d-[13579abcd]]+", "abcd\x066102468\x0660", RegexOptions.ECMAScript, new string[] { "02468" } };
+            yield return new object[] { null, @"[\d-[13579\s]]+", " \t\x066102468\x0660", RegexOptions.None, new string[] { "\x066102468\x0660" } };
 
-            yield return new object[] { @"[\w-[b-y\p{Po}]]+", "!#bbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "aaaABCD09zzz" } };
+            yield return new object[] { null, @"[\w-[b-y\p{Po}]]+", "!#bbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "aaaABCD09zzz" } };
 
-            yield return new object[] { @"[\w-[b-y!.,]]+", "!.,bbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "aaaABCD09zzz" } };
-            yield return new object[] { "[\\w-[b-y\x00-\x0F]]+", "\0bbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "aaaABCD09zzz" } };
+            yield return new object[] { null, @"[\w-[b-y!.,]]+", "!.,bbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "aaaABCD09zzz" } };
+            yield return new object[] { null, "[\\w-[b-y\x00-\x0F]]+", "\0bbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "aaaABCD09zzz" } };
 
-            yield return new object[] { @"[\p{Ll}-[ae-z0-9]]+", "09aaabbbcccdddeee", RegexOptions.None, new string[] { "bbbcccddd" } };
-            yield return new object[] { @"[\p{Nd}-[2468az]]+", "az20135798", RegexOptions.None, new string[] { "013579" } };
+            yield return new object[] { null, @"[\p{Ll}-[ae-z0-9]]+", "09aaabbbcccdddeee", RegexOptions.None, new string[] { "bbbcccddd" } };
+            yield return new object[] { null, @"[\p{Nd}-[2468az]]+", "az20135798", RegexOptions.None, new string[] { "013579" } };
 
-            yield return new object[] { @"[\P{Lu}-[ae-zA-Z]]+", "AZaaabbbcccdddeee", RegexOptions.None, new string[] { "bbbcccddd" } };
-            yield return new object[] { @"[\P{Nd}-[\p{Ll}0123456789]]+", "09az09AZ'[]", RegexOptions.None, new string[] { "AZ'[]" } };
+            yield return new object[] { null, @"[\P{Lu}-[ae-zA-Z]]+", "AZaaabbbcccdddeee", RegexOptions.None, new string[] { "bbbcccddd" } };
+            yield return new object[] { null, @"[\P{Nd}-[\p{Ll}0123456789]]+", "09az09AZ'[]", RegexOptions.None, new string[] { "AZ'[]" } };
 
             // (A - B) B only contains chars that are not in A
-            yield return new object[] { "[abc-[defg]]+", "dddaabbccddd", RegexOptions.None, new string[] { "aabbcc" } };
+            yield return new object[] { null, "[abc-[defg]]+", "dddaabbccddd", RegexOptions.None, new string[] { "aabbcc" } };
 
-            yield return new object[] { @"[\d-[abc]]+", "abc09abc", RegexOptions.None, new string[] { "09" } };
-            yield return new object[] { @"[\d-[a-zA-Z]]+", "az09AZ", RegexOptions.None, new string[] { "09" } };
-            yield return new object[] { @"[\d-[\p{Ll}]]+", "az09az", RegexOptions.None, new string[] { "09" } };
+            yield return new object[] { null, @"[\d-[abc]]+", "abc09abc", RegexOptions.None, new string[] { "09" } };
+            yield return new object[] { null, @"[\d-[a-zA-Z]]+", "az09AZ", RegexOptions.None, new string[] { "09" } };
+            yield return new object[] { null, @"[\d-[\p{Ll}]]+", "az09az", RegexOptions.None, new string[] { "09" } };
 
-            yield return new object[] { @"[\w-[\x00-\x0F]]+", "bbbaaaABYZ09zzzyyy", RegexOptions.None, new string[] { "bbbaaaABYZ09zzzyyy" } };
+            yield return new object[] { null, @"[\w-[\x00-\x0F]]+", "bbbaaaABYZ09zzzyyy", RegexOptions.None, new string[] { "bbbaaaABYZ09zzzyyy" } };
 
-            yield return new object[] { @"[\w-[\s]]+", "0AZaz9", RegexOptions.None, new string[] { "0AZaz9" } };
-            yield return new object[] { @"[\w-[\W]]+", "0AZaz9", RegexOptions.None, new string[] { "0AZaz9" } };
-            yield return new object[] { @"[\w-[\p{Po}]]+", "#a09AZz!", RegexOptions.None, new string[] { "a09AZz" } };
+            yield return new object[] { null, @"[\w-[\s]]+", "0AZaz9", RegexOptions.None, new string[] { "0AZaz9" } };
+            yield return new object[] { null, @"[\w-[\W]]+", "0AZaz9", RegexOptions.None, new string[] { "0AZaz9" } };
+            yield return new object[] { null, @"[\w-[\p{Po}]]+", "#a09AZz!", RegexOptions.None, new string[] { "a09AZz" } };
 
-            yield return new object[] { @"[\d-[\D]]+", "azAZ1024689", RegexOptions.ECMAScript, new string[] { "1024689" } };
-            yield return new object[] { @"[\d-[a-zA-Z]]+", "azAZ\x066102468\x0660", RegexOptions.ECMAScript, new string[] { "02468" } };
-            yield return new object[] { @"[\d-[\p{Ll}]]+", "\x066102468\x0660", RegexOptions.None, new string[] { "\x066102468\x0660" } };
+            yield return new object[] { null, @"[\d-[\D]]+", "azAZ1024689", RegexOptions.ECMAScript, new string[] { "1024689" } };
+            yield return new object[] { null, @"[\d-[a-zA-Z]]+", "azAZ\x066102468\x0660", RegexOptions.ECMAScript, new string[] { "02468" } };
+            yield return new object[] { null, @"[\d-[\p{Ll}]]+", "\x066102468\x0660", RegexOptions.None, new string[] { "\x066102468\x0660" } };
 
-            yield return new object[] { @"[a-zA-Z0-9-[\s]]+", " \tazAZ09", RegexOptions.None, new string[] { "azAZ09" } };
+            yield return new object[] { null, @"[a-zA-Z0-9-[\s]]+", " \tazAZ09", RegexOptions.None, new string[] { "azAZ09" } };
 
-            yield return new object[] { @"[a-zA-Z0-9-[\W]]+", "bbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "bbbaaaABCD09zzzyyy" } };
-            yield return new object[] { @"[a-zA-Z0-9-[^a-zA-Z0-9]]+", "bbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "bbbaaaABCD09zzzyyy" } };
+            yield return new object[] { null, @"[a-zA-Z0-9-[\W]]+", "bbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "bbbaaaABCD09zzzyyy" } };
+            yield return new object[] { null, @"[a-zA-Z0-9-[^a-zA-Z0-9]]+", "bbbaaaABCD09zzzyyy", RegexOptions.None, new string[] { "bbbaaaABCD09zzzyyy" } };
 
-            yield return new object[] { @"[\p{Ll}-[A-Z]]+", "AZaz09", RegexOptions.None, new string[] { "az" } };
-            yield return new object[] { @"[\p{Nd}-[a-z]]+", "az09", RegexOptions.None, new string[] { "09" } };
+            yield return new object[] { null, @"[\p{Ll}-[A-Z]]+", "AZaz09", RegexOptions.None, new string[] { "az" } };
+            yield return new object[] { null, @"[\p{Nd}-[a-z]]+", "az09", RegexOptions.None, new string[] { "09" } };
 
-            yield return new object[] { @"[\P{Lu}-[\p{Lu}]]+", "AZazAZ", RegexOptions.None, new string[] { "az" } };
-            yield return new object[] { @"[\P{Lu}-[A-Z]]+", "AZazAZ", RegexOptions.None, new string[] { "az" } };
-            yield return new object[] { @"[\P{Nd}-[\p{Nd}]]+", "azAZ09", RegexOptions.None, new string[] { "azAZ" } };
-            yield return new object[] { @"[\P{Nd}-[2-8]]+", "1234567890azAZ1234567890", RegexOptions.None, new string[] { "azAZ" } };
+            yield return new object[] { null, @"[\P{Lu}-[\p{Lu}]]+", "AZazAZ", RegexOptions.None, new string[] { "az" } };
+            yield return new object[] { null, @"[\P{Lu}-[A-Z]]+", "AZazAZ", RegexOptions.None, new string[] { "az" } };
+            yield return new object[] { null, @"[\P{Nd}-[\p{Nd}]]+", "azAZ09", RegexOptions.None, new string[] { "azAZ" } };
+            yield return new object[] { null, @"[\P{Nd}-[2-8]]+", "1234567890azAZ1234567890", RegexOptions.None, new string[] { "azAZ" } };
 
             // Alternating construct
-            yield return new object[] { @"([ ]|[\w-[0-9]])+", "09az AZ90", RegexOptions.None, new string[] { "az AZ", "Z" } };
-            yield return new object[] { @"([0-9-[02468]]|[0-9-[13579]])+", "az1234567890za", RegexOptions.None, new string[] { "1234567890", "0" } };
-            yield return new object[] { @"([^0-9-[a-zAE-Z]]|[\w-[a-zAF-Z]])+", "azBCDE1234567890BCDEFza", RegexOptions.None, new string[] { "BCDE1234567890BCDE", "E" } };
-            yield return new object[] { @"([\p{Ll}-[aeiou]]|[^\w-[\s]])+", "aeiobcdxyz!@#aeio", RegexOptions.None, new string[] { "bcdxyz!@#", "#" } };
+            yield return new object[] { null, @"([ ]|[\w-[0-9]])+", "09az AZ90", RegexOptions.None, new string[] { "az AZ", "Z" } };
+            yield return new object[] { null, @"([0-9-[02468]]|[0-9-[13579]])+", "az1234567890za", RegexOptions.None, new string[] { "1234567890", "0" } };
+            yield return new object[] { null, @"([^0-9-[a-zAE-Z]]|[\w-[a-zAF-Z]])+", "azBCDE1234567890BCDEFza", RegexOptions.None, new string[] { "BCDE1234567890BCDE", "E" } };
+            yield return new object[] { null, @"([\p{Ll}-[aeiou]]|[^\w-[\s]])+", "aeiobcdxyz!@#aeio", RegexOptions.None, new string[] { "bcdxyz!@#", "#" } };
 
             // Multiple character classes using character class subtraction
-            yield return new object[] { @"98[\d-[9]][\d-[8]][\d-[0]]", "98911 98881 98870 98871", RegexOptions.None, new string[] { "98871" } };
-            yield return new object[] { @"m[\w-[^aeiou]][\w-[^aeiou]]t", "mbbt mect meet", RegexOptions.None, new string[] { "meet" } };
+            yield return new object[] { null, @"98[\d-[9]][\d-[8]][\d-[0]]", "98911 98881 98870 98871", RegexOptions.None, new string[] { "98871" } };
+            yield return new object[] { null, @"m[\w-[^aeiou]][\w-[^aeiou]]t", "mbbt mect meet", RegexOptions.None, new string[] { "meet" } };
 
             // Negation with character class subtraction
-            yield return new object[] { "[abcdef-[^bce]]+", "adfbcefda", RegexOptions.None, new string[] { "bce" } };
-            yield return new object[] { "[^cde-[ag]]+", "agbfxyzga", RegexOptions.None, new string[] { "bfxyz" } };
+            yield return new object[] { null, "[abcdef-[^bce]]+", "adfbcefda", RegexOptions.None, new string[] { "bce" } };
+            yield return new object[] { null, "[^cde-[ag]]+", "agbfxyzga", RegexOptions.None, new string[] { "bfxyz" } };
 
             // Misc The idea here is come up with real world examples of char class subtraction. Things that
             // would be difficult to define without it
-            yield return new object[] { @"[\p{L}-[^\p{Lu}]]+", "09',.abcxyzABCXYZ", RegexOptions.None, new string[] { "ABCXYZ" } };
+            yield return new object[] { null, @"[\p{L}-[^\p{Lu}]]+", "09',.abcxyzABCXYZ", RegexOptions.None, new string[] { "ABCXYZ" } };
 
-            yield return new object[] { @"[\p{IsGreek}-[\P{Lu}]]+", "\u0390\u03FE\u0386\u0388\u03EC\u03EE\u0400", RegexOptions.None, new string[] { "\u03FE\u0386\u0388\u03EC\u03EE" } };
-            yield return new object[] { @"[\p{IsBasicLatin}-[G-L]]+", "GAFMZL", RegexOptions.None, new string[] { "AFMZ" } };
+            yield return new object[] { null, @"[\p{IsGreek}-[\P{Lu}]]+", "\u0390\u03FE\u0386\u0388\u03EC\u03EE\u0400", RegexOptions.None, new string[] { "\u03FE\u0386\u0388\u03EC\u03EE" } };
+            yield return new object[] { null, @"[\p{IsBasicLatin}-[G-L]]+", "GAFMZL", RegexOptions.None, new string[] { "AFMZ" } };
 
-            yield return new object[] { "[a-zA-Z-[aeiouAEIOU]]+", "aeiouAEIOUbcdfghjklmnpqrstvwxyz", RegexOptions.None, new string[] { "bcdfghjklmnpqrstvwxyz" } };
+            yield return new object[] { null, "[a-zA-Z-[aeiouAEIOU]]+", "aeiouAEIOUbcdfghjklmnpqrstvwxyz", RegexOptions.None, new string[] { "bcdfghjklmnpqrstvwxyz" } };
 
             // The following is an overly complex way of matching an ip address using char class subtraction
-            yield return new object[] { @"^
+            yield return new object[] { null, @"^
             (?<octet>^
                 (
                     (
@@ -164,324 +157,324 @@ public static IEnumerable<object[]> Groups_Basic_TestData()
             , "255", RegexOptions.IgnorePatternWhitespace, new string[] { "255", "255", "2", "5", "5", "", "255", "2", "5" } };
 
             // Character Class Substraction
-            yield return new object[] { @"[abcd\-d-[bc]]+", "bbbaaa---dddccc", RegexOptions.None, new string[] { "aaa---ddd" } };
-            yield return new object[] { @"[abcd\-d-[bc]]+", "bbbaaa---dddccc", RegexOptions.None, new string[] { "aaa---ddd" } };
-            yield return new object[] { @"[^a-f-[\x00-\x60\u007B-\uFFFF]]+", "aaafffgggzzz{{{", RegexOptions.None, new string[] { "gggzzz" } };
-            yield return new object[] { @"[\[\]a-f-[[]]+", "gggaaafff]]][[[", RegexOptions.None, new string[] { "aaafff]]]" } };
-            yield return new object[] { @"[\[\]a-f-[]]]+", "gggaaafff[[[]]]", RegexOptions.None, new string[] { "aaafff[[[" } };
+            yield return new object[] { null, @"[abcd\-d-[bc]]+", "bbbaaa---dddccc", RegexOptions.None, new string[] { "aaa---ddd" } };
+            yield return new object[] { null, @"[abcd\-d-[bc]]+", "bbbaaa---dddccc", RegexOptions.None, new string[] { "aaa---ddd" } };
+            yield return new object[] { null, @"[^a-f-[\x00-\x60\u007B-\uFFFF]]+", "aaafffgggzzz{{{", RegexOptions.None, new string[] { "gggzzz" } };
+            yield return new object[] { null, @"[\[\]a-f-[[]]+", "gggaaafff]]][[[", RegexOptions.None, new string[] { "aaafff]]]" } };
+            yield return new object[] { null, @"[\[\]a-f-[]]]+", "gggaaafff[[[]]]", RegexOptions.None, new string[] { "aaafff[[[" } };
 
-            yield return new object[] { @"[ab\-\[cd-[-[]]]]", "a]]", RegexOptions.None, new string[] { "a]]" } };
-            yield return new object[] { @"[ab\-\[cd-[-[]]]]", "b]]", RegexOptions.None, new string[] { "b]]" } };
-            yield return new object[] { @"[ab\-\[cd-[-[]]]]", "c]]", RegexOptions.None, new string[] { "c]]" } };
-            yield return new object[] { @"[ab\-\[cd-[-[]]]]", "d]]", RegexOptions.None, new string[] { "d]]" } };
+            yield return new object[] { null, @"[ab\-\[cd-[-[]]]]", "a]]", RegexOptions.None, new string[] { "a]]" } };
+            yield return new object[] { null, @"[ab\-\[cd-[-[]]]]", "b]]", RegexOptions.None, new string[] { "b]]" } };
+            yield return new object[] { null, @"[ab\-\[cd-[-[]]]]", "c]]", RegexOptions.None, new string[] { "c]]" } };
+            yield return new object[] { null, @"[ab\-\[cd-[-[]]]]", "d]]", RegexOptions.None, new string[] { "d]]" } };
 
-            yield return new object[] { @"[ab\-\[cd-[[]]]]", "a]]", RegexOptions.None, new string[] { "a]]" } };
-            yield return new object[] { @"[ab\-\[cd-[[]]]]", "b]]", RegexOptions.None, new string[] { "b]]" } };
-            yield return new object[] { @"[ab\-\[cd-[[]]]]", "c]]", RegexOptions.None, new string[] { "c]]" } };
-            yield return new object[] { @"[ab\-\[cd-[[]]]]", "d]]", RegexOptions.None, new string[] { "d]]" } };
-            yield return new object[] { @"[ab\-\[cd-[[]]]]", "-]]", RegexOptions.None, new string[] { "-]]" } };
+            yield return new object[] { null, @"[ab\-\[cd-[[]]]]", "a]]", RegexOptions.None, new string[] { "a]]" } };
+            yield return new object[] { null, @"[ab\-\[cd-[[]]]]", "b]]", RegexOptions.None, new string[] { "b]]" } };
+            yield return new object[] { null, @"[ab\-\[cd-[[]]]]", "c]]", RegexOptions.None, new string[] { "c]]" } };
+            yield return new object[] { null, @"[ab\-\[cd-[[]]]]", "d]]", RegexOptions.None, new string[] { "d]]" } };
+            yield return new object[] { null, @"[ab\-\[cd-[[]]]]", "-]]", RegexOptions.None, new string[] { "-]]" } };
 
-            yield return new object[] { @"[a-[c-e]]+", "bbbaaaccc", RegexOptions.None, new string[] { "aaa" } };
-            yield return new object[] { @"[a-[c-e]]+", "```aaaccc", RegexOptions.None, new string[] { "aaa" } };
+            yield return new object[] { null, @"[a-[c-e]]+", "bbbaaaccc", RegexOptions.None, new string[] { "aaa" } };
+            yield return new object[] { null, @"[a-[c-e]]+", "```aaaccc", RegexOptions.None, new string[] { "aaa" } };
 
-            yield return new object[] { @"[a-d\--[bc]]+", "cccaaa--dddbbb", RegexOptions.None, new string[] { "aaa--ddd" } };
+            yield return new object[] { null, @"[a-d\--[bc]]+", "cccaaa--dddbbb", RegexOptions.None, new string[] { "aaa--ddd" } };
 
             // Not Character class substraction
-            yield return new object[] { @"[\0- [bc]+", "!!!\0\0\t\t  [[[[bbbcccaaa", RegexOptions.None, new string[] { "\0\0\t\t  [[[[bbbccc" } };
-            yield return new object[] { "[[abcd]-[bc]]+", "a-b]", RegexOptions.None, new string[] { "a-b]" } };
-            yield return new object[] { "[-[e-g]+", "ddd[[[---eeefffggghhh", RegexOptions.None, new string[] { "[[[---eeefffggg" } };
-            yield return new object[] { "[-e-g]+", "ddd---eeefffggghhh", RegexOptions.None, new string[] { "---eeefffggg" } };
-            yield return new object[] { "[-e-g]+", "ddd---eeefffggghhh", RegexOptions.None, new string[] { "---eeefffggg" } };
-            yield return new object[] { "[a-e - m-p]+", "---a b c d e m n o p---", RegexOptions.None, new string[] { "a b c d e m n o p" } };
-            yield return new object[] { "[^-[bc]]", "b] c] -] aaaddd]", RegexOptions.None, new string[] { "d]" } };
-            yield return new object[] { "[^-[bc]]", "b] c] -] aaa]ddd]", RegexOptions.None, new string[] { "a]" } };
+            yield return new object[] { null, @"[\0- [bc]+", "!!!\0\0\t\t  [[[[bbbcccaaa", RegexOptions.None, new string[] { "\0\0\t\t  [[[[bbbccc" } };
+            yield return new object[] { null, "[[abcd]-[bc]]+", "a-b]", RegexOptions.None, new string[] { "a-b]" } };
+            yield return new object[] { null, "[-[e-g]+", "ddd[[[---eeefffggghhh", RegexOptions.None, new string[] { "[[[---eeefffggg" } };
+            yield return new object[] { null, "[-e-g]+", "ddd---eeefffggghhh", RegexOptions.None, new string[] { "---eeefffggg" } };
+            yield return new object[] { null, "[-e-g]+", "ddd---eeefffggghhh", RegexOptions.None, new string[] { "---eeefffggg" } };
+            yield return new object[] { null, "[a-e - m-p]+", "---a b c d e m n o p---", RegexOptions.None, new string[] { "a b c d e m n o p" } };
+            yield return new object[] { null, "[^-[bc]]", "b] c] -] aaaddd]", RegexOptions.None, new string[] { "d]" } };
+            yield return new object[] { null, "[^-[bc]]", "b] c] -] aaa]ddd]", RegexOptions.None, new string[] { "a]" } };
 
             // Make sure we correctly handle \-
-            yield return new object[] { @"[a\-[bc]+", "```bbbaaa---[[[cccddd", RegexOptions.None, new string[] { "bbbaaa---[[[ccc" } };
-            yield return new object[] { @"[a\-[\-\-bc]+", "```bbbaaa---[[[cccddd", RegexOptions.None, new string[] { "bbbaaa---[[[ccc" } };
-            yield return new object[] { @"[a\-\[\-\[\-bc]+", "```bbbaaa---[[[cccddd", RegexOptions.None, new string[] { "bbbaaa---[[[ccc" } };
-            yield return new object[] { @"[abc\--[b]]+", "[[[```bbbaaa---cccddd", RegexOptions.None, new string[] { "aaa---ccc" } };
-            yield return new object[] { @"[abc\-z-[b]]+", "```aaaccc---zzzbbb", RegexOptions.None, new string[] { "aaaccc---zzz" } };
-            yield return new object[] { @"[a-d\-[b]+", "```aaabbbcccddd----[[[[]]]", RegexOptions.None, new string[] { "aaabbbcccddd----[[[[" } };
-            yield return new object[] { @"[abcd\-d\-[bc]+", "bbbaaa---[[[dddccc", RegexOptions.None, new string[] { "bbbaaa---[[[dddccc" } };
+            yield return new object[] { null, @"[a\-[bc]+", "```bbbaaa---[[[cccddd", RegexOptions.None, new string[] { "bbbaaa---[[[ccc" } };
+            yield return new object[] { null, @"[a\-[\-\-bc]+", "```bbbaaa---[[[cccddd", RegexOptions.None, new string[] { "bbbaaa---[[[ccc" } };
+            yield return new object[] { null, @"[a\-\[\-\[\-bc]+", "```bbbaaa---[[[cccddd", RegexOptions.None, new string[] { "bbbaaa---[[[ccc" } };
+            yield return new object[] { null, @"[abc\--[b]]+", "[[[```bbbaaa---cccddd", RegexOptions.None, new string[] { "aaa---ccc" } };
+            yield return new object[] { null, @"[abc\-z-[b]]+", "```aaaccc---zzzbbb", RegexOptions.None, new string[] { "aaaccc---zzz" } };
+            yield return new object[] { null, @"[a-d\-[b]+", "```aaabbbcccddd----[[[[]]]", RegexOptions.None, new string[] { "aaabbbcccddd----[[[[" } };
+            yield return new object[] { null, @"[abcd\-d\-[bc]+", "bbbaaa---[[[dddccc", RegexOptions.None, new string[] { "bbbaaa---[[[dddccc" } };
 
             // Everything works correctly with option RegexOptions.IgnorePatternWhitespace
-            yield return new object[] { "[a - c - [ b ] ]+", "dddaaa   ccc [[[[ bbb ]]]", RegexOptions.IgnorePatternWhitespace, new string[] { " ]]]" } };
-            yield return new object[] { "[a - c - [ b ] +", "dddaaa   ccc [[[[ bbb ]]]", RegexOptions.IgnorePatternWhitespace, new string[] { "aaa   ccc [[[[ bbb " } };
+            yield return new object[] { null, "[a - c - [ b ] ]+", "dddaaa   ccc [[[[ bbb ]]]", RegexOptions.IgnorePatternWhitespace, new string[] { " ]]]" } };
+            yield return new object[] { null, "[a - c - [ b ] +", "dddaaa   ccc [[[[ bbb ]]]", RegexOptions.IgnorePatternWhitespace, new string[] { "aaa   ccc [[[[ bbb " } };
 
             // Unicode Char Classes
-            yield return new object[] { @"(\p{Lu}\w*)\s(\p{Lu}\w*)", "Hello World", RegexOptions.None, new string[] { "Hello World", "Hello", "World" } };
-            yield return new object[] { @"(\p{Lu}\p{Ll}*)\s(\p{Lu}\p{Ll}*)", "Hello World", RegexOptions.None, new string[] { "Hello World", "Hello", "World" } };
-            yield return new object[] { @"(\P{Ll}\p{Ll}*)\s(\P{Ll}\p{Ll}*)", "Hello World", RegexOptions.None, new string[] { "Hello World", "Hello", "World" } };
-            yield return new object[] { @"(\P{Lu}+\p{Lu})\s(\P{Lu}+\p{Lu})", "hellO worlD", RegexOptions.None, new string[] { "hellO worlD", "hellO", "worlD" } };
-            yield return new object[] { @"(\p{Lt}\w*)\s(\p{Lt}*\w*)", "\u01C5ello \u01C5orld", RegexOptions.None, new string[] { "\u01C5ello \u01C5orld", "\u01C5ello", "\u01C5orld" } };
-            yield return new object[] { @"(\P{Lt}\w*)\s(\P{Lt}*\w*)", "Hello World", RegexOptions.None, new string[] { "Hello World", "Hello", "World" } };
+            yield return new object[] { null, @"(\p{Lu}\w*)\s(\p{Lu}\w*)", "Hello World", RegexOptions.None, new string[] { "Hello World", "Hello", "World" } };
+            yield return new object[] { null, @"(\p{Lu}\p{Ll}*)\s(\p{Lu}\p{Ll}*)", "Hello World", RegexOptions.None, new string[] { "Hello World", "Hello", "World" } };
+            yield return new object[] { null, @"(\P{Ll}\p{Ll}*)\s(\P{Ll}\p{Ll}*)", "Hello World", RegexOptions.None, new string[] { "Hello World", "Hello", "World" } };
+            yield return new object[] { null, @"(\P{Lu}+\p{Lu})\s(\P{Lu}+\p{Lu})", "hellO worlD", RegexOptions.None, new string[] { "hellO worlD", "hellO", "worlD" } };
+            yield return new object[] { null, @"(\p{Lt}\w*)\s(\p{Lt}*\w*)", "\u01C5ello \u01C5orld", RegexOptions.None, new string[] { "\u01C5ello \u01C5orld", "\u01C5ello", "\u01C5orld" } };
+            yield return new object[] { null, @"(\P{Lt}\w*)\s(\P{Lt}*\w*)", "Hello World", RegexOptions.None, new string[] { "Hello World", "Hello", "World" } };
 
             // Character ranges IgnoreCase
-            yield return new object[] { @"[@-D]+", "eE?@ABCDabcdeE", RegexOptions.IgnoreCase, new string[] { "@ABCDabcd" } };
-            yield return new object[] { @"[>-D]+", "eE=>?@ABCDabcdeE", RegexOptions.IgnoreCase, new string[] { ">?@ABCDabcd" } };
-            yield return new object[] { @"[\u0554-\u0557]+", "\u0583\u0553\u0554\u0555\u0556\u0584\u0585\u0586\u0557\u0558", RegexOptions.IgnoreCase, new string[] { "\u0554\u0555\u0556\u0584\u0585\u0586\u0557" } };
-            yield return new object[] { @"[X-\]]+", "wWXYZxyz[\\]^", RegexOptions.IgnoreCase, new string[] { "XYZxyz[\\]" } };
-            yield return new object[] { @"[X-\u0533]+", "\u0551\u0554\u0560AXYZaxyz\u0531\u0532\u0533\u0561\u0562\u0563\u0564", RegexOptions.IgnoreCase, new string[] { "AXYZaxyz\u0531\u0532\u0533\u0561\u0562\u0563" } };
-            yield return new object[] { @"[X-a]+", "wWAXYZaxyz", RegexOptions.IgnoreCase, new string[] { "AXYZaxyz" } };
-            yield return new object[] { @"[X-c]+", "wWABCXYZabcxyz", RegexOptions.IgnoreCase, new string[] { "ABCXYZabcxyz" } };
-            yield return new object[] { @"[X-\u00C0]+", "\u00C1\u00E1\u00C0\u00E0wWABCXYZabcxyz", RegexOptions.IgnoreCase, new string[] { "\u00C0\u00E0wWABCXYZabcxyz" } };
-            yield return new object[] { @"[\u0100\u0102\u0104]+", "\u00FF \u0100\u0102\u0104\u0101\u0103\u0105\u0106", RegexOptions.IgnoreCase, new string[] { "\u0100\u0102\u0104\u0101\u0103\u0105" } };
-            yield return new object[] { @"[B-D\u0130]+", "aAeE\u0129\u0131\u0068 BCDbcD\u0130\u0069\u0070", RegexOptions.IgnoreCase, new string[] { "BCDbcD\u0130\u0069" } };
-            yield return new object[] { @"[\u013B\u013D\u013F]+", "\u013A\u013B\u013D\u013F\u013C\u013E\u0140\u0141", RegexOptions.IgnoreCase, new string[] { "\u013B\u013D\u013F\u013C\u013E\u0140" } };
+            yield return new object[] { null, @"[@-D]+", "eE?@ABCDabcdeE", RegexOptions.IgnoreCase, new string[] { "@ABCDabcd" } };
+            yield return new object[] { null, @"[>-D]+", "eE=>?@ABCDabcdeE", RegexOptions.IgnoreCase, new string[] { ">?@ABCDabcd" } };
+            yield return new object[] { null, @"[\u0554-\u0557]+", "\u0583\u0553\u0554\u0555\u0556\u0584\u0585\u0586\u0557\u0558", RegexOptions.IgnoreCase, new string[] { "\u0554\u0555\u0556\u0584\u0585\u0586\u0557" } };
+            yield return new object[] { null, @"[X-\]]+", "wWXYZxyz[\\]^", RegexOptions.IgnoreCase, new string[] { "XYZxyz[\\]" } };
+            yield return new object[] { null, @"[X-\u0533]+", "\u0551\u0554\u0560AXYZaxyz\u0531\u0532\u0533\u0561\u0562\u0563\u0564", RegexOptions.IgnoreCase, new string[] { "AXYZaxyz\u0531\u0532\u0533\u0561\u0562\u0563" } };
+            yield return new object[] { null, @"[X-a]+", "wWAXYZaxyz", RegexOptions.IgnoreCase, new string[] { "AXYZaxyz" } };
+            yield return new object[] { null, @"[X-c]+", "wWABCXYZabcxyz", RegexOptions.IgnoreCase, new string[] { "ABCXYZabcxyz" } };
+            yield return new object[] { null, @"[X-\u00C0]+", "\u00C1\u00E1\u00C0\u00E0wWABCXYZabcxyz", RegexOptions.IgnoreCase, new string[] { "\u00C0\u00E0wWABCXYZabcxyz" } };
+            yield return new object[] { null, @"[\u0100\u0102\u0104]+", "\u00FF \u0100\u0102\u0104\u0101\u0103\u0105\u0106", RegexOptions.IgnoreCase, new string[] { "\u0100\u0102\u0104\u0101\u0103\u0105" } };
+            yield return new object[] { null, @"[B-D\u0130]+", "aAeE\u0129\u0131\u0068 BCDbcD\u0130\u0069\u0070", RegexOptions.IgnoreCase, new string[] { "BCDbcD\u0130\u0069" } };
+            yield return new object[] { null, @"[\u013B\u013D\u013F]+", "\u013A\u013B\u013D\u013F\u013C\u013E\u0140\u0141", RegexOptions.IgnoreCase, new string[] { "\u013B\u013D\u013F\u013C\u013E\u0140" } };
 
             // Escape Chars
-            yield return new object[] { "(Cat)\r(Dog)", "Cat\rDog", RegexOptions.None, new string[] { "Cat\rDog", "Cat", "Dog" } };
-            yield return new object[] { "(Cat)\t(Dog)", "Cat\tDog", RegexOptions.None, new string[] { "Cat\tDog", "Cat", "Dog" } };
-            yield return new object[] { "(Cat)\f(Dog)", "Cat\fDog", RegexOptions.None, new string[] { "Cat\fDog", "Cat", "Dog" } };
+            yield return new object[] { null, "(Cat)\r(Dog)", "Cat\rDog", RegexOptions.None, new string[] { "Cat\rDog", "Cat", "Dog" } };
+            yield return new object[] { null, "(Cat)\t(Dog)", "Cat\tDog", RegexOptions.None, new string[] { "Cat\tDog", "Cat", "Dog" } };
+            yield return new object[] { null, "(Cat)\f(Dog)", "Cat\fDog", RegexOptions.None, new string[] { "Cat\fDog", "Cat", "Dog" } };
 
             // Miscellaneous { witout matching }
-            yield return new object[] { @"{5", "hello {5 world", RegexOptions.None, new string[] { "{5" } };
-            yield return new object[] { @"{5,", "hello {5, world", RegexOptions.None, new string[] { "{5," } };
-            yield return new object[] { @"{5,6", "hello {5,6 world", RegexOptions.None, new string[] { "{5,6" } };
+            yield return new object[] { null, @"{5", "hello {5 world", RegexOptions.None, new string[] { "{5" } };
+            yield return new object[] { null, @"{5,", "hello {5, world", RegexOptions.None, new string[] { "{5," } };
+            yield return new object[] { null, @"{5,6", "hello {5,6 world", RegexOptions.None, new string[] { "{5,6" } };
 
             // Miscellaneous inline options
-            yield return new object[] { @"(?n:(?<cat>cat)(\s+)(?<dog>dog))", "cat   dog", RegexOptions.None, new string[] { "cat   dog", "cat", "dog" } };
-            yield return new object[] { @"(?n:(cat)(\s+)(dog))", "cat   dog", RegexOptions.None, new string[] { "cat   dog" } };
-            yield return new object[] { @"(?n:(cat)(?<SpaceChars>\s+)(dog))", "cat   dog", RegexOptions.None, new string[] { "cat   dog", "   " } };
-            yield return new object[] { @"(?x:
+            yield return new object[] { null, @"(?n:(?<cat>cat)(\s+)(?<dog>dog))", "cat   dog", RegexOptions.None, new string[] { "cat   dog", "cat", "dog" } };
+            yield return new object[] { null, @"(?n:(cat)(\s+)(dog))", "cat   dog", RegexOptions.None, new string[] { "cat   dog" } };
+            yield return new object[] { null, @"(?n:(cat)(?<SpaceChars>\s+)(dog))", "cat   dog", RegexOptions.None, new string[] { "cat   dog", "   " } };
+            yield return new object[] { null, @"(?x:
                             (?<cat>cat) # Cat statement
                             (\s+) # Whitespace chars
                             (?<dog>dog # Dog statement
                             ))", "cat   dog", RegexOptions.None, new string[] { "cat   dog", "   ", "cat", "dog" } };
-            yield return new object[] { @"(?+i:cat)", "CAT", RegexOptions.None, new string[] { "CAT" } };
+            yield return new object[] { null, @"(?+i:cat)", "CAT", RegexOptions.None, new string[] { "CAT" } };
 
             // \d, \D, \s, \S, \w, \W, \P, \p inside character range
-            yield return new object[] { @"cat([\d]*)dog", "hello123cat230927dog1412d", RegexOptions.None, new string[] { "cat230927dog", "230927" } };
-            yield return new object[] { @"([\D]*)dog", "65498catdog58719", RegexOptions.None, new string[] { "catdog", "cat" } };
-            yield return new object[] { @"cat([\s]*)dog", "wiocat   dog3270", RegexOptions.None, new string[] { "cat   dog", "   " } };
-            yield return new object[] { @"cat([\S]*)", "sfdcatdog    3270", RegexOptions.None, new string[] { "catdog", "dog" } };
-            yield return new object[] { @"cat([\w]*)", "sfdcatdog    3270", RegexOptions.None, new string[] { "catdog", "dog" } };
-            yield return new object[] { @"cat([\W]*)dog", "wiocat   dog3270", RegexOptions.None, new string[] { "cat   dog", "   " } };
-            yield return new object[] { @"([\p{Lu}]\w*)\s([\p{Lu}]\w*)", "Hello World", RegexOptions.None, new string[] { "Hello World", "Hello", "World" } };
-            yield return new object[] { @"([\P{Ll}][\p{Ll}]*)\s([\P{Ll}][\p{Ll}]*)", "Hello World", RegexOptions.None, new string[] { "Hello World", "Hello", "World" } };
+            yield return new object[] { null, @"cat([\d]*)dog", "hello123cat230927dog1412d", RegexOptions.None, new string[] { "cat230927dog", "230927" } };
+            yield return new object[] { null, @"([\D]*)dog", "65498catdog58719", RegexOptions.None, new string[] { "catdog", "cat" } };
+            yield return new object[] { null, @"cat([\s]*)dog", "wiocat   dog3270", RegexOptions.None, new string[] { "cat   dog", "   " } };
+            yield return new object[] { null, @"cat([\S]*)", "sfdcatdog    3270", RegexOptions.None, new string[] { "catdog", "dog" } };
+            yield return new object[] { null, @"cat([\w]*)", "sfdcatdog    3270", RegexOptions.None, new string[] { "catdog", "dog" } };
+            yield return new object[] { null, @"cat([\W]*)dog", "wiocat   dog3270", RegexOptions.None, new string[] { "cat   dog", "   " } };
+            yield return new object[] { null, @"([\p{Lu}]\w*)\s([\p{Lu}]\w*)", "Hello World", RegexOptions.None, new string[] { "Hello World", "Hello", "World" } };
+            yield return new object[] { null, @"([\P{Ll}][\p{Ll}]*)\s([\P{Ll}][\p{Ll}]*)", "Hello World", RegexOptions.None, new string[] { "Hello World", "Hello", "World" } };
 
             // \x, \u, \a, \b, \e, \f, \n, \r, \t, \v, \c, inside character range
-            yield return new object[] { @"(cat)([\x41]*)(dog)", "catAAAdog", RegexOptions.None, new string[] { "catAAAdog", "cat", "AAA", "dog" } };
-            yield return new object[] { @"(cat)([\u0041]*)(dog)", "catAAAdog", RegexOptions.None, new string[] { "catAAAdog", "cat", "AAA", "dog" } };
-            yield return new object[] { @"(cat)([\a]*)(dog)", "cat\a\a\adog", RegexOptions.None, new string[] { "cat\a\a\adog", "cat", "\a\a\a", "dog" } };
-            yield return new object[] { @"(cat)([\b]*)(dog)", "cat\b\b\bdog", RegexOptions.None, new string[] { "cat\b\b\bdog", "cat", "\b\b\b", "dog" } };
-            yield return new object[] { @"(cat)([\e]*)(dog)", "cat\u001B\u001B\u001Bdog", RegexOptions.None, new string[] { "cat\u001B\u001B\u001Bdog", "cat", "\u001B\u001B\u001B", "dog" } };
-            yield return new object[] { @"(cat)([\f]*)(dog)", "cat\f\f\fdog", RegexOptions.None, new string[] { "cat\f\f\fdog", "cat", "\f\f\f", "dog" } };
-            yield return new object[] { @"(cat)([\r]*)(dog)", "cat\r\r\rdog", RegexOptions.None, new string[] { "cat\r\r\rdog", "cat", "\r\r\r", "dog" } };
-            yield return new object[] { @"(cat)([\v]*)(dog)", "cat\v\v\vdog", RegexOptions.None, new string[] { "cat\v\v\vdog", "cat", "\v\v\v", "dog" } };
+            yield return new object[] { null, @"(cat)([\x41]*)(dog)", "catAAAdog", RegexOptions.None, new string[] { "catAAAdog", "cat", "AAA", "dog" } };
+            yield return new object[] { null, @"(cat)([\u0041]*)(dog)", "catAAAdog", RegexOptions.None, new string[] { "catAAAdog", "cat", "AAA", "dog" } };
+            yield return new object[] { null, @"(cat)([\a]*)(dog)", "cat\a\a\adog", RegexOptions.None, new string[] { "cat\a\a\adog", "cat", "\a\a\a", "dog" } };
+            yield return new object[] { null, @"(cat)([\b]*)(dog)", "cat\b\b\bdog", RegexOptions.None, new string[] { "cat\b\b\bdog", "cat", "\b\b\b", "dog" } };
+            yield return new object[] { null, @"(cat)([\e]*)(dog)", "cat\u001B\u001B\u001Bdog", RegexOptions.None, new string[] { "cat\u001B\u001B\u001Bdog", "cat", "\u001B\u001B\u001B", "dog" } };
+            yield return new object[] { null, @"(cat)([\f]*)(dog)", "cat\f\f\fdog", RegexOptions.None, new string[] { "cat\f\f\fdog", "cat", "\f\f\f", "dog" } };
+            yield return new object[] { null, @"(cat)([\r]*)(dog)", "cat\r\r\rdog", RegexOptions.None, new string[] { "cat\r\r\rdog", "cat", "\r\r\r", "dog" } };
+            yield return new object[] { null, @"(cat)([\v]*)(dog)", "cat\v\v\vdog", RegexOptions.None, new string[] { "cat\v\v\vdog", "cat", "\v\v\v", "dog" } };
 
             // \d, \D, \s, \S, \w, \W, \P, \p inside character range ([0-5]) with ECMA Option
-            yield return new object[] { @"cat([\d]*)dog", "hello123cat230927dog1412d", RegexOptions.ECMAScript, new string[] { "cat230927dog", "230927" } };
-            yield return new object[] { @"([\D]*)dog", "65498catdog58719", RegexOptions.ECMAScript, new string[] { "catdog", "cat" } };
-            yield return new object[] { @"cat([\s]*)dog", "wiocat   dog3270", RegexOptions.ECMAScript, new string[] { "cat   dog", "   " } };
-            yield return new object[] { @"cat([\S]*)", "sfdcatdog    3270", RegexOptions.ECMAScript, new string[] { "catdog", "dog" } };
-            yield return new object[] { @"cat([\w]*)", "sfdcatdog    3270", RegexOptions.ECMAScript, new string[] { "catdog", "dog" } };
-            yield return new object[] { @"cat([\W]*)dog", "wiocat   dog3270", RegexOptions.ECMAScript, new string[] { "cat   dog", "   " } };
-            yield return new object[] { @"([\p{Lu}]\w*)\s([\p{Lu}]\w*)", "Hello World", RegexOptions.ECMAScript, new string[] { "Hello World", "Hello", "World" } };
-            yield return new object[] { @"([\P{Ll}][\p{Ll}]*)\s([\P{Ll}][\p{Ll}]*)", "Hello World", RegexOptions.ECMAScript, new string[] { "Hello World", "Hello", "World" } };
+            yield return new object[] { null, @"cat([\d]*)dog", "hello123cat230927dog1412d", RegexOptions.ECMAScript, new string[] { "cat230927dog", "230927" } };
+            yield return new object[] { null, @"([\D]*)dog", "65498catdog58719", RegexOptions.ECMAScript, new string[] { "catdog", "cat" } };
+            yield return new object[] { null, @"cat([\s]*)dog", "wiocat   dog3270", RegexOptions.ECMAScript, new string[] { "cat   dog", "   " } };
+            yield return new object[] { null, @"cat([\S]*)", "sfdcatdog    3270", RegexOptions.ECMAScript, new string[] { "catdog", "dog" } };
+            yield return new object[] { null, @"cat([\w]*)", "sfdcatdog    3270", RegexOptions.ECMAScript, new string[] { "catdog", "dog" } };
+            yield return new object[] { null, @"cat([\W]*)dog", "wiocat   dog3270", RegexOptions.ECMAScript, new string[] { "cat   dog", "   " } };
+            yield return new object[] { null, @"([\p{Lu}]\w*)\s([\p{Lu}]\w*)", "Hello World", RegexOptions.ECMAScript, new string[] { "Hello World", "Hello", "World" } };
+            yield return new object[] { null, @"([\P{Ll}][\p{Ll}]*)\s([\P{Ll}][\p{Ll}]*)", "Hello World", RegexOptions.ECMAScript, new string[] { "Hello World", "Hello", "World" } };
 
             // \d, \D, \s, \S, \w, \W, \P, \p outside character range ([0-5]) with ECMA Option
-            yield return new object[] { @"(cat)\d*dog", "hello123cat230927dog1412d", RegexOptions.ECMAScript, new string[] { "cat230927dog", "cat" } };
-            yield return new object[] { @"\D*(dog)", "65498catdog58719", RegexOptions.ECMAScript, new string[] { "catdog", "dog" } };
-            yield return new object[] { @"(cat)\s*(dog)", "wiocat   dog3270", RegexOptions.ECMAScript, new string[] { "cat   dog", "cat", "dog" } };
-            yield return new object[] { @"(cat)\S*", "sfdcatdog    3270", RegexOptions.ECMAScript, new string[] { "catdog", "cat" } };
-            yield return new object[] { @"(cat)\w*", "sfdcatdog    3270", RegexOptions.ECMAScript, new string[] { "catdog", "cat" } };
-            yield return new object[] { @"(cat)\W*(dog)", "wiocat   dog3270", RegexOptions.ECMAScript, new string[] { "cat   dog", "cat", "dog" } };
-            yield return new object[] { @"\p{Lu}(\w*)\s\p{Lu}(\w*)", "Hello World", RegexOptions.ECMAScript, new string[] { "Hello World", "ello", "orld" } };
-            yield return new object[] { @"\P{Ll}\p{Ll}*\s\P{Ll}\p{Ll}*", "Hello World", RegexOptions.ECMAScript, new string[] { "Hello World" } };
+            yield return new object[] { null, @"(cat)\d*dog", "hello123cat230927dog1412d", RegexOptions.ECMAScript, new string[] { "cat230927dog", "cat" } };
+            yield return new object[] { null, @"\D*(dog)", "65498catdog58719", RegexOptions.ECMAScript, new string[] { "catdog", "dog" } };
+            yield return new object[] { null, @"(cat)\s*(dog)", "wiocat   dog3270", RegexOptions.ECMAScript, new string[] { "cat   dog", "cat", "dog" } };
+            yield return new object[] { null, @"(cat)\S*", "sfdcatdog    3270", RegexOptions.ECMAScript, new string[] { "catdog", "cat" } };
+            yield return new object[] { null, @"(cat)\w*", "sfdcatdog    3270", RegexOptions.ECMAScript, new string[] { "catdog", "cat" } };
+            yield return new object[] { null, @"(cat)\W*(dog)", "wiocat   dog3270", RegexOptions.ECMAScript, new string[] { "cat   dog", "cat", "dog" } };
+            yield return new object[] { null, @"\p{Lu}(\w*)\s\p{Lu}(\w*)", "Hello World", RegexOptions.ECMAScript, new string[] { "Hello World", "ello", "orld" } };
+            yield return new object[] { null, @"\P{Ll}\p{Ll}*\s\P{Ll}\p{Ll}*", "Hello World", RegexOptions.ECMAScript, new string[] { "Hello World" } };
 
             // Use < in a group
-            yield return new object[] { @"cat(?<dog121>dog)", "catcatdogdogcat", RegexOptions.None, new string[] { "catdog", "dog" } };
-            yield return new object[] { @"(?<cat>cat)\s*(?<cat>dog)", "catcat    dogdogcat", RegexOptions.None, new string[] { "cat    dog", "dog" } };
-            yield return new object[] { @"(?<1>cat)\s*(?<1>dog)", "catcat    dogdogcat", RegexOptions.None, new string[] { "cat    dog", "dog" } };
-            yield return new object[] { @"(?<2048>cat)\s*(?<2048>dog)", "catcat    dogdogcat", RegexOptions.None, new string[] { "cat    dog", "dog" } };
-            yield return new object[] { @"(?<cat>cat)\w+(?<dog-cat>dog)", "cat_Hello_World_dog", RegexOptions.None, new string[] { "cat_Hello_World_dog", "", "_Hello_World_" } };
-            yield return new object[] { @"(?<cat>cat)\w+(?<-cat>dog)", "cat_Hello_World_dog", RegexOptions.None, new string[] { "cat_Hello_World_dog", "" } };
-            yield return new object[] { @"(?<cat>cat)\w+(?<cat-cat>dog)", "cat_Hello_World_dog", RegexOptions.None, new string[] { "cat_Hello_World_dog", "_Hello_World_" } };
-            yield return new object[] { @"(?<1>cat)\w+(?<dog-1>dog)", "cat_Hello_World_dog", RegexOptions.None, new string[] { "cat_Hello_World_dog", "", "_Hello_World_" } };
-            yield return new object[] { @"(?<cat>cat)\w+(?<2-cat>dog)", "cat_Hello_World_dog", RegexOptions.None, new string[] { "cat_Hello_World_dog", "", "_Hello_World_" } };
-            yield return new object[] { @"(?<1>cat)\w+(?<2-1>dog)", "cat_Hello_World_dog", RegexOptions.None, new string[] { "cat_Hello_World_dog", "", "_Hello_World_" } };
+            yield return new object[] { null, @"cat(?<dog121>dog)", "catcatdogdogcat", RegexOptions.None, new string[] { "catdog", "dog" } };
+            yield return new object[] { null, @"(?<cat>cat)\s*(?<cat>dog)", "catcat    dogdogcat", RegexOptions.None, new string[] { "cat    dog", "dog" } };
+            yield return new object[] { null, @"(?<1>cat)\s*(?<1>dog)", "catcat    dogdogcat", RegexOptions.None, new string[] { "cat    dog", "dog" } };
+            yield return new object[] { null, @"(?<2048>cat)\s*(?<2048>dog)", "catcat    dogdogcat", RegexOptions.None, new string[] { "cat    dog", "dog" } };
+            yield return new object[] { null, @"(?<cat>cat)\w+(?<dog-cat>dog)", "cat_Hello_World_dog", RegexOptions.None, new string[] { "cat_Hello_World_dog", "", "_Hello_World_" } };
+            yield return new object[] { null, @"(?<cat>cat)\w+(?<-cat>dog)", "cat_Hello_World_dog", RegexOptions.None, new string[] { "cat_Hello_World_dog", "" } };
+            yield return new object[] { null, @"(?<cat>cat)\w+(?<cat-cat>dog)", "cat_Hello_World_dog", RegexOptions.None, new string[] { "cat_Hello_World_dog", "_Hello_World_" } };
+            yield return new object[] { null, @"(?<1>cat)\w+(?<dog-1>dog)", "cat_Hello_World_dog", RegexOptions.None, new string[] { "cat_Hello_World_dog", "", "_Hello_World_" } };
+            yield return new object[] { null, @"(?<cat>cat)\w+(?<2-cat>dog)", "cat_Hello_World_dog", RegexOptions.None, new string[] { "cat_Hello_World_dog", "", "_Hello_World_" } };
+            yield return new object[] { null, @"(?<1>cat)\w+(?<2-1>dog)", "cat_Hello_World_dog", RegexOptions.None, new string[] { "cat_Hello_World_dog", "", "_Hello_World_" } };
 
             // Quantifiers
-            yield return new object[] { @"(?<cat>cat){", "STARTcat{", RegexOptions.None, new string[] { "cat{", "cat" } };
-            yield return new object[] { @"(?<cat>cat){fdsa", "STARTcat{fdsa", RegexOptions.None, new string[] { "cat{fdsa", "cat" } };
-            yield return new object[] { @"(?<cat>cat){1", "STARTcat{1", RegexOptions.None, new string[] { "cat{1", "cat" } };
-            yield return new object[] { @"(?<cat>cat){1END", "STARTcat{1END", RegexOptions.None, new string[] { "cat{1END", "cat" } };
-            yield return new object[] { @"(?<cat>cat){1,", "STARTcat{1,", RegexOptions.None, new string[] { "cat{1,", "cat" } };
-            yield return new object[] { @"(?<cat>cat){1,END", "STARTcat{1,END", RegexOptions.None, new string[] { "cat{1,END", "cat" } };
-            yield return new object[] { @"(?<cat>cat){1,2", "STARTcat{1,2", RegexOptions.None, new string[] { "cat{1,2", "cat" } };
-            yield return new object[] { @"(?<cat>cat){1,2END", "STARTcat{1,2END", RegexOptions.None, new string[] { "cat{1,2END", "cat" } };
+            yield return new object[] { null, @"(?<cat>cat){", "STARTcat{", RegexOptions.None, new string[] { "cat{", "cat" } };
+            yield return new object[] { null, @"(?<cat>cat){fdsa", "STARTcat{fdsa", RegexOptions.None, new string[] { "cat{fdsa", "cat" } };
+            yield return new object[] { null, @"(?<cat>cat){1", "STARTcat{1", RegexOptions.None, new string[] { "cat{1", "cat" } };
+            yield return new object[] { null, @"(?<cat>cat){1END", "STARTcat{1END", RegexOptions.None, new string[] { "cat{1END", "cat" } };
+            yield return new object[] { null, @"(?<cat>cat){1,", "STARTcat{1,", RegexOptions.None, new string[] { "cat{1,", "cat" } };
+            yield return new object[] { null, @"(?<cat>cat){1,END", "STARTcat{1,END", RegexOptions.None, new string[] { "cat{1,END", "cat" } };
+            yield return new object[] { null, @"(?<cat>cat){1,2", "STARTcat{1,2", RegexOptions.None, new string[] { "cat{1,2", "cat" } };
+            yield return new object[] { null, @"(?<cat>cat){1,2END", "STARTcat{1,2END", RegexOptions.None, new string[] { "cat{1,2END", "cat" } };
 
             // Use IgnorePatternWhitespace
-            yield return new object[] { @"(cat) #cat
+            yield return new object[] { null, @"(cat) #cat
                             \s+ #followed by 1 or more whitespace
                             (dog)  #followed by dog
                             ", "cat    dog", RegexOptions.IgnorePatternWhitespace, new string[] { "cat    dog", "cat", "dog" } };
-            yield return new object[] { @"(cat) #cat
+            yield return new object[] { null, @"(cat) #cat
                             \s+ #followed by 1 or more whitespace
                             (dog)  #followed by dog", "cat    dog", RegexOptions.IgnorePatternWhitespace, new string[] { "cat    dog", "cat", "dog" } };
-            yield return new object[] { @"(cat) (?#cat)    \s+ (?#followed by 1 or more whitespace) (dog)  (?#followed by dog)", "cat    dog", RegexOptions.IgnorePatternWhitespace, new string[] { "cat    dog", "cat", "dog" } };
+            yield return new object[] { null, @"(cat) (?#cat)    \s+ (?#followed by 1 or more whitespace) (dog)  (?#followed by dog)", "cat    dog", RegexOptions.IgnorePatternWhitespace, new string[] { "cat    dog", "cat", "dog" } };
 
             // Back Reference
-            yield return new object[] { @"(?<cat>cat)(?<dog>dog)\k<cat>", "asdfcatdogcatdog", RegexOptions.None, new string[] { "catdogcat", "cat", "dog" } };
-            yield return new object[] { @"(?<cat>cat)\s+(?<dog>dog)\k<cat>", "asdfcat   dogcat   dog", RegexOptions.None, new string[] { "cat   dogcat", "cat", "dog" } };
-            yield return new object[] { @"(?<cat>cat)\s+(?<dog>dog)\k'cat'", "asdfcat   dogcat   dog", RegexOptions.None, new string[] { "cat   dogcat", "cat", "dog" } };
-            yield return new object[] { @"(?<cat>cat)\s+(?<dog>dog)\<cat>", "asdfcat   dogcat   dog", RegexOptions.None, new string[] { "cat   dogcat", "cat", "dog" } };
-            yield return new object[] { @"(?<cat>cat)\s+(?<dog>dog)\'cat'", "asdfcat   dogcat   dog", RegexOptions.None, new string[] { "cat   dogcat", "cat", "dog" } };
-
-            yield return new object[] { @"(?<cat>cat)\s+(?<dog>dog)\k<1>", "asdfcat   dogcat   dog", RegexOptions.None, new string[] { "cat   dogcat", "cat", "dog" } };
-            yield return new object[] { @"(?<cat>cat)\s+(?<dog>dog)\k'1'", "asdfcat   dogcat   dog", RegexOptions.None, new string[] { "cat   dogcat", "cat", "dog" } };
-            yield return new object[] { @"(?<cat>cat)\s+(?<dog>dog)\<1>", "asdfcat   dogcat   dog", RegexOptions.None, new string[] { "cat   dogcat", "cat", "dog" } };
-            yield return new object[] { @"(?<cat>cat)\s+(?<dog>dog)\'1'", "asdfcat   dogcat   dog", RegexOptions.None, new string[] { "cat   dogcat", "cat", "dog" } };
-            yield return new object[] { @"(?<cat>cat)\s+(?<dog>dog)\1", "asdfcat   dogcat   dog", RegexOptions.None, new string[] { "cat   dogcat", "cat", "dog" } };
-            yield return new object[] { @"(?<cat>cat)\s+(?<dog>dog)\1", "asdfcat   dogcat   dog", RegexOptions.ECMAScript, new string[] { "cat   dogcat", "cat", "dog" } };
-
-            yield return new object[] { @"(?<cat>cat)\s+(?<dog>dog)\k<dog>", "asdfcat   dogdog   dog", RegexOptions.None, new string[] { "cat   dogdog", "cat", "dog" } };
-            yield return new object[] { @"(?<cat>cat)\s+(?<dog>dog)\2", "asdfcat   dogdog   dog", RegexOptions.None, new string[] { "cat   dogdog", "cat", "dog" } };
-            yield return new object[] { @"(?<cat>cat)\s+(?<dog>dog)\2", "asdfcat   dogdog   dog", RegexOptions.ECMAScript, new string[] { "cat   dogdog", "cat", "dog" } };
+            yield return new object[] { null, @"(?<cat>cat)(?<dog>dog)\k<cat>", "asdfcatdogcatdog", RegexOptions.None, new string[] { "catdogcat", "cat", "dog" } };
+            yield return new object[] { null, @"(?<cat>cat)\s+(?<dog>dog)\k<cat>", "asdfcat   dogcat   dog", RegexOptions.None, new string[] { "cat   dogcat", "cat", "dog" } };
+            yield return new object[] { null, @"(?<cat>cat)\s+(?<dog>dog)\k'cat'", "asdfcat   dogcat   dog", RegexOptions.None, new string[] { "cat   dogcat", "cat", "dog" } };
+            yield return new object[] { null, @"(?<cat>cat)\s+(?<dog>dog)\<cat>", "asdfcat   dogcat   dog", RegexOptions.None, new string[] { "cat   dogcat", "cat", "dog" } };
+            yield return new object[] { null, @"(?<cat>cat)\s+(?<dog>dog)\'cat'", "asdfcat   dogcat   dog", RegexOptions.None, new string[] { "cat   dogcat", "cat", "dog" } };
+
+            yield return new object[] { null, @"(?<cat>cat)\s+(?<dog>dog)\k<1>", "asdfcat   dogcat   dog", RegexOptions.None, new string[] { "cat   dogcat", "cat", "dog" } };
+            yield return new object[] { null, @"(?<cat>cat)\s+(?<dog>dog)\k'1'", "asdfcat   dogcat   dog", RegexOptions.None, new string[] { "cat   dogcat", "cat", "dog" } };
+            yield return new object[] { null, @"(?<cat>cat)\s+(?<dog>dog)\<1>", "asdfcat   dogcat   dog", RegexOptions.None, new string[] { "cat   dogcat", "cat", "dog" } };
+            yield return new object[] { null, @"(?<cat>cat)\s+(?<dog>dog)\'1'", "asdfcat   dogcat   dog", RegexOptions.None, new string[] { "cat   dogcat", "cat", "dog" } };
+            yield return new object[] { null, @"(?<cat>cat)\s+(?<dog>dog)\1", "asdfcat   dogcat   dog", RegexOptions.None, new string[] { "cat   dogcat", "cat", "dog" } };
+            yield return new object[] { null, @"(?<cat>cat)\s+(?<dog>dog)\1", "asdfcat   dogcat   dog", RegexOptions.ECMAScript, new string[] { "cat   dogcat", "cat", "dog" } };
+
+            yield return new object[] { null, @"(?<cat>cat)\s+(?<dog>dog)\k<dog>", "asdfcat   dogdog   dog", RegexOptions.None, new string[] { "cat   dogdog", "cat", "dog" } };
+            yield return new object[] { null, @"(?<cat>cat)\s+(?<dog>dog)\2", "asdfcat   dogdog   dog", RegexOptions.None, new string[] { "cat   dogdog", "cat", "dog" } };
+            yield return new object[] { null, @"(?<cat>cat)\s+(?<dog>dog)\2", "asdfcat   dogdog   dog", RegexOptions.ECMAScript, new string[] { "cat   dogdog", "cat", "dog" } };
 
             // Octal
-            yield return new object[] { @"(cat)(\077)", "hellocat?dogworld", RegexOptions.None, new string[] { "cat?", "cat", "?" } };
-            yield return new object[] { @"(cat)(\77)", "hellocat?dogworld", RegexOptions.None, new string[] { "cat?", "cat", "?" } };
-            yield return new object[] { @"(cat)(\176)", "hellocat~dogworld", RegexOptions.None, new string[] { "cat~", "cat", "~" } };
-            yield return new object[] { @"(cat)(\400)", "hellocat\0dogworld", RegexOptions.None, new string[] { "cat\0", "cat", "\0" } };
-            yield return new object[] { @"(cat)(\300)", "hellocat\u00C0dogworld", RegexOptions.None, new string[] { "cat\u00C0", "cat", "\u00C0" } };
-            yield return new object[] { @"(cat)(\300)", "hellocat\u00C0dogworld", RegexOptions.None, new string[] { "cat\u00C0", "cat", "\u00C0" } };
-            yield return new object[] { @"(cat)(\477)", "hellocat\u003Fdogworld", RegexOptions.None, new string[] { "cat\u003F", "cat", "\u003F" } };
-            yield return new object[] { @"(cat)(\777)", "hellocat\u00FFdogworld", RegexOptions.None, new string[] { "cat\u00FF", "cat", "\u00FF" } };
-            yield return new object[] { @"(cat)(\7770)", "hellocat\u00FF0dogworld", RegexOptions.None, new string[] { "cat\u00FF0", "cat", "\u00FF0" } };
-
-            yield return new object[] { @"(cat)(\077)", "hellocat?dogworld", RegexOptions.ECMAScript, new string[] { "cat?", "cat", "?" } };
-            yield return new object[] { @"(cat)(\77)", "hellocat?dogworld", RegexOptions.ECMAScript, new string[] { "cat?", "cat", "?" } };
-            yield return new object[] { @"(cat)(\7)", "hellocat\adogworld", RegexOptions.ECMAScript, new string[] { "cat\a", "cat", "\a" } };
-            yield return new object[] { @"(cat)(\40)", "hellocat dogworld", RegexOptions.ECMAScript, new string[] { "cat ", "cat", " " } };
-            yield return new object[] { @"(cat)(\040)", "hellocat dogworld", RegexOptions.ECMAScript, new string[] { "cat ", "cat", " " } };
-            yield return new object[] { @"(cat)(\176)", "hellocatcat76dogworld", RegexOptions.ECMAScript, new string[] { "catcat76", "cat", "cat76" } };
-            yield return new object[] { @"(cat)(\377)", "hellocat\u00FFdogworld", RegexOptions.ECMAScript, new string[] { "cat\u00FF", "cat", "\u00FF" } };
-            yield return new object[] { @"(cat)(\400)", "hellocat 0Fdogworld", RegexOptions.ECMAScript, new string[] { "cat 0", "cat", " 0" } };
+            yield return new object[] { null, @"(cat)(\077)", "hellocat?dogworld", RegexOptions.None, new string[] { "cat?", "cat", "?" } };
+            yield return new object[] { null, @"(cat)(\77)", "hellocat?dogworld", RegexOptions.None, new string[] { "cat?", "cat", "?" } };
+            yield return new object[] { null, @"(cat)(\176)", "hellocat~dogworld", RegexOptions.None, new string[] { "cat~", "cat", "~" } };
+            yield return new object[] { null, @"(cat)(\400)", "hellocat\0dogworld", RegexOptions.None, new string[] { "cat\0", "cat", "\0" } };
+            yield return new object[] { null, @"(cat)(\300)", "hellocat\u00C0dogworld", RegexOptions.None, new string[] { "cat\u00C0", "cat", "\u00C0" } };
+            yield return new object[] { null, @"(cat)(\300)", "hellocat\u00C0dogworld", RegexOptions.None, new string[] { "cat\u00C0", "cat", "\u00C0" } };
+            yield return new object[] { null, @"(cat)(\477)", "hellocat\u003Fdogworld", RegexOptions.None, new string[] { "cat\u003F", "cat", "\u003F" } };
+            yield return new object[] { null, @"(cat)(\777)", "hellocat\u00FFdogworld", RegexOptions.None, new string[] { "cat\u00FF", "cat", "\u00FF" } };
+            yield return new object[] { null, @"(cat)(\7770)", "hellocat\u00FF0dogworld", RegexOptions.None, new string[] { "cat\u00FF0", "cat", "\u00FF0" } };
+
+            yield return new object[] { null, @"(cat)(\077)", "hellocat?dogworld", RegexOptions.ECMAScript, new string[] { "cat?", "cat", "?" } };
+            yield return new object[] { null, @"(cat)(\77)", "hellocat?dogworld", RegexOptions.ECMAScript, new string[] { "cat?", "cat", "?" } };
+            yield return new object[] { null, @"(cat)(\7)", "hellocat\adogworld", RegexOptions.ECMAScript, new string[] { "cat\a", "cat", "\a" } };
+            yield return new object[] { null, @"(cat)(\40)", "hellocat dogworld", RegexOptions.ECMAScript, new string[] { "cat ", "cat", " " } };
+            yield return new object[] { null, @"(cat)(\040)", "hellocat dogworld", RegexOptions.ECMAScript, new string[] { "cat ", "cat", " " } };
+            yield return new object[] { null, @"(cat)(\176)", "hellocatcat76dogworld", RegexOptions.ECMAScript, new string[] { "catcat76", "cat", "cat76" } };
+            yield return new object[] { null, @"(cat)(\377)", "hellocat\u00FFdogworld", RegexOptions.ECMAScript, new string[] { "cat\u00FF", "cat", "\u00FF" } };
+            yield return new object[] { null, @"(cat)(\400)", "hellocat 0Fdogworld", RegexOptions.ECMAScript, new string[] { "cat 0", "cat", " 0" } };
 
             // Decimal
-            yield return new object[] { @"(cat)\s+(?<2147483646>dog)", "asdlkcat  dogiwod", RegexOptions.None, new string[] { "cat  dog", "cat", "dog" } };
-            yield return new object[] { @"(cat)\s+(?<2147483647>dog)", "asdlkcat  dogiwod", RegexOptions.None, new string[] { "cat  dog", "cat", "dog" } };
+            yield return new object[] { null, @"(cat)\s+(?<2147483646>dog)", "asdlkcat  dogiwod", RegexOptions.None, new string[] { "cat  dog", "cat", "dog" } };
+            yield return new object[] { null, @"(cat)\s+(?<2147483647>dog)", "asdlkcat  dogiwod", RegexOptions.None, new string[] { "cat  dog", "cat", "dog" } };
 
             // Hex
-            yield return new object[] { @"(cat)(\x2a*)(dog)", "asdlkcat***dogiwod", RegexOptions.None, new string[] { "cat***dog", "cat", "***", "dog" } };
-            yield return new object[] { @"(cat)(\x2b*)(dog)", "asdlkcat+++dogiwod", RegexOptions.None, new string[] { "cat+++dog", "cat", "+++", "dog" } };
-            yield return new object[] { @"(cat)(\x2c*)(dog)", "asdlkcat,,,dogiwod", RegexOptions.None, new string[] { "cat,,,dog", "cat", ",,,", "dog" } };
-            yield return new object[] { @"(cat)(\x2d*)(dog)", "asdlkcat---dogiwod", RegexOptions.None, new string[] { "cat---dog", "cat", "---", "dog" } };
-            yield return new object[] { @"(cat)(\x2e*)(dog)", "asdlkcat...dogiwod", RegexOptions.None, new string[] { "cat...dog", "cat", "...", "dog" } };
-            yield return new object[] { @"(cat)(\x2f*)(dog)", "asdlkcat///dogiwod", RegexOptions.None, new string[] { "cat///dog", "cat", "///", "dog" } };
-
-            yield return new object[] { @"(cat)(\x2A*)(dog)", "asdlkcat***dogiwod", RegexOptions.None, new string[] { "cat***dog", "cat", "***", "dog" } };
-            yield return new object[] { @"(cat)(\x2B*)(dog)", "asdlkcat+++dogiwod", RegexOptions.None, new string[] { "cat+++dog", "cat", "+++", "dog" } };
-            yield return new object[] { @"(cat)(\x2C*)(dog)", "asdlkcat,,,dogiwod", RegexOptions.None, new string[] { "cat,,,dog", "cat", ",,,", "dog" } };
-            yield return new object[] { @"(cat)(\x2D*)(dog)", "asdlkcat---dogiwod", RegexOptions.None, new string[] { "cat---dog", "cat", "---", "dog" } };
-            yield return new object[] { @"(cat)(\x2E*)(dog)", "asdlkcat...dogiwod", RegexOptions.None, new string[] { "cat...dog", "cat", "...", "dog" } };
-            yield return new object[] { @"(cat)(\x2F*)(dog)", "asdlkcat///dogiwod", RegexOptions.None, new string[] { "cat///dog", "cat", "///", "dog" } };
+            yield return new object[] { null, @"(cat)(\x2a*)(dog)", "asdlkcat***dogiwod", RegexOptions.None, new string[] { "cat***dog", "cat", "***", "dog" } };
+            yield return new object[] { null, @"(cat)(\x2b*)(dog)", "asdlkcat+++dogiwod", RegexOptions.None, new string[] { "cat+++dog", "cat", "+++", "dog" } };
+            yield return new object[] { null, @"(cat)(\x2c*)(dog)", "asdlkcat,,,dogiwod", RegexOptions.None, new string[] { "cat,,,dog", "cat", ",,,", "dog" } };
+            yield return new object[] { null, @"(cat)(\x2d*)(dog)", "asdlkcat---dogiwod", RegexOptions.None, new string[] { "cat---dog", "cat", "---", "dog" } };
+            yield return new object[] { null, @"(cat)(\x2e*)(dog)", "asdlkcat...dogiwod", RegexOptions.None, new string[] { "cat...dog", "cat", "...", "dog" } };
+            yield return new object[] { null, @"(cat)(\x2f*)(dog)", "asdlkcat///dogiwod", RegexOptions.None, new string[] { "cat///dog", "cat", "///", "dog" } };
+
+            yield return new object[] { null, @"(cat)(\x2A*)(dog)", "asdlkcat***dogiwod", RegexOptions.None, new string[] { "cat***dog", "cat", "***", "dog" } };
+            yield return new object[] { null, @"(cat)(\x2B*)(dog)", "asdlkcat+++dogiwod", RegexOptions.None, new string[] { "cat+++dog", "cat", "+++", "dog" } };
+            yield return new object[] { null, @"(cat)(\x2C*)(dog)", "asdlkcat,,,dogiwod", RegexOptions.None, new string[] { "cat,,,dog", "cat", ",,,", "dog" } };
+            yield return new object[] { null, @"(cat)(\x2D*)(dog)", "asdlkcat---dogiwod", RegexOptions.None, new string[] { "cat---dog", "cat", "---", "dog" } };
+            yield return new object[] { null, @"(cat)(\x2E*)(dog)", "asdlkcat...dogiwod", RegexOptions.None, new string[] { "cat...dog", "cat", "...", "dog" } };
+            yield return new object[] { null, @"(cat)(\x2F*)(dog)", "asdlkcat///dogiwod", RegexOptions.None, new string[] { "cat///dog", "cat", "///", "dog" } };
 
             // ScanControl
-            yield return new object[] { @"(cat)(\c@*)(dog)", "asdlkcat\0\0dogiwod", RegexOptions.None, new string[] { "cat\0\0dog", "cat", "\0\0", "dog" } };
-            yield return new object[] { @"(cat)(\cA*)(dog)", "asdlkcat\u0001dogiwod", RegexOptions.None, new string[] { "cat\u0001dog", "cat", "\u0001", "dog" } };
-            yield return new object[] { @"(cat)(\ca*)(dog)", "asdlkcat\u0001dogiwod", RegexOptions.None, new string[] { "cat\u0001dog", "cat", "\u0001", "dog" } };
+            yield return new object[] { null, @"(cat)(\c@*)(dog)", "asdlkcat\0\0dogiwod", RegexOptions.None, new string[] { "cat\0\0dog", "cat", "\0\0", "dog" } };
+            yield return new object[] { null, @"(cat)(\cA*)(dog)", "asdlkcat\u0001dogiwod", RegexOptions.None, new string[] { "cat\u0001dog", "cat", "\u0001", "dog" } };
+            yield return new object[] { null, @"(cat)(\ca*)(dog)", "asdlkcat\u0001dogiwod", RegexOptions.None, new string[] { "cat\u0001dog", "cat", "\u0001", "dog" } };
 
-            yield return new object[] { @"(cat)(\cC*)(dog)", "asdlkcat\u0003dogiwod", RegexOptions.None, new string[] { "cat\u0003dog", "cat", "\u0003", "dog" } };
-            yield return new object[] { @"(cat)(\cc*)(dog)", "asdlkcat\u0003dogiwod", RegexOptions.None, new string[] { "cat\u0003dog", "cat", "\u0003", "dog" } };
+            yield return new object[] { null, @"(cat)(\cC*)(dog)", "asdlkcat\u0003dogiwod", RegexOptions.None, new string[] { "cat\u0003dog", "cat", "\u0003", "dog" } };
+            yield return new object[] { null, @"(cat)(\cc*)(dog)", "asdlkcat\u0003dogiwod", RegexOptions.None, new string[] { "cat\u0003dog", "cat", "\u0003", "dog" } };
 
-            yield return new object[] { @"(cat)(\cD*)(dog)", "asdlkcat\u0004dogiwod", RegexOptions.None, new string[] { "cat\u0004dog", "cat", "\u0004", "dog" } };
-            yield return new object[] { @"(cat)(\cd*)(dog)", "asdlkcat\u0004dogiwod", RegexOptions.None, new string[] { "cat\u0004dog", "cat", "\u0004", "dog" } };
+            yield return new object[] { null, @"(cat)(\cD*)(dog)", "asdlkcat\u0004dogiwod", RegexOptions.None, new string[] { "cat\u0004dog", "cat", "\u0004", "dog" } };
+            yield return new object[] { null, @"(cat)(\cd*)(dog)", "asdlkcat\u0004dogiwod", RegexOptions.None, new string[] { "cat\u0004dog", "cat", "\u0004", "dog" } };
 
-            yield return new object[] { @"(cat)(\cX*)(dog)", "asdlkcat\u0018dogiwod", RegexOptions.None, new string[] { "cat\u0018dog", "cat", "\u0018", "dog" } };
-            yield return new object[] { @"(cat)(\cx*)(dog)", "asdlkcat\u0018dogiwod", RegexOptions.None, new string[] { "cat\u0018dog", "cat", "\u0018", "dog" } };
+            yield return new object[] { null, @"(cat)(\cX*)(dog)", "asdlkcat\u0018dogiwod", RegexOptions.None, new string[] { "cat\u0018dog", "cat", "\u0018", "dog" } };
+            yield return new object[] { null, @"(cat)(\cx*)(dog)", "asdlkcat\u0018dogiwod", RegexOptions.None, new string[] { "cat\u0018dog", "cat", "\u0018", "dog" } };
 
-            yield return new object[] { @"(cat)(\cZ*)(dog)", "asdlkcat\u001adogiwod", RegexOptions.None, new string[] { "cat\u001adog", "cat", "\u001a", "dog" } };
-            yield return new object[] { @"(cat)(\cz*)(dog)", "asdlkcat\u001adogiwod", RegexOptions.None, new string[] { "cat\u001adog", "cat", "\u001a", "dog" } };
+            yield return new object[] { null, @"(cat)(\cZ*)(dog)", "asdlkcat\u001adogiwod", RegexOptions.None, new string[] { "cat\u001adog", "cat", "\u001a", "dog" } };
+            yield return new object[] { null, @"(cat)(\cz*)(dog)", "asdlkcat\u001adogiwod", RegexOptions.None, new string[] { "cat\u001adog", "cat", "\u001a", "dog" } };
 
-            yield return new object[] { @"(cat)(\c[*)(dog)", "asdlkcat\u001bdogiwod", RegexOptions.None, new string[] { "cat\u001bdog", "cat", "\u001b", "dog" } };
-            yield return new object[] { @"(cat)(\c[*)(dog)", "asdlkcat\u001Bdogiwod", RegexOptions.None, new string[] { "cat\u001Bdog", "cat", "\u001B", "dog" } };
+            yield return new object[] { null, @"(cat)(\c[*)(dog)", "asdlkcat\u001bdogiwod", RegexOptions.None, new string[] { "cat\u001bdog", "cat", "\u001b", "dog" } };
+            yield return new object[] { null, @"(cat)(\c[*)(dog)", "asdlkcat\u001Bdogiwod", RegexOptions.None, new string[] { "cat\u001Bdog", "cat", "\u001B", "dog" } };
 
             // Atomic Zero-Width Assertions \A \Z \z \G \b \B
             //\A
-            yield return new object[] { @"\A(cat)\s+(dog)", "cat   \n\n\n   dog", RegexOptions.None, new string[] { "cat   \n\n\n   dog", "cat", "dog" } };
-            yield return new object[] { @"\A(cat)\s+(dog)", "cat   \n\n\n   dog", RegexOptions.Multiline, new string[] { "cat   \n\n\n   dog", "cat", "dog" } };
-            yield return new object[] { @"\A(cat)\s+(dog)", "cat   \n\n\n   dog", RegexOptions.ECMAScript, new string[] { "cat   \n\n\n   dog", "cat", "dog" } };
+            yield return new object[] { null, @"\A(cat)\s+(dog)", "cat   \n\n\n   dog", RegexOptions.None, new string[] { "cat   \n\n\n   dog", "cat", "dog" } };
+            yield return new object[] { null, @"\A(cat)\s+(dog)", "cat   \n\n\n   dog", RegexOptions.Multiline, new string[] { "cat   \n\n\n   dog", "cat", "dog" } };
+            yield return new object[] { null, @"\A(cat)\s+(dog)", "cat   \n\n\n   dog", RegexOptions.ECMAScript, new string[] { "cat   \n\n\n   dog", "cat", "dog" } };
 
             //\Z
-            yield return new object[] { @"(cat)\s+(dog)\Z", "cat   \n\n\n   dog", RegexOptions.None, new string[] { "cat   \n\n\n   dog", "cat", "dog" } };
-            yield return new object[] { @"(cat)\s+(dog)\Z", "cat   \n\n\n   dog", RegexOptions.Multiline, new string[] { "cat   \n\n\n   dog", "cat", "dog" } };
-            yield return new object[] { @"(cat)\s+(dog)\Z", "cat   \n\n\n   dog", RegexOptions.ECMAScript, new string[] { "cat   \n\n\n   dog", "cat", "dog" } };
-            yield return new object[] { @"(cat)\s+(dog)\Z", "cat   \n\n\n   dog\n", RegexOptions.None, new string[] { "cat   \n\n\n   dog", "cat", "dog" } };
-            yield return new object[] { @"(cat)\s+(dog)\Z", "cat   \n\n\n   dog\n", RegexOptions.Multiline, new string[] { "cat   \n\n\n   dog", "cat", "dog" } };
-            yield return new object[] { @"(cat)\s+(dog)\Z", "cat   \n\n\n   dog\n", RegexOptions.ECMAScript, new string[] { "cat   \n\n\n   dog", "cat", "dog" } };
+            yield return new object[] { null, @"(cat)\s+(dog)\Z", "cat   \n\n\n   dog", RegexOptions.None, new string[] { "cat   \n\n\n   dog", "cat", "dog" } };
+            yield return new object[] { null, @"(cat)\s+(dog)\Z", "cat   \n\n\n   dog", RegexOptions.Multiline, new string[] { "cat   \n\n\n   dog", "cat", "dog" } };
+            yield return new object[] { null, @"(cat)\s+(dog)\Z", "cat   \n\n\n   dog", RegexOptions.ECMAScript, new string[] { "cat   \n\n\n   dog", "cat", "dog" } };
+            yield return new object[] { null, @"(cat)\s+(dog)\Z", "cat   \n\n\n   dog\n", RegexOptions.None, new string[] { "cat   \n\n\n   dog", "cat", "dog" } };
+            yield return new object[] { null, @"(cat)\s+(dog)\Z", "cat   \n\n\n   dog\n", RegexOptions.Multiline, new string[] { "cat   \n\n\n   dog", "cat", "dog" } };
+            yield return new object[] { null, @"(cat)\s+(dog)\Z", "cat   \n\n\n   dog\n", RegexOptions.ECMAScript, new string[] { "cat   \n\n\n   dog", "cat", "dog" } };
 
             //\z
-            yield return new object[] { @"(cat)\s+(dog)\z", "cat   \n\n\n   dog", RegexOptions.None, new string[] { "cat   \n\n\n   dog", "cat", "dog" } };
-            yield return new object[] { @"(cat)\s+(dog)\z", "cat   \n\n\n   dog", RegexOptions.Multiline, new string[] { "cat   \n\n\n   dog", "cat", "dog" } };
-            yield return new object[] { @"(cat)\s+(dog)\z", "cat   \n\n\n   dog", RegexOptions.ECMAScript, new string[] { "cat   \n\n\n   dog", "cat", "dog" } };
+            yield return new object[] { null, @"(cat)\s+(dog)\z", "cat   \n\n\n   dog", RegexOptions.None, new string[] { "cat   \n\n\n   dog", "cat", "dog" } };
+            yield return new object[] { null, @"(cat)\s+(dog)\z", "cat   \n\n\n   dog", RegexOptions.Multiline, new string[] { "cat   \n\n\n   dog", "cat", "dog" } };
+            yield return new object[] { null, @"(cat)\s+(dog)\z", "cat   \n\n\n   dog", RegexOptions.ECMAScript, new string[] { "cat   \n\n\n   dog", "cat", "dog" } };
 
             //\b
-            yield return new object[] { @"\b@cat", "123START123@catEND", RegexOptions.None, new string[] { "@cat" } };
-            yield return new object[] { @"\b\<cat", "123START123<catEND", RegexOptions.None, new string[] { "<cat" } };
-            yield return new object[] { @"\b,cat", "satwe,,,START,catEND", RegexOptions.None, new string[] { ",cat" } };
-            yield return new object[] { @"\b\[cat", "`12START123[catEND", RegexOptions.None, new string[] { "[cat" } };
+            yield return new object[] { null, @"\b@cat", "123START123@catEND", RegexOptions.None, new string[] { "@cat" } };
+            yield return new object[] { null, @"\b\<cat", "123START123<catEND", RegexOptions.None, new string[] { "<cat" } };
+            yield return new object[] { null, @"\b,cat", "satwe,,,START,catEND", RegexOptions.None, new string[] { ",cat" } };
+            yield return new object[] { null, @"\b\[cat", "`12START123[catEND", RegexOptions.None, new string[] { "[cat" } };
 
             //\B
-            yield return new object[] { @"\B@cat", "123START123;@catEND", RegexOptions.None, new string[] { "@cat" } };
-            yield return new object[] { @"\B\<cat", "123START123'<catEND", RegexOptions.None, new string[] { "<cat" } };
-            yield return new object[] { @"\B,cat", "satwe,,,START',catEND", RegexOptions.None, new string[] { ",cat" } };
-            yield return new object[] { @"\B\[cat", "`12START123'[catEND", RegexOptions.None, new string[] { "[cat" } };
+            yield return new object[] { null, @"\B@cat", "123START123;@catEND", RegexOptions.None, new string[] { "@cat" } };
+            yield return new object[] { null, @"\B\<cat", "123START123'<catEND", RegexOptions.None, new string[] { "<cat" } };
+            yield return new object[] { null, @"\B,cat", "satwe,,,START',catEND", RegexOptions.None, new string[] { ",cat" } };
+            yield return new object[] { null, @"\B\[cat", "`12START123'[catEND", RegexOptions.None, new string[] { "[cat" } };
 
             // \w matching \p{Lm} (Letter, Modifier)
-            yield return new object[] { @"(\w+)\s+(\w+)", "cat\u02b0 dog\u02b1", RegexOptions.None, new string[] { "cat\u02b0 dog\u02b1", "cat\u02b0", "dog\u02b1" } };
-            yield return new object[] { @"(cat\w+)\s+(dog\w+)", "STARTcat\u30FC dog\u3005END", RegexOptions.None, new string[] { "cat\u30FC dog\u3005END", "cat\u30FC", "dog\u3005END" } };
-            yield return new object[] { @"(cat\w+)\s+(dog\w+)", "STARTcat\uff9e dog\uff9fEND", RegexOptions.None, new string[] { "cat\uff9e dog\uff9fEND", "cat\uff9e", "dog\uff9fEND" } };
+            yield return new object[] { null, @"(\w+)\s+(\w+)", "cat\u02b0 dog\u02b1", RegexOptions.None, new string[] { "cat\u02b0 dog\u02b1", "cat\u02b0", "dog\u02b1" } };
+            yield return new object[] { null, @"(cat\w+)\s+(dog\w+)", "STARTcat\u30FC dog\u3005END", RegexOptions.None, new string[] { "cat\u30FC dog\u3005END", "cat\u30FC", "dog\u3005END" } };
+            yield return new object[] { null, @"(cat\w+)\s+(dog\w+)", "STARTcat\uff9e dog\uff9fEND", RegexOptions.None, new string[] { "cat\uff9e dog\uff9fEND", "cat\uff9e", "dog\uff9fEND" } };
 
             // Positive and negative character classes [a-c]|[^b-c]
-            yield return new object[] { @"[^a]|d", "d", RegexOptions.None, new string[] { "d" } };
-            yield return new object[] { @"([^a]|[d])*", "Hello Worlddf", RegexOptions.None, new string[] { "Hello Worlddf", "f" } };
-            yield return new object[] { @"([^{}]|\n)+", "{{{{Hello\n World \n}END", RegexOptions.None, new string[] { "Hello\n World \n", "\n" } };
-            yield return new object[] { @"([a-d]|[^abcd])+", "\tonce\n upon\0 a- ()*&^%#time?", RegexOptions.None, new string[] { "\tonce\n upon\0 a- ()*&^%#time?", "?" } };
-            yield return new object[] { @"([^a]|[a])*", "once upon a time", RegexOptions.None, new string[] { "once upon a time", "e" } };
-            yield return new object[] { @"([a-d]|[^abcd]|[x-z]|^wxyz])+", "\tonce\n upon\0 a- ()*&^%#time?", RegexOptions.None, new string[] { "\tonce\n upon\0 a- ()*&^%#time?", "?" } };
-            yield return new object[] { @"([a-d]|[e-i]|[^e]|wxyz])+", "\tonce\n upon\0 a- ()*&^%#time?", RegexOptions.None, new string[] { "\tonce\n upon\0 a- ()*&^%#time?", "?" } };
+            yield return new object[] { null, @"[^a]|d", "d", RegexOptions.None, new string[] { "d" } };
+            yield return new object[] { null, @"([^a]|[d])*", "Hello Worlddf", RegexOptions.None, new string[] { "Hello Worlddf", "f" } };
+            yield return new object[] { null, @"([^{}]|\n)+", "{{{{Hello\n World \n}END", RegexOptions.None, new string[] { "Hello\n World \n", "\n" } };
+            yield return new object[] { null, @"([a-d]|[^abcd])+", "\tonce\n upon\0 a- ()*&^%#time?", RegexOptions.None, new string[] { "\tonce\n upon\0 a- ()*&^%#time?", "?" } };
+            yield return new object[] { null, @"([^a]|[a])*", "once upon a time", RegexOptions.None, new string[] { "once upon a time", "e" } };
+            yield return new object[] { null, @"([a-d]|[^abcd]|[x-z]|^wxyz])+", "\tonce\n upon\0 a- ()*&^%#time?", RegexOptions.None, new string[] { "\tonce\n upon\0 a- ()*&^%#time?", "?" } };
+            yield return new object[] { null, @"([a-d]|[e-i]|[^e]|wxyz])+", "\tonce\n upon\0 a- ()*&^%#time?", RegexOptions.None, new string[] { "\tonce\n upon\0 a- ()*&^%#time?", "?" } };
 
             // Canonical and noncanonical char class, where one group is in it's
             // simplest form [a-e] and another is more complex.
-            yield return new object[] { @"^(([^b]+ )|(.* ))$", "aaa ", RegexOptions.None, new string[] { "aaa ", "aaa ", "aaa ", "" } };
-            yield return new object[] { @"^(([^b]+ )|(.*))$", "aaa", RegexOptions.None, new string[] { "aaa", "aaa", "", "aaa" } };
-            yield return new object[] { @"^(([^b]+ )|(.* ))$", "bbb ", RegexOptions.None, new string[] { "bbb ", "bbb ", "", "bbb " } };
-            yield return new object[] { @"^(([^b]+ )|(.*))$", "bbb", RegexOptions.None, new string[] { "bbb", "bbb", "", "bbb" } };
-            yield return new object[] { @"^((a*)|(.*))$", "aaa", RegexOptions.None, new string[] { "aaa", "aaa", "aaa", "" } };
-            yield return new object[] { @"^((a*)|(.*))$", "aaabbb", RegexOptions.None, new string[] { "aaabbb", "aaabbb", "", "aaabbb" } };
-
-            yield return new object[] { @"(([0-9])|([a-z])|([A-Z]))*", "{hello 1234567890 world}", RegexOptions.None, new string[] { "", "", "", "", "" } };
-            yield return new object[] { @"(([0-9])|([a-z])|([A-Z]))+", "{hello 1234567890 world}", RegexOptions.None, new string[] { "hello", "o", "", "o", "" } };
-            yield return new object[] { @"(([0-9])|([a-z])|([A-Z]))*", "{HELLO 1234567890 world}", RegexOptions.None, new string[] { "", "", "", "", "" } };
-            yield return new object[] { @"(([0-9])|([a-z])|([A-Z]))+", "{HELLO 1234567890 world}", RegexOptions.None, new string[] { "HELLO", "O", "", "", "O" } };
-            yield return new object[] { @"(([0-9])|([a-z])|([A-Z]))*", "{1234567890 hello  world}", RegexOptions.None, new string[] { "", "", "", "", "" } };
-            yield return new object[] { @"(([0-9])|([a-z])|([A-Z]))+", "{1234567890 hello world}", RegexOptions.None, new string[] { "1234567890", "0", "0", "", "" } };
-
-            yield return new object[] { @"^(([a-d]*)|([a-z]*))$", "aaabbbcccdddeeefff", RegexOptions.None, new string[] { "aaabbbcccdddeeefff", "aaabbbcccdddeeefff", "", "aaabbbcccdddeeefff" } };
-            yield return new object[] { @"^(([d-f]*)|([c-e]*))$", "dddeeeccceee", RegexOptions.None, new string[] { "dddeeeccceee", "dddeeeccceee", "", "dddeeeccceee" } };
-            yield return new object[] { @"^(([c-e]*)|([d-f]*))$", "dddeeeccceee", RegexOptions.None, new string[] { "dddeeeccceee", "dddeeeccceee", "dddeeeccceee", "" } };
-
-            yield return new object[] { @"(([a-d]*)|([a-z]*))", "aaabbbcccdddeeefff", RegexOptions.None, new string[] { "aaabbbcccddd", "aaabbbcccddd", "aaabbbcccddd", "" } };
-            yield return new object[] { @"(([d-f]*)|([c-e]*))", "dddeeeccceee", RegexOptions.None, new string[] { "dddeee", "dddeee", "dddeee", "" } };
-            yield return new object[] { @"(([c-e]*)|([d-f]*))", "dddeeeccceee", RegexOptions.None, new string[] { "dddeeeccceee", "dddeeeccceee", "dddeeeccceee", "" } };
-
-            yield return new object[] { @"(([a-d]*)|(.*))", "aaabbbcccdddeeefff", RegexOptions.None, new string[] { "aaabbbcccddd", "aaabbbcccddd", "aaabbbcccddd", "" } };
-            yield return new object[] { @"(([d-f]*)|(.*))", "dddeeeccceee", RegexOptions.None, new string[] { "dddeee", "dddeee", "dddeee", "" } };
-            yield return new object[] { @"(([c-e]*)|(.*))", "dddeeeccceee", RegexOptions.None, new string[] { "dddeeeccceee", "dddeeeccceee", "dddeeeccceee", "" } };
+            yield return new object[] { null, @"^(([^b]+ )|(.* ))$", "aaa ", RegexOptions.None, new string[] { "aaa ", "aaa ", "aaa ", "" } };
+            yield return new object[] { null, @"^(([^b]+ )|(.*))$", "aaa", RegexOptions.None, new string[] { "aaa", "aaa", "", "aaa" } };
+            yield return new object[] { null, @"^(([^b]+ )|(.* ))$", "bbb ", RegexOptions.None, new string[] { "bbb ", "bbb ", "", "bbb " } };
+            yield return new object[] { null, @"^(([^b]+ )|(.*))$", "bbb", RegexOptions.None, new string[] { "bbb", "bbb", "", "bbb" } };
+            yield return new object[] { null, @"^((a*)|(.*))$", "aaa", RegexOptions.None, new string[] { "aaa", "aaa", "aaa", "" } };
+            yield return new object[] { null, @"^((a*)|(.*))$", "aaabbb", RegexOptions.None, new string[] { "aaabbb", "aaabbb", "", "aaabbb" } };
+
+            yield return new object[] { null, @"(([0-9])|([a-z])|([A-Z]))*", "{hello 1234567890 world}", RegexOptions.None, new string[] { "", "", "", "", "" } };
+            yield return new object[] { null, @"(([0-9])|([a-z])|([A-Z]))+", "{hello 1234567890 world}", RegexOptions.None, new string[] { "hello", "o", "", "o", "" } };
+            yield return new object[] { null, @"(([0-9])|([a-z])|([A-Z]))*", "{HELLO 1234567890 world}", RegexOptions.None, new string[] { "", "", "", "", "" } };
+            yield return new object[] { null, @"(([0-9])|([a-z])|([A-Z]))+", "{HELLO 1234567890 world}", RegexOptions.None, new string[] { "HELLO", "O", "", "", "O" } };
+            yield return new object[] { null, @"(([0-9])|([a-z])|([A-Z]))*", "{1234567890 hello  world}", RegexOptions.None, new string[] { "", "", "", "", "" } };
+            yield return new object[] { null, @"(([0-9])|([a-z])|([A-Z]))+", "{1234567890 hello world}", RegexOptions.None, new string[] { "1234567890", "0", "0", "", "" } };
+
+            yield return new object[] { null, @"^(([a-d]*)|([a-z]*))$", "aaabbbcccdddeeefff", RegexOptions.None, new string[] { "aaabbbcccdddeeefff", "aaabbbcccdddeeefff", "", "aaabbbcccdddeeefff" } };
+            yield return new object[] { null, @"^(([d-f]*)|([c-e]*))$", "dddeeeccceee", RegexOptions.None, new string[] { "dddeeeccceee", "dddeeeccceee", "", "dddeeeccceee" } };
+            yield return new object[] { null, @"^(([c-e]*)|([d-f]*))$", "dddeeeccceee", RegexOptions.None, new string[] { "dddeeeccceee", "dddeeeccceee", "dddeeeccceee", "" } };
+
+            yield return new object[] { null, @"(([a-d]*)|([a-z]*))", "aaabbbcccdddeeefff", RegexOptions.None, new string[] { "aaabbbcccddd", "aaabbbcccddd", "aaabbbcccddd", "" } };
+            yield return new object[] { null, @"(([d-f]*)|([c-e]*))", "dddeeeccceee", RegexOptions.None, new string[] { "dddeee", "dddeee", "dddeee", "" } };
+            yield return new object[] { null, @"(([c-e]*)|([d-f]*))", "dddeeeccceee", RegexOptions.None, new string[] { "dddeeeccceee", "dddeeeccceee", "dddeeeccceee", "" } };
+
+            yield return new object[] { null, @"(([a-d]*)|(.*))", "aaabbbcccdddeeefff", RegexOptions.None, new string[] { "aaabbbcccddd", "aaabbbcccddd", "aaabbbcccddd", "" } };
+            yield return new object[] { null, @"(([d-f]*)|(.*))", "dddeeeccceee", RegexOptions.None, new string[] { "dddeee", "dddeee", "dddeee", "" } };
+            yield return new object[] { null, @"(([c-e]*)|(.*))", "dddeeeccceee", RegexOptions.None, new string[] { "dddeeeccceee", "dddeeeccceee", "dddeeeccceee", "" } };
 
             // \p{Pi} (Punctuation Initial quote) \p{Pf} (Punctuation Final quote)
-            yield return new object[] { @"\p{Pi}(\w*)\p{Pf}", "\u00ABCat\u00BB   \u00BBDog\u00AB'", RegexOptions.None, new string[] { "\u00ABCat\u00BB", "Cat" } };
-            yield return new object[] { @"\p{Pi}(\w*)\p{Pf}", "\u2018Cat\u2019   \u2019Dog\u2018'", RegexOptions.None, new string[] { "\u2018Cat\u2019", "Cat" } };
+            yield return new object[] { null, @"\p{Pi}(\w*)\p{Pf}", "\u00ABCat\u00BB   \u00BBDog\u00AB'", RegexOptions.None, new string[] { "\u00ABCat\u00BB", "Cat" } };
+            yield return new object[] { null, @"\p{Pi}(\w*)\p{Pf}", "\u2018Cat\u2019   \u2019Dog\u2018'", RegexOptions.None, new string[] { "\u2018Cat\u2019", "Cat" } };
 
             // ECMAScript
-            yield return new object[] { @"(?<cat>cat)\s+(?<dog>dog)\s+\123\s+\234", "asdfcat   dog     cat23    dog34eia", RegexOptions.ECMAScript, new string[] { "cat   dog     cat23    dog34", "cat", "dog" } };
+            yield return new object[] { null, @"(?<cat>cat)\s+(?<dog>dog)\s+\123\s+\234", "asdfcat   dog     cat23    dog34eia", RegexOptions.ECMAScript, new string[] { "cat   dog     cat23    dog34", "cat", "dog" } };
 
             // Balanced Matching
-            yield return new object[] { @"<div>
+            yield return new object[] { null, @"<div>
             (?>
                 <div>(?<DEPTH>) |
                 </div> (?<-DEPTH>) |
@@ -490,31 +483,31 @@ public static IEnumerable<object[]> Groups_Basic_TestData()
             (?(DEPTH)(?!))
             </div>", "<div>this is some <div>red</div> text</div></div></div>", RegexOptions.IgnorePatternWhitespace, new string[] { "<div>this is some <div>red</div> text</div>", "" } };
 
-            yield return new object[] { @"(
+            yield return new object[] { null, @"(
             ((?'open'<+)[^<>]*)+
             ((?'close-open'>+)[^<>]*)+
             )+", "<01deep_01<02deep_01<03deep_01>><02deep_02><02deep_03<03deep_03>>>", RegexOptions.IgnorePatternWhitespace, new string[] { "<01deep_01<02deep_01<03deep_01>><02deep_02><02deep_03<03deep_03>>>", "<02deep_03<03deep_03>>>", "<03deep_03", ">>>", "<", "03deep_03" } };
 
-            yield return new object[] { @"(
+            yield return new object[] { null, @"(
             (?<start><)?
             [^<>]?
             (?<end-start>>)?
             )*", "<01deep_01<02deep_01<03deep_01>><02deep_02><02deep_03<03deep_03>>>", RegexOptions.IgnorePatternWhitespace, new string[] { "<01deep_01<02deep_01<03deep_01>><02deep_02><02deep_03<03deep_03>>>", "", "", "01deep_01<02deep_01<03deep_01>><02deep_02><02deep_03<03deep_03>>" } };
 
-            yield return new object[] { @"(
+            yield return new object[] { null, @"(
             (?<start><[^/<>]*>)?
             [^<>]?
             (?<end-start></[^/<>]*>)?
             )*", "<b><a>Cat</a></b>", RegexOptions.IgnorePatternWhitespace, new string[] { "<b><a>Cat</a></b>", "", "", "<a>Cat</a>" } };
 
-            yield return new object[] { @"(
+            yield return new object[] { null, @"(
             (?<start><(?<TagName>[^/<>]*)>)?
             [^<>]?
             (?<end-start></\k<TagName>>)?
             )*", "<b>cat</b><a>dog</a>", RegexOptions.IgnorePatternWhitespace, new string[] { "<b>cat</b><a>dog</a>", "", "", "a", "dog" } };
 
             // Balanced Matching With Backtracking
-            yield return new object[] { @"(
+            yield return new object[] { null, @"(
             (?<start><[^/<>]*>)?
             .?
             (?<end-start></[^/<>]*>)?
@@ -522,228 +515,158 @@ public static IEnumerable<object[]> Groups_Basic_TestData()
             (?(start)(?!)) ", "<b><a>Cat</a></b><<<<c>>>><<d><e<f>><g><<<>>>>", RegexOptions.IgnorePatternWhitespace, new string[] { "<b><a>Cat</a></b><<<<c>>>><<d><e<f>><g><<<>>>>", "", "", "<a>Cat" } };
 
             // Character Classes and Lazy quantifier
-            yield return new object[] { @"([0-9]+?)([\w]+?)", "55488aheiaheiad", RegexOptions.ECMAScript, new string[] { "55", "5", "5" } };
-            yield return new object[] { @"([0-9]+?)([a-z]+?)", "55488aheiaheiad", RegexOptions.ECMAScript, new string[] { "55488a", "55488", "a" } };
+            yield return new object[] { null, @"([0-9]+?)([\w]+?)", "55488aheiaheiad", RegexOptions.ECMAScript, new string[] { "55", "5", "5" } };
+            yield return new object[] { null, @"([0-9]+?)([a-z]+?)", "55488aheiaheiad", RegexOptions.ECMAScript, new string[] { "55488a", "55488", "a" } };
 
             // Miscellaneous/Regression scenarios
-            yield return new object[] { @"(?<openingtag>1)(?<content>.*?)(?=2)", "1" + Environment.NewLine + "<Projecaa DefaultTargets=\"x\"/>" + Environment.NewLine + "2", RegexOptions.Singleline | RegexOptions.ExplicitCapture,
+            yield return new object[] { null, @"(?<openingtag>1)(?<content>.*?)(?=2)", "1" + Environment.NewLine + "<Projecaa DefaultTargets=\"x\"/>" + Environment.NewLine + "2", RegexOptions.Singleline | RegexOptions.ExplicitCapture,
             new string[] { "1" + Environment.NewLine + "<Projecaa DefaultTargets=\"x\"/>" + Environment.NewLine, "1", Environment.NewLine + "<Projecaa DefaultTargets=\"x\"/>"+ Environment.NewLine } };
 
-            yield return new object[] { @"\G<%#(?<code>.*?)?%>", @"<%# DataBinder.Eval(this, ""MyNumber"") %>", RegexOptions.Singleline, new string[] { @"<%# DataBinder.Eval(this, ""MyNumber"") %>", @" DataBinder.Eval(this, ""MyNumber"") " } };
+            yield return new object[] { null, @"\G<%#(?<code>.*?)?%>", @"<%# DataBinder.Eval(this, ""MyNumber"") %>", RegexOptions.Singleline, new string[] { @"<%# DataBinder.Eval(this, ""MyNumber"") %>", @" DataBinder.Eval(this, ""MyNumber"") " } };
 
             // Nested Quantifiers
-            yield return new object[] { @"^[abcd]{0,0x10}*$", "a{0,0x10}}}", RegexOptions.None, new string[] { "a{0,0x10}}}" } };
+            yield return new object[] { null, @"^[abcd]{0,0x10}*$", "a{0,0x10}}}", RegexOptions.None, new string[] { "a{0,0x10}}}" } };
 
             // Lazy operator Backtracking
-            yield return new object[] { @"http://([a-zA-z0-9\-]*\.?)*?(:[0-9]*)??/", "http://www.msn.com/", RegexOptions.IgnoreCase, new string[] { "http://www.msn.com/", "com", string.Empty } };
-            yield return new object[] { @"http://([a-zA-Z0-9\-]*\.?)*?/", @"http://www.google.com/", RegexOptions.IgnoreCase, new string[] { "http://www.google.com/", "com" } };
+            yield return new object[] { null, @"http://([a-zA-z0-9\-]*\.?)*?(:[0-9]*)??/", "http://www.msn.com/", RegexOptions.IgnoreCase, new string[] { "http://www.msn.com/", "com", string.Empty } };
+            yield return new object[] { null, @"http://([a-zA-Z0-9\-]*\.?)*?/", @"http://www.google.com/", RegexOptions.IgnoreCase, new string[] { "http://www.google.com/", "com" } };
 
-            yield return new object[] { @"([a-z]*?)([\w])", "cat", RegexOptions.IgnoreCase, new string[] { "c", string.Empty, "c" } };
-            yield return new object[] { @"^([a-z]*?)([\w])$", "cat", RegexOptions.IgnoreCase, new string[] { "cat", "ca", "t" } };
+            yield return new object[] { null, @"([a-z]*?)([\w])", "cat", RegexOptions.IgnoreCase, new string[] { "c", string.Empty, "c" } };
+            yield return new object[] { null, @"^([a-z]*?)([\w])$", "cat", RegexOptions.IgnoreCase, new string[] { "cat", "ca", "t" } };
 
             // Backtracking
-            yield return new object[] { @"([a-z]*)([\w])", "cat", RegexOptions.IgnoreCase, new string[] { "cat", "ca", "t" } };
-            yield return new object[] { @"^([a-z]*)([\w])$", "cat", RegexOptions.IgnoreCase, new string[] { "cat", "ca", "t" } };
+            yield return new object[] { null, @"([a-z]*)([\w])", "cat", RegexOptions.IgnoreCase, new string[] { "cat", "ca", "t" } };
+            yield return new object[] { null, @"^([a-z]*)([\w])$", "cat", RegexOptions.IgnoreCase, new string[] { "cat", "ca", "t" } };
 
             // Quantifiers
-            yield return new object[] { @"(cat){", "cat{", RegexOptions.None, new string[] { "cat{", "cat" } };
-            yield return new object[] { @"(cat){}", "cat{}", RegexOptions.None, new string[] { "cat{}", "cat" } };
-            yield return new object[] { @"(cat){,", "cat{,", RegexOptions.None, new string[] { "cat{,", "cat" } };
-            yield return new object[] { @"(cat){,}", "cat{,}", RegexOptions.None, new string[] { "cat{,}", "cat" } };
-            yield return new object[] { @"(cat){cat}", "cat{cat}", RegexOptions.None, new string[] { "cat{cat}", "cat" } };
-            yield return new object[] { @"(cat){cat,5}", "cat{cat,5}", RegexOptions.None, new string[] { "cat{cat,5}", "cat" } };
-            yield return new object[] { @"(cat){5,dog}", "cat{5,dog}", RegexOptions.None, new string[] { "cat{5,dog}", "cat" } };
-            yield return new object[] { @"(cat){cat,dog}", "cat{cat,dog}", RegexOptions.None, new string[] { "cat{cat,dog}", "cat" } };
-            yield return new object[] { @"(cat){,}?", "cat{,}?", RegexOptions.None, new string[] { "cat{,}", "cat" } };
-            yield return new object[] { @"(cat){cat}?", "cat{cat}?", RegexOptions.None, new string[] { "cat{cat}", "cat" } };
-            yield return new object[] { @"(cat){cat,5}?", "cat{cat,5}?", RegexOptions.None, new string[] { "cat{cat,5}", "cat" } };
-            yield return new object[] { @"(cat){5,dog}?", "cat{5,dog}?", RegexOptions.None, new string[] { "cat{5,dog}", "cat" } };
-            yield return new object[] { @"(cat){cat,dog}?", "cat{cat,dog}?", RegexOptions.None, new string[] { "cat{cat,dog}", "cat" } };
+            yield return new object[] { null, @"(cat){", "cat{", RegexOptions.None, new string[] { "cat{", "cat" } };
+            yield return new object[] { null, @"(cat){}", "cat{}", RegexOptions.None, new string[] { "cat{}", "cat" } };
+            yield return new object[] { null, @"(cat){,", "cat{,", RegexOptions.None, new string[] { "cat{,", "cat" } };
+            yield return new object[] { null, @"(cat){,}", "cat{,}", RegexOptions.None, new string[] { "cat{,}", "cat" } };
+            yield return new object[] { null, @"(cat){cat}", "cat{cat}", RegexOptions.None, new string[] { "cat{cat}", "cat" } };
+            yield return new object[] { null, @"(cat){cat,5}", "cat{cat,5}", RegexOptions.None, new string[] { "cat{cat,5}", "cat" } };
+            yield return new object[] { null, @"(cat){5,dog}", "cat{5,dog}", RegexOptions.None, new string[] { "cat{5,dog}", "cat" } };
+            yield return new object[] { null, @"(cat){cat,dog}", "cat{cat,dog}", RegexOptions.None, new string[] { "cat{cat,dog}", "cat" } };
+            yield return new object[] { null, @"(cat){,}?", "cat{,}?", RegexOptions.None, new string[] { "cat{,}", "cat" } };
+            yield return new object[] { null, @"(cat){cat}?", "cat{cat}?", RegexOptions.None, new string[] { "cat{cat}", "cat" } };
+            yield return new object[] { null, @"(cat){cat,5}?", "cat{cat,5}?", RegexOptions.None, new string[] { "cat{cat,5}", "cat" } };
+            yield return new object[] { null, @"(cat){5,dog}?", "cat{5,dog}?", RegexOptions.None, new string[] { "cat{5,dog}", "cat" } };
+            yield return new object[] { null, @"(cat){cat,dog}?", "cat{cat,dog}?", RegexOptions.None, new string[] { "cat{cat,dog}", "cat" } };
 
             // Grouping Constructs Invalid Regular Expressions
-            yield return new object[] { @"()", "cat", RegexOptions.None, new string[] { string.Empty, string.Empty } };
-            yield return new object[] { @"(?<cat>)", "cat", RegexOptions.None, new string[] { string.Empty, string.Empty } };
-            yield return new object[] { @"(?'cat')", "cat", RegexOptions.None, new string[] { string.Empty, string.Empty } };
-            yield return new object[] { @"(?:)", "cat", RegexOptions.None, new string[] { string.Empty } };
-            yield return new object[] { @"(?imn)", "cat", RegexOptions.None, new string[] { string.Empty } };
-            yield return new object[] { @"(?imn)cat", "(?imn)cat", RegexOptions.None, new string[] { "cat" } };
-            yield return new object[] { @"(?=)", "cat", RegexOptions.None, new string[] { string.Empty } };
-            yield return new object[] { @"(?<=)", "cat", RegexOptions.None, new string[] { string.Empty } };
-            yield return new object[] { @"(?>)", "cat", RegexOptions.None, new string[] { string.Empty } };
+            yield return new object[] { null, @"()", "cat", RegexOptions.None, new string[] { string.Empty, string.Empty } };
+            yield return new object[] { null, @"(?<cat>)", "cat", RegexOptions.None, new string[] { string.Empty, string.Empty } };
+            yield return new object[] { null, @"(?'cat')", "cat", RegexOptions.None, new string[] { string.Empty, string.Empty } };
+            yield return new object[] { null, @"(?:)", "cat", RegexOptions.None, new string[] { string.Empty } };
+            yield return new object[] { null, @"(?imn)", "cat", RegexOptions.None, new string[] { string.Empty } };
+            yield return new object[] { null, @"(?imn)cat", "(?imn)cat", RegexOptions.None, new string[] { "cat" } };
+            yield return new object[] { null, @"(?=)", "cat", RegexOptions.None, new string[] { string.Empty } };
+            yield return new object[] { null, @"(?<=)", "cat", RegexOptions.None, new string[] { string.Empty } };
+            yield return new object[] { null, @"(?>)", "cat", RegexOptions.None, new string[] { string.Empty } };
 
             // Alternation construct Invalid Regular Expressions
-            yield return new object[] { @"(?()|)", "(?()|)", RegexOptions.None, new string[] { "" } };
+            yield return new object[] { null, @"(?()|)", "(?()|)", RegexOptions.None, new string[] { "" } };
 
-            yield return new object[] { @"(?(cat)|)", "cat", RegexOptions.None, new string[] { "" } };
-            yield return new object[] { @"(?(cat)|)", "dog", RegexOptions.None, new string[] { "" } };
+            yield return new object[] { null, @"(?(cat)|)", "cat", RegexOptions.None, new string[] { "" } };
+            yield return new object[] { null, @"(?(cat)|)", "dog", RegexOptions.None, new string[] { "" } };
 
-            yield return new object[] { @"(?(cat)catdog|)", "catdog", RegexOptions.None, new string[] { "catdog" } };
-            yield return new object[] { @"(?(cat)catdog|)", "dog", RegexOptions.None, new string[] { "" } };
-            yield return new object[] { @"(?(cat)dog|)", "dog", RegexOptions.None, new string[] { "" } };
-            yield return new object[] { @"(?(cat)dog|)", "cat", RegexOptions.None, new string[] { "" } };
+            yield return new object[] { null, @"(?(cat)catdog|)", "catdog", RegexOptions.None, new string[] { "catdog" } };
+            yield return new object[] { null, @"(?(cat)catdog|)", "dog", RegexOptions.None, new string[] { "" } };
+            yield return new object[] { null, @"(?(cat)dog|)", "dog", RegexOptions.None, new string[] { "" } };
+            yield return new object[] { null, @"(?(cat)dog|)", "cat", RegexOptions.None, new string[] { "" } };
 
-            yield return new object[] { @"(?(cat)|catdog)", "cat", RegexOptions.None, new string[] { "" } };
-            yield return new object[] { @"(?(cat)|catdog)", "catdog", RegexOptions.None, new string[] { "" } };
-            yield return new object[] { @"(?(cat)|dog)", "dog", RegexOptions.None, new string[] { "dog" } };
+            yield return new object[] { null, @"(?(cat)|catdog)", "cat", RegexOptions.None, new string[] { "" } };
+            yield return new object[] { null, @"(?(cat)|catdog)", "catdog", RegexOptions.None, new string[] { "" } };
+            yield return new object[] { null, @"(?(cat)|dog)", "dog", RegexOptions.None, new string[] { "dog" } };
 
             // Invalid unicode
-            yield return new object[] { "([\u0000-\uFFFF-[azAZ09]]|[\u0000-\uFFFF-[^azAZ09]])+", "azAZBCDE1234567890BCDEFAZza", RegexOptions.None, new string[] { "azAZBCDE1234567890BCDEFAZza", "a" } };
-            yield return new object[] { "[\u0000-\uFFFF-[\u0000-\uFFFF-[\u0000-\uFFFF-[\u0000-\uFFFF-[\u0000-\uFFFF-[a]]]]]]+", "abcxyzABCXYZ123890", RegexOptions.None, new string[] { "bcxyzABCXYZ123890" } };
-            yield return new object[] { "[\u0000-\uFFFF-[\u0000-\uFFFF-[\u0000-\uFFFF-[\u0000-\uFFFF-[\u0000-\uFFFF-[\u0000-\uFFFF-[a]]]]]]]+", "bcxyzABCXYZ123890a", RegexOptions.None, new string[] { "a" } };
-            yield return new object[] { "[\u0000-\uFFFF-[\\p{P}\\p{S}\\p{C}]]+", "!@`';.,$+<>=\x0001\x001FazAZ09", RegexOptions.None, new string[] { "azAZ09" } };
+            yield return new object[] { null, "([\u0000-\uFFFF-[azAZ09]]|[\u0000-\uFFFF-[^azAZ09]])+", "azAZBCDE1234567890BCDEFAZza", RegexOptions.None, new string[] { "azAZBCDE1234567890BCDEFAZza", "a" } };
+            yield return new object[] { null, "[\u0000-\uFFFF-[\u0000-\uFFFF-[\u0000-\uFFFF-[\u0000-\uFFFF-[\u0000-\uFFFF-[a]]]]]]+", "abcxyzABCXYZ123890", RegexOptions.None, new string[] { "bcxyzABCXYZ123890" } };
+            yield return new object[] { null, "[\u0000-\uFFFF-[\u0000-\uFFFF-[\u0000-\uFFFF-[\u0000-\uFFFF-[\u0000-\uFFFF-[\u0000-\uFFFF-[a]]]]]]]+", "bcxyzABCXYZ123890a", RegexOptions.None, new string[] { "a" } };
+            yield return new object[] { null, "[\u0000-\uFFFF-[\\p{P}\\p{S}\\p{C}]]+", "!@`';.,$+<>=\x0001\x001FazAZ09", RegexOptions.None, new string[] { "azAZ09" } };
 
-            yield return new object[] { @"[\uFFFD-\uFFFF]+", "\uFFFC\uFFFD\uFFFE\uFFFF", RegexOptions.IgnoreCase, new string[] { "\uFFFD\uFFFE\uFFFF" } };
-            yield return new object[] { @"[\uFFFC-\uFFFE]+", "\uFFFB\uFFFC\uFFFD\uFFFE\uFFFF", RegexOptions.IgnoreCase, new string[] { "\uFFFC\uFFFD\uFFFE" } };
+            yield return new object[] { null, @"[\uFFFD-\uFFFF]+", "\uFFFC\uFFFD\uFFFE\uFFFF", RegexOptions.IgnoreCase, new string[] { "\uFFFD\uFFFE\uFFFF" } };
+            yield return new object[] { null, @"[\uFFFC-\uFFFE]+", "\uFFFB\uFFFC\uFFFD\uFFFE\uFFFF", RegexOptions.IgnoreCase, new string[] { "\uFFFC\uFFFD\uFFFE" } };
 
             // Empty Match
-            yield return new object[] { @"([a*]*)+?$", "ab", RegexOptions.None, new string[] { "", "" } };
-            yield return new object[] { @"(a*)+?$", "b", RegexOptions.None, new string[] { "", "" } };
+            yield return new object[] { null, @"([a*]*)+?$", "ab", RegexOptions.None, new string[] { "", "" } };
+            yield return new object[] { null, @"(a*)+?$", "b", RegexOptions.None, new string[] { "", "" } };
         }
 
         public static IEnumerable<object[]> Groups_CustomCulture_TestData_enUS()
         {
-            yield return new object[] { "CH", "Ch", RegexOptions.IgnoreCase, new string[] { "Ch" } };
-            yield return new object[] { "cH", "Ch", RegexOptions.IgnoreCase, new string[] { "Ch" } };
-            yield return new object[] { "AA", "Aa", RegexOptions.IgnoreCase, new string[] { "Aa" } };
-            yield return new object[] { "aA", "Aa", RegexOptions.IgnoreCase, new string[] { "Aa" } };
-            yield return new object[] { "\u0130", "\u0049", RegexOptions.IgnoreCase, new string[] { "\u0049" } };
-            yield return new object[] { "\u0130", "\u0069", RegexOptions.IgnoreCase, new string[] { "\u0069" } };
+            yield return new object[] { "en-US", "CH", "Ch", RegexOptions.IgnoreCase, new string[] { "Ch" } };
+            yield return new object[] { "en-US", "cH", "Ch", RegexOptions.IgnoreCase, new string[] { "Ch" } };
+            yield return new object[] { "en-US", "AA", "Aa", RegexOptions.IgnoreCase, new string[] { "Aa" } };
+            yield return new object[] { "en-US", "aA", "Aa", RegexOptions.IgnoreCase, new string[] { "Aa" } };
+            yield return new object[] { "en-US", "\u0130", "\u0049", RegexOptions.IgnoreCase, new string[] { "\u0049" } };
+            yield return new object[] { "en-US", "\u0130", "\u0069", RegexOptions.IgnoreCase, new string[] { "\u0069" } };
         }
 
         public static IEnumerable<object[]> Groups_CustomCulture_TestData_Czech()
         {
-            yield return new object[] { "CH", "Ch", RegexOptions.IgnoreCase, new string[] { "Ch" } };
-            yield return new object[] { "cH", "Ch", RegexOptions.IgnoreCase, new string[] { "Ch" } };
+            yield return new object[] { "cs-CZ", "CH", "Ch", RegexOptions.IgnoreCase, new string[] { "Ch" } };
+            yield return new object[] { "cs-CZ", "cH", "Ch", RegexOptions.IgnoreCase, new string[] { "Ch" } };
         }
 
 
         public static IEnumerable<object[]> Groups_CustomCulture_TestData_Danish()
         {
-            yield return new object[] { "AA", "Aa", RegexOptions.IgnoreCase, new string[] { "Aa" } };
-            yield return new object[] { "aA", "Aa", RegexOptions.IgnoreCase, new string[] { "Aa" } };
+            yield return new object[] { "da-DK", "AA", "Aa", RegexOptions.IgnoreCase, new string[] { "Aa" } };
+            yield return new object[] { "da-DK", "aA", "Aa", RegexOptions.IgnoreCase, new string[] { "Aa" } };
         }
 
         public static IEnumerable<object[]> Groups_CustomCulture_TestData_Turkish()
         {
-            yield return new object[] { "\u0131", "\u0049", RegexOptions.IgnoreCase, new string[] { "\u0049" } };
-            yield return new object[] { "\u0130", "\u0069", RegexOptions.IgnoreCase, new string[] { "\u0069" } };
+            yield return new object[] { "tr-TR", "\u0131", "\u0049", RegexOptions.IgnoreCase, new string[] { "\u0049" } };
+            yield return new object[] { "tr-TR", "\u0130", "\u0069", RegexOptions.IgnoreCase, new string[] { "\u0069" } };
         }
 
         public static IEnumerable<object[]> Groups_CustomCulture_TestData_AzeriLatin()
         {
-            yield return new object[] { "\u0131", "\u0049", RegexOptions.IgnoreCase, new string[] { "\u0049" } };
-            yield return new object[] { "\u0130", "\u0069", RegexOptions.IgnoreCase, new string[] { "\u0069" } };
+            yield return new object[] { "az-Latn-AZ", "\u0131", "\u0049", RegexOptions.IgnoreCase, new string[] { "\u0049" } };
+            yield return new object[] { "az-Latn-AZ", "\u0130", "\u0069", RegexOptions.IgnoreCase, new string[] { "\u0069" } };
         }
 
-        private static CultureInfo GetDefaultCultureForTests()
+        [Theory]
+        [MemberData(nameof(Groups_Basic_TestData))]
+        [MemberData(nameof(Groups_CustomCulture_TestData_enUS))]
+        [MemberData(nameof(Groups_CustomCulture_TestData_Czech))]
+        [MemberData(nameof(Groups_CustomCulture_TestData_Danish))]
+        [MemberData(nameof(Groups_CustomCulture_TestData_Turkish))]
+        [MemberData(nameof(Groups_CustomCulture_TestData_AzeriLatin))]
+        public void Groups(string cultureName, string pattern, string input, RegexOptions options, string[] expectedGroups)
         {
-            CultureInfo defaultCulture = CultureInfo.CurrentCulture;
-
-            // In invariant culture, the unicode char matches differ from expected values provided.
-            if (defaultCulture.Equals(CultureInfo.InvariantCulture))
+            if (cultureName is null)
             {
-                defaultCulture = new CultureInfo("en-US");
+                CultureInfo culture = CultureInfo.CurrentCulture;
+                cultureName = culture.Equals(CultureInfo.InvariantCulture) ? "en-US" : culture.Name;
             }
 
-            return defaultCulture;
-        }
-
-        private static void Groups(string pattern, string input, RegexOptions options, string[] expectedGroups)
-        {
-            Regex regex = new Regex(pattern, options);
-            Match match = regex.Match(input);
-            Assert.True(match.Success, $"match.Success. pattern=/{pattern}/  input=[[[{input}]]]  culture={CultureInfo.CurrentCulture.Name}");
-
-            Assert.Equal(expectedGroups.Length, match.Groups.Count);
-            Assert.True(expectedGroups[0] == match.Value, string.Format("Culture used: {0}", CultureInfo.CurrentCulture));
-
-            int[] groupNumbers = regex.GetGroupNumbers();
-            string[] groupNames = regex.GetGroupNames();
-            for (int i = 0; i < expectedGroups.Length; i++)
+            using (new ThreadCultureChange(cultureName))
             {
-                Assert.Equal(expectedGroups[i], match.Groups[groupNumbers[i]].Value);
-                Assert.Equal(match.Groups[groupNumbers[i]], match.Groups[groupNames[i]]);
-
-                Assert.Equal(groupNumbers[i], regex.GroupNumberFromName(groupNames[i]));
-                Assert.Equal(groupNames[i], regex.GroupNameFromNumber(groupNumbers[i]));
+                Groups(pattern, input, options, expectedGroups);
+                Groups(pattern, input, RegexOptions.Compiled | options, expectedGroups);
             }
-        }
-
-        private void GroupsTest(object[] testCase)
-        {
-            Groups((string)testCase[0], (string)testCase[1], (RegexOptions)testCase[2], (string[])testCase[3]);
-            Groups((string)testCase[0], (string)testCase[1], RegexOptions.Compiled | (RegexOptions)testCase[2], (string[])testCase[3]);
-        }
-
 
-        [Fact]
-        public void GroupsEnUS()
-        {
-            using (new ThreadCultureChange(s_enUSCulture))
+            static void Groups(string pattern, string input, RegexOptions options, string[] expectedGroups)
             {
-                foreach (object[] testCase in Groups_CustomCulture_TestData_enUS())
-                {
-                    GroupsTest(testCase);
-                }
-            }
-        }
+                Regex regex = new Regex(pattern, options);
+                Match match = regex.Match(input);
+                Assert.True(match.Success);
 
-        [Fact]
-        public void GroupsCzech()
-        {
-            using (new ThreadCultureChange(s_czechCulture))
-            {
-                foreach (object[] testCase in Groups_CustomCulture_TestData_Czech())
-                {
-                    GroupsTest(testCase);
-                }
-            }
-        }
+                Assert.Equal(expectedGroups.Length, match.Groups.Count);
+                Assert.Equal(expectedGroups[0], match.Value);
 
-        [Fact]
-        public void GroupsDanish()
-        {
-            using (new ThreadCultureChange(s_danishCulture))
-            {
-                foreach (object[] testCase in Groups_CustomCulture_TestData_Danish())
-                {
-                    GroupsTest(testCase);
-                }
-            }
-        }
-
-        [Fact]
-        public void GroupsTurkish()
-        {
-            using (new ThreadCultureChange(s_turkishCulture))
-            {
-                foreach (object[] testCase in Groups_CustomCulture_TestData_Turkish())
-                {
-                    GroupsTest(testCase);
-                }
-            }
-        }
-
-        [Fact]
-        public void GroupsAzeriLatin()
-        {
-            using (new ThreadCultureChange(s_azeriLatinCulture))
-            {
-                foreach (object[] testCase in Groups_CustomCulture_TestData_AzeriLatin())
+                int[] groupNumbers = regex.GetGroupNumbers();
+                string[] groupNames = regex.GetGroupNames();
+                for (int i = 0; i < expectedGroups.Length; i++)
                 {
-                    GroupsTest(testCase);
-                }
-            }
-        }
+                    Assert.Equal(expectedGroups[i], match.Groups[groupNumbers[i]].Value);
+                    Assert.Equal(match.Groups[groupNumbers[i]], match.Groups[groupNames[i]]);
 
-        [Fact]
-        public void GroupsBasic()
-        {
-            using (new ThreadCultureChange(GetDefaultCultureForTests()))
-            {
-                foreach (object[] testCase in Groups_Basic_TestData())
-                {
-                    GroupsTest(testCase);
+                    Assert.Equal(groupNumbers[i], regex.GroupNumberFromName(groupNames[i]));
+                    Assert.Equal(groupNames[i], regex.GroupNameFromNumber(groupNumbers[i]));
                 }
             }
         }

From be1840f3e1ed7d39b156ebcad3fd41ffa248b8a1 Mon Sep 17 00:00:00 2001
From: Stephen Toub <stoub@microsoft.com>
Date: Fri, 22 Nov 2019 11:49:11 -0500
Subject: [PATCH 19/21] Make low-hanging fruit allocation reduction

e.g. avoiding allocating a range list if a class contains only categories, avoiding some intermediary strings, avoiding some delegate allocations for sorting, etc.
---
 .../System/Text/RegularExpressions/Regex.cs   |  20 +-
 .../Text/RegularExpressions/RegexCharClass.cs | 273 +++++++++---------
 .../Text/RegularExpressions/RegexFCD.cs       |   6 +-
 .../Text/RegularExpressions/RegexNode.cs      |   2 +-
 .../Text/RegularExpressions/RegexParser.cs    |   2 +-
 .../RegularExpressions/RegexReplacement.cs    |   9 +-
 6 files changed, 151 insertions(+), 161 deletions(-)

diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.cs
index e62d17cd68c59d..e8741fc636c58f 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.cs
@@ -305,18 +305,15 @@ public string[] GetGroupNames()
 
             if (capslist == null)
             {
-                int max = capsize;
-                result = new string[max];
-
-                for (int i = 0; i < max; i++)
+                result = new string[capsize];
+                for (int i = 0; i < result.Length; i++)
                 {
-                    result[i] = Convert.ToString(i, CultureInfo.InvariantCulture);
+                    result[i] = i.ToString();
                 }
             }
             else
             {
-                result = new string[capslist.Length];
-                Array.Copy(capslist, result, capslist.Length);
+                result = capslist.AsSpan().ToArray();
             }
 
             return result;
@@ -375,7 +372,7 @@ public string GroupNameFromNumber(int i)
             if (capslist == null)
             {
                 if (i >= 0 && i < capsize)
-                    return i.ToString(CultureInfo.InvariantCulture);
+                    return i.ToString();
 
                 return string.Empty;
             }
@@ -469,10 +466,9 @@ protected void InitializeReferences()
             if (runner == null)
             {
                 // Use the compiled RegexRunner factory if the code was compiled to MSIL
-                if (factory != null)
-                    runner = factory.CreateInstance();
-                else
-                    runner = new RegexInterpreter(_code!, UseOptionInvariant() ? CultureInfo.InvariantCulture : CultureInfo.CurrentCulture);
+                runner = factory != null ?
+                    factory.CreateInstance() :
+                    new RegexInterpreter(_code!, UseOptionInvariant() ? CultureInfo.InvariantCulture : CultureInfo.CurrentCulture);
             }
 
             Match? match;
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
index feccd94c951b30..5476d45f7b57f8 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
@@ -396,9 +396,9 @@ these intervals. It follows from the nature of the L on each interval.
             new LowerCaseMapping('\uFF21', '\uFF3A', LowercaseAdd, 32),
         };
 
-        private readonly List<SingleRange> _rangelist;
+        private List<SingleRange>? _rangelist;
         private StringBuilder? _categories;
-        private bool _canonical;
+        private bool _canonical = true;
         private bool _negate;
         private RegexCharClass? _subtractor;
 
@@ -425,15 +425,12 @@ static RegexCharClass()
         /// </summary>
         public RegexCharClass()
         {
-            _rangelist = new List<SingleRange>(6);
-            _canonical = true;
         }
 
-        private RegexCharClass(bool negate, List<SingleRange> ranges, StringBuilder categories, RegexCharClass? subtraction)
+        private RegexCharClass(bool negate, List<SingleRange>? ranges, StringBuilder? categories, RegexCharClass? subtraction)
         {
             _rangelist = ranges;
             _categories = categories;
-            _canonical = true;
             _negate = negate;
             _subtractor = subtraction;
         }
@@ -461,21 +458,22 @@ public void AddChar(char c)
         /// </summary>
         public void AddCharClass(RegexCharClass cc)
         {
-            int i;
-
             Debug.Assert(cc.CanMerge && CanMerge, "Both character classes added together must be able to merge");
 
-            if (!cc._canonical)
+            int ccRangeCount = cc._rangelist?.Count ?? 0;
+
+            if (!cc._canonical || // if the new char class to add isn't canonical, we're not either.
+                (_canonical &&
+                 ccRangeCount > 0 &&
+                 _rangelist != null && _rangelist.Count > 0 &&
+                 cc._rangelist![0].First <= _rangelist[^1].Last))
             {
-                // if the new char class to add isn't canonical, we're not either.
                 _canonical = false;
             }
-            else if (_canonical && RangeCount() > 0 && cc.RangeCount() > 0 && cc.GetRangeAt(0).First <= GetRangeAt(RangeCount() - 1).Last)
-                _canonical = false;
 
-            for (i = 0; i < cc.RangeCount(); i += 1)
+            if (ccRangeCount != 0)
             {
-                _rangelist.Add(cc.GetRangeAt(i));
+                EnsureRangeList().AddRange(cc._rangelist!);
             }
 
             if (cc._categories != null)
@@ -487,25 +485,35 @@ public void AddCharClass(RegexCharClass cc)
         private StringBuilder EnsureCategories() =>
             _categories ??= new StringBuilder();
 
+        private List<SingleRange> EnsureRangeList() =>
+            _rangelist ??= new List<SingleRange>(6);
+
         /// <summary>
         /// Adds a set (specified by its string representation) to the class.
         /// </summary>
-        private void AddSet(string set)
+        private void AddSet(ReadOnlySpan<char> set)
         {
-            int i;
+            if (set.Length == 0)
+            {
+                return;
+            }
+
+            List<SingleRange> rangeList = EnsureRangeList();
 
-            if (_canonical && RangeCount() > 0 && set.Length > 0 &&
-                set[0] <= GetRangeAt(RangeCount() - 1).Last)
+            if (_canonical && rangeList.Count > 0 && set[0] <= rangeList[^1].Last)
+            {
                 _canonical = false;
+            }
 
+            int i;
             for (i = 0; i < set.Length - 1; i += 2)
             {
-                _rangelist.Add(new SingleRange(set[i], (char)(set[i + 1] - 1)));
+                rangeList.Add(new SingleRange(set[i], (char)(set[i + 1] - 1)));
             }
 
             if (i < set.Length)
             {
-                _rangelist.Add(new SingleRange(set[i], LastChar));
+                rangeList.Add(new SingleRange(set[i], LastChar));
             }
         }
 
@@ -520,7 +528,7 @@ public void AddSubtraction(RegexCharClass sub)
         /// </summary>
         public void AddRange(char first, char last)
         {
-            _rangelist.Add(new SingleRange(first, last));
+            EnsureRangeList().Add(new SingleRange(first, last));
             if (_canonical && first <= last)
             {
                 _canonical = false;
@@ -575,18 +583,22 @@ public void AddLowercase(CultureInfo culture)
         {
             _canonical = false;
 
-            int count = _rangelist.Count;
-            for (int i = 0; i < count; i++)
+            List<SingleRange>? rangeList = _rangelist;
+            if (rangeList != null)
             {
-                SingleRange range = _rangelist[i];
-                if (range.First == range.Last)
-                {
-                    char lower = culture.TextInfo.ToLower(range.First);
-                    _rangelist[i] = new SingleRange(lower, lower);
-                }
-                else
+                int count = rangeList.Count;
+                for (int i = 0; i < count; i++)
                 {
-                    AddLowercaseRange(range.First, range.Last, culture);
+                    SingleRange range = rangeList[i];
+                    if (range.First == range.Last)
+                    {
+                        char lower = culture.TextInfo.ToLower(range.First);
+                        rangeList[i] = new SingleRange(lower, lower);
+                    }
+                    else
+                    {
+                        AddLowercaseRange(range.First, range.Last, culture);
+                    }
                 }
             }
         }
@@ -598,8 +610,6 @@ public void AddLowercase(CultureInfo culture)
         private void AddLowercaseRange(char chMin, char chMax, CultureInfo culture)
         {
             int i, iMax, iMid;
-            char chMinT, chMaxT;
-            LowerCaseMapping lc;
 
             for (i = 0, iMax = s_lcTable.Length; i < iMax;)
             {
@@ -613,6 +623,9 @@ private void AddLowercaseRange(char chMin, char chMax, CultureInfo culture)
             if (i >= s_lcTable.Length)
                 return;
 
+            char chMinT, chMaxT;
+            LowerCaseMapping lc;
+
             for (; i < s_lcTable.Length && (lc = s_lcTable[i]).ChMin <= chMax; i++)
             {
                 if ((chMinT = lc.ChMin) < chMin)
@@ -1023,95 +1036,96 @@ private static RegexCharClass ParseRecursive(string charClass, int start)
             int myCategoryLength = charClass[start + CATEGORYLENGTH];
             int myEndPosition = start + SETSTART + mySetLength + myCategoryLength;
 
-            List<SingleRange> ranges = new List<SingleRange>(mySetLength);
             int i = start + SETSTART;
             int end = i + mySetLength;
-            while (i < end)
+
+            List<SingleRange>? ranges = null;
+            if (mySetLength > 0)
             {
-                char first = charClass[i];
-                i++;
+                ranges = new List<SingleRange>(mySetLength);
+                while (i < end)
+                {
+                    char first = charClass[i];
+                    i++;
 
-                char last;
-                if (i < end)
-                    last = (char)(charClass[i] - 1);
-                else
-                    last = LastChar;
-                i++;
-                ranges.Add(new SingleRange(first, last));
+                    char last = i < end ?
+                        (char)(charClass[i] - 1) :
+                        LastChar;
+                    i++;
+
+                    ranges.Add(new SingleRange(first, last));
+                }
             }
 
             RegexCharClass? sub = null;
             if (charClass.Length > myEndPosition)
+            {
                 sub = ParseRecursive(charClass, myEndPosition);
+            }
 
-            return new RegexCharClass(charClass[start + FLAGS] == 1, ranges, new StringBuilder(charClass.Substring(end, myCategoryLength)), sub);
-        }
+            StringBuilder? categoriesBuilder = null;
+            if (myCategoryLength > 0)
+            {
+                categoriesBuilder = new StringBuilder();
+                categoriesBuilder.Append(charClass.AsSpan(end, myCategoryLength));
+            }
 
-        /// <summary>
-        /// The number of single ranges that have been accumulated so far.
-        /// </summary>
-        private int RangeCount()
-        {
-            return _rangelist.Count;
+            return new RegexCharClass(charClass[start + FLAGS] == 1, ranges, categoriesBuilder, sub);
         }
 
         /// <summary>
         /// Constructs the string representation of the class.
         /// </summary>
         public string ToStringClass()
+        {
+            var vsb = new ValueStringBuilder(stackalloc char[256]);
+            ToStringClass(ref vsb);
+            return vsb.ToString();
+        }
+
+        private void ToStringClass(ref ValueStringBuilder vsb)
         {
             if (!_canonical)
+            {
                 Canonicalize();
+            }
 
-            // make a guess about the length of the ranges.  We'll update this at the end.
-            // This is important because if the last range ends in LastChar, we won't append
-            // LastChar to the list.
+            int initialLength = vsb.Length;
             int categoriesLength = _categories?.Length ?? 0;
-            int rangeLen = _rangelist.Count * 2;
-            int strGuessCount = rangeLen + categoriesLength + 3;
-
-            ValueStringBuilder vsb = strGuessCount <= 256 ?
-                new ValueStringBuilder(stackalloc char[256]) :
-                new ValueStringBuilder(strGuessCount);
-
-            int flags = _negate ? 1 : 0;
+            vsb.Append((char)(_negate ? 1 : 0)); // FLAGS
+            vsb.Append('\0'); // SETLENGTH (will be replaced once we know how long a range we've added)
+            vsb.Append((char)categoriesLength); // CATEGORYLENGTH
 
-            vsb.Append((char)flags);
-            vsb.Append((char)rangeLen);
-            vsb.Append((char)categoriesLength);
-
-            for (int i = 0; i < _rangelist.Count; i++)
+            // Append ranges
+            List<SingleRange>? rangelist = _rangelist;
+            if (rangelist != null)
             {
-                SingleRange currentRange = _rangelist[i];
-                vsb.Append(currentRange.First);
-
-                if (currentRange.Last != LastChar)
-                    vsb.Append((char)(currentRange.Last + 1));
+                for (int i = 0; i < rangelist.Count; i++)
+                {
+                    SingleRange currentRange = rangelist[i];
+                    vsb.Append(currentRange.First);
+                    if (currentRange.Last != LastChar)
+                    {
+                        vsb.Append((char)(currentRange.Last + 1));
+                    }
+                }
             }
 
-            vsb[SETLENGTH] = (char)(vsb.Length - SETSTART);
+            // Update the range length.  The ValueStringBuilder may have already had some
+            // contents (if this is a subtactor), so we need to offset by the initial length.
+            vsb[initialLength + SETLENGTH] = (char)((vsb.Length - initialLength) - SETSTART);
 
-            // Append the categories string
-            if (_categories != null)
+            // Append categories
+            if (categoriesLength != 0)
             {
-                foreach (ReadOnlyMemory<char> chunk in _categories.GetChunks())
+                foreach (ReadOnlyMemory<char> chunk in _categories!.GetChunks())
                 {
                     vsb.Append(chunk.Span);
                 }
             }
 
-            if (_subtractor != null)
-                vsb.Append(_subtractor.ToStringClass());
-
-            return vsb.ToString();
-        }
-
-        /// <summary>
-        /// The ith range.
-        /// </summary>
-        private SingleRange GetRangeAt(int i)
-        {
-            return _rangelist[i];
+            // Append a subtractor if there is one.
+            _subtractor?.ToStringClass(ref vsb);
         }
 
         /// <summary>
@@ -1119,41 +1133,40 @@ private SingleRange GetRangeAt(int i)
         /// </summary>
         private void Canonicalize()
         {
-            SingleRange CurrentRange;
-            int i;
-            int j;
-            char last;
-            bool done;
-
             _canonical = true;
-            _rangelist.Sort(SingleRangeComparer.Instance);
 
-            //
-            // Find and eliminate overlapping or abutting ranges
-            //
-
-            if (_rangelist.Count > 1)
+            List<SingleRange>? rangelist = _rangelist;
+            if (rangelist != null && rangelist.Count > 1)
             {
-                done = false;
+                rangelist.Sort((x, y) => x.First.CompareTo(y.First));
+
+                //
+                // Find and eliminate overlapping or abutting ranges
+                //
+
+                bool done = false;
+                int i, j;
 
                 for (i = 1, j = 0; ; i++)
                 {
-                    for (last = _rangelist[j].Last; ; i++)
+                    char last;
+                    for (last = rangelist[j].Last; ; i++)
                     {
-                        if (i == _rangelist.Count || last == LastChar)
+                        if (i == rangelist.Count || last == LastChar)
                         {
                             done = true;
                             break;
                         }
 
-                        if ((CurrentRange = _rangelist[i]).First > last + 1)
+                        SingleRange currentRange;
+                        if ((currentRange = rangelist[i]).First > last + 1)
                             break;
 
-                        if (last < CurrentRange.Last)
-                            last = CurrentRange.Last;
+                        if (last < currentRange.Last)
+                            last = currentRange.Last;
                     }
 
-                    _rangelist[j] = new SingleRange(_rangelist[j].First, last);
+                    rangelist[j] = new SingleRange(rangelist[j].First, last);
 
                     j++;
 
@@ -1161,13 +1174,14 @@ private void Canonicalize()
                         break;
 
                     if (j < i)
-                        _rangelist[j] = _rangelist[i];
+                        rangelist[j] = rangelist[i];
                 }
-                _rangelist.RemoveRange(j, _rangelist.Count - j);
+
+                rangelist.RemoveRange(j, rangelist.Count - j);
             }
         }
 
-        private static string SetFromProperty(string capname, bool invert, string pattern, int currentPos)
+        private static ReadOnlySpan<char> SetFromProperty(string capname, bool invert, string pattern, int currentPos)
         {
             int min = 0;
             int max = s_propTable.Length;
@@ -1176,25 +1190,21 @@ private static string SetFromProperty(string capname, bool invert, string patter
                 int mid = (min + max) / 2;
                 int res = string.Compare(capname, s_propTable[mid][0], StringComparison.Ordinal);
                 if (res < 0)
+                {
                     max = mid;
+                }
                 else if (res > 0)
+                {
                     min = mid + 1;
+                }
                 else
                 {
                     string set = s_propTable[mid][1];
                     Debug.Assert(!string.IsNullOrEmpty(set), "Found a null/empty element in RegexCharClass prop table");
-                    if (invert)
-                    {
-                        if (set[0] == NullChar)
-                        {
-                            return set.Substring(1);
-                        }
-                        return NullCharString + set;
-                    }
-                    else
-                    {
-                        return set;
-                    }
+                    return
+                        !invert ? set :
+                        set[0] == NullChar ? set.AsSpan(1) :
+                        NullCharString + set;
                 }
             }
 
@@ -1385,23 +1395,6 @@ internal LowerCaseMapping(char chMin, char chMax, int lcOp, int data)
             }
         }
 
-        /// <summary>
-        /// For sorting ranges; compare based on the first char in the range.
-        /// </summary>
-        private sealed class SingleRangeComparer : IComparer<SingleRange>
-        {
-            public static readonly SingleRangeComparer Instance = new SingleRangeComparer();
-
-            private SingleRangeComparer()
-            {
-            }
-
-            public int Compare(SingleRange x, SingleRange y)
-            {
-                return x.First.CompareTo(y.First);
-            }
-        }
-
         /// <summary>
         /// A first/last pair representing a single range of characters.
         /// </summary>
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexFCD.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexFCD.cs
index 1dd3e27db5ea36..234af27f67b31e 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexFCD.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexFCD.cs
@@ -108,11 +108,11 @@ public static RegexPrefix Prefix(RegexTree tree)
 
                         if (curNode.M > 0 && curNode.M < Cutoff)
                         {
-                            string pref = string.Empty.PadRight(curNode.M, curNode.Ch);
+                            string pref = new string(curNode.Ch, curNode.M);
                             return new RegexPrefix(pref, 0 != (curNode.Options & RegexOptions.IgnoreCase));
                         }
-                        else
-                            return RegexPrefix.Empty;
+
+                        return RegexPrefix.Empty;
 
                     case RegexNode.One:
                         return new RegexPrefix(curNode.Ch.ToString(), 0 != (curNode.Options & RegexOptions.IgnoreCase));
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexNode.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexNode.cs
index 35ca944b7e65c7..f22fa884f0acb9 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexNode.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexNode.cs
@@ -476,7 +476,7 @@ private RegexNode ReduceConcatenation()
                     if (prev.NType == One)
                     {
                         prev.NType = Multi;
-                        prev.Str = Convert.ToString(prev.Ch, CultureInfo.InvariantCulture);
+                        prev.Str = prev.Ch.ToString();
                     }
 
                     if ((optionsAt & RegexOptions.RightToLeft) == 0)
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs
index 59e38d0b1945e7..b64bcf4c5e4cfc 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs
@@ -1849,7 +1849,7 @@ private void AssignNameSlots()
                     }
                     else
                     {
-                        string str = Convert.ToString(j, _culture);
+                        string str = j.ToString(_culture);
                         _capnamelist.Add(str);
                         _capnames[str] = j;
                     }
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexReplacement.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexReplacement.cs
index 6dd81aa687e228..e299bc9642734b 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexReplacement.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexReplacement.cs
@@ -33,9 +33,10 @@ public RegexReplacement(string rep, RegexNode concat, Hashtable _caps)
             if (concat.Type() != RegexNode.Concatenate)
                 throw new ArgumentException(SR.ReplacementError);
 
-            ValueStringBuilder vsb = new ValueStringBuilder(stackalloc char[256]);
-            List<string> strings = new List<string>();
-            List<int> rules = new List<int>();
+            Span<char> vsbStack = stackalloc char[256];
+            var vsb = new ValueStringBuilder(vsbStack);
+            var strings = new List<string>();
+            var rules = new List<int>();
 
             for (int i = 0; i < concat.ChildCount(); i++)
             {
@@ -56,7 +57,7 @@ public RegexReplacement(string rep, RegexNode concat, Hashtable _caps)
                         {
                             rules.Add(strings.Count);
                             strings.Add(vsb.ToString());
-                            vsb.Length = 0;
+                            vsb = new ValueStringBuilder(vsbStack);
                         }
                         int slot = child.M;
 

From d2f1a69abd90021790ff0d87b506a791c9f5d816 Mon Sep 17 00:00:00 2001
From: Stephen Toub <stoub@microsoft.com>
Date: Mon, 25 Nov 2019 22:48:15 -0500
Subject: [PATCH 20/21] Use String.IndexOf in RegexParser.Unescape

---
 .../System/Text/RegularExpressions/RegexParser.cs   | 13 ++++---------
 1 file changed, 4 insertions(+), 9 deletions(-)

diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs
index b64bcf4c5e4cfc..ed87d0ae9be8e6 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs
@@ -183,15 +183,10 @@ private static string EscapeImpl(string input, int i)
         /// </summary>
         public static string Unescape(string input)
         {
-            for (int i = 0; i < input.Length; i++)
-            {
-                if (input[i] == '\\')
-                {
-                    return UnescapeImpl(input, i);
-                }
-            }
-
-            return input;
+            int i = input.IndexOf('\\');
+            return i >= 0 ?
+                UnescapeImpl(input, i) :
+                input;
         }
 
         private static string UnescapeImpl(string input, int i)

From 0fedf21e2e4261a5490ea3f7d20c3a8ff7b4d015 Mon Sep 17 00:00:00 2001
From: Stephen Toub <stoub@microsoft.com>
Date: Mon, 25 Nov 2019 18:01:20 -0500
Subject: [PATCH 21/21] More code cleanup

---
 .../RegularExpressions/CaptureCollection.cs   |   9 +-
 .../System/Text/RegularExpressions/Group.cs   |  11 +-
 .../RegularExpressions/GroupCollection.cs     |   1 +
 .../System/Text/RegularExpressions/Match.cs   |  45 +-
 .../System/Text/RegularExpressions/Regex.cs   |  40 +-
 .../Text/RegularExpressions/RegexCharClass.cs | 520 +++++-----
 .../RegexCompilationInfo.cs                   |   1 -
 .../Text/RegularExpressions/RegexCompiler.cs  |  21 +-
 .../Text/RegularExpressions/RegexFCD.cs       |  68 +-
 .../Text/RegularExpressions/RegexNode.cs      |   5 +-
 .../Text/RegularExpressions/RegexParser.cs    | 897 +++++++++---------
 .../Text/RegularExpressions/RegexPrefix.cs    |   4 +-
 .../Text/RegularExpressions/RegexRunner.cs    |   5 +-
 .../tests/RegexGroupNameTests.cs              |   3 +
 14 files changed, 755 insertions(+), 875 deletions(-)

diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/CaptureCollection.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/CaptureCollection.cs
index ba5cc128249eab..6f4413bd88168a 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/CaptureCollection.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/CaptureCollection.cs
@@ -11,11 +11,10 @@
 
 namespace System.Text.RegularExpressions
 {
-    /*
-     * This collection returns the Captures for a group
-     * in the order in which they were matched (left to right
-     * or right to left). It is created by Group.Captures
-     */
+    // This collection returns the Captures for a group
+    // in the order in which they were matched (left to right
+    // or right to left). It is created by Group.Captures.
+
     /// <summary>
     /// Represents a sequence of capture substrings. The object is used
     /// to return the set of captures done by a single capturing group.
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Group.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Group.cs
index 48d3f4f7bdf5d4..54d6f23f03828b 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Group.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Group.cs
@@ -42,16 +42,7 @@ internal Group(string text, int[] caps, int capcount, string name)
         /// group, in innermost-leftmost-first order (or innermost-rightmost-first order if
         /// compiled with the "r" option). The collection may have zero or more items.
         /// </summary>
-        public CaptureCollection Captures
-        {
-            get
-            {
-                if (_capcoll == null)
-                    _capcoll = new CaptureCollection(this);
-
-                return _capcoll;
-            }
-        }
+        public CaptureCollection Captures => _capcoll ??= new CaptureCollection(this);
 
         /// <summary>
         /// Returns a Group object equivalent to the one supplied that is safe to share between
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/GroupCollection.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/GroupCollection.cs
index 41e86b4022be66..a3b4920c077a11 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/GroupCollection.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/GroupCollection.cs
@@ -221,6 +221,7 @@ public bool TryGetValue(string key, [NotNullWhen(true)] out Group? value)
                 value = null;
                 return false;
             }
+
             value = group;
             return true;
         }
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Match.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Match.cs
index 3e84da0e7e87a2..9c68d11ba6d157 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Match.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Match.cs
@@ -89,16 +89,7 @@ internal void Reset(Regex regex, string text, int textbeg, int textend, int text
             _balancing = false;
         }
 
-        public virtual GroupCollection Groups
-        {
-            get
-            {
-                if (_groupcoll == null)
-                    _groupcoll = new GroupCollection(this, null);
-
-                return _groupcoll;
-            }
-        }
+        public virtual GroupCollection Groups => _groupcoll ??= new GroupCollection(this, null);
 
         /// <summary>
         /// Returns a new Match with the results for the next match, starting
@@ -127,12 +118,9 @@ public virtual string Result(string replacement)
                 throw new NotSupportedException(SR.NoResultOnFailed);
 
             // Gets the weakly cached replacement helper or creates one if there isn't one already.
-            RegexReplacement repl = RegexReplacement.GetOrCreate(_regex._replref!, replacement, _regex.caps!, _regex.capsize,
-                _regex.capnames!, _regex.roptions);
+            RegexReplacement repl = RegexReplacement.GetOrCreate(_regex._replref!, replacement, _regex.caps!, _regex.capsize, _regex.capnames!, _regex.roptions);
             var vsb = new ValueStringBuilder(stackalloc char[ReplaceBufferSize]);
-
             repl.ReplacementImpl(ref vsb, this);
-
             return vsb.ToString();
         }
 
@@ -181,12 +169,9 @@ public static Match Synchronized(Match inner)
         /// </summary>
         internal void AddMatch(int cap, int start, int len)
         {
-            int capcount;
-
-            if (_matches[cap] == null)
-                _matches[cap] = new int[2];
+            _matches[cap] ??= new int[2];
 
-            capcount = _matchcount[cap];
+            int capcount = _matchcount[cap];
 
             if (capcount * 2 + 2 > _matches[cap].Length)
             {
@@ -333,16 +318,7 @@ internal void Tidy(int textpos)
         }
 
 #if DEBUG
-        internal bool Debug
-        {
-            get
-            {
-                if (_regex == null)
-                    return false;
-
-                return _regex.Debug;
-            }
-        }
+        internal bool Debug => _regex != null && _regex.Debug;
 
         internal virtual void Dump()
         {
@@ -380,16 +356,7 @@ internal MatchSparse(Regex regex, Hashtable caps, int capcount, string text, int
             _caps = caps;
         }
 
-        public override GroupCollection Groups
-        {
-            get
-            {
-                if (_groupcoll == null)
-                    _groupcoll = new GroupCollection(this, _caps);
-
-                return _groupcoll;
-            }
-        }
+        public override GroupCollection Groups => _groupcoll ??= new GroupCollection(this, _caps);
 
 #if DEBUG
         internal override void Dump()
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.cs
index e8741fc636c58f..0d9b0e2941cb46 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Regex.cs
@@ -187,10 +187,7 @@ void ISerializable.GetObjectData(SerializationInfo si, StreamingContext context)
         [CLSCompliant(false), DisallowNull]
         protected IDictionary? Caps
         {
-            get
-            {
-                return caps;
-            }
+            get => caps;
             set
             {
                 if (value == null)
@@ -203,10 +200,7 @@ protected IDictionary? Caps
         [CLSCompliant(false), DisallowNull]
         protected IDictionary? CapNames
         {
-            get
-            {
-                return capnames;
-            }
+            get => capnames;
             set
             {
                 if (value == null)
@@ -222,7 +216,7 @@ protected IDictionary? CapNames
         /// Regex constructor, we don't load RegexCompiler and its reflection classes when
         /// instantiating a non-compiled regex.
         /// </summary>
-        [MethodImplAttribute(MethodImplOptions.NoInlining)]
+        [MethodImpl(MethodImplOptions.NoInlining)]
         private RegexRunnerFactory Compile(RegexCode code, RegexOptions roptions, bool hasTimeout)
         {
             return RegexCompiler.Compile(code!, roptions, hasTimeout);
@@ -403,38 +397,32 @@ public string GroupNameFromNumber(int i)
         /// </summary>
         public int GroupNumberFromName(string name)
         {
-            int result = -1;
-
             if (name == null)
                 throw new ArgumentNullException(nameof(name));
 
+            int result;
+
             // look up name if we have a hashtable of names
             if (capnames != null)
             {
-                if (!capnames.TryGetValue(name, out result))
-                    return -1;
-
-                return result;
+                return capnames.TryGetValue(name, out result) ? result : -1;
             }
 
             // convert to an int if it looks like a number
             result = 0;
             for (int i = 0; i < name.Length; i++)
             {
-                char ch = name[i];
-
-                if (ch > '9' || ch < '0')
+                uint digit = (uint)(name[i] - '0');
+                if (digit > 9)
+                {
                     return -1;
+                }
 
-                result *= 10;
-                result += (ch - '0');
+                result = (result * 10) + (int)digit;
             }
 
             // return int if it's in range
-            if (result >= 0 && result < capsize)
-                return result;
-
-            return -1;
+            return result >= 0 && result < capsize ? result : -1;
         }
 
         protected void InitializeReferences()
@@ -492,9 +480,7 @@ protected void InitializeReferences()
 
         protected bool UseOptionC() => (roptions & RegexOptions.Compiled) != 0;
 
-        /*
-         * True if the L option was set
-         */
+        /// <summary>True if the L option was set</summary>
         protected internal bool UseOptionR() => (roptions & RegexOptions.RightToLeft) != 0;
 
         internal bool UseOptionInvariant() => (roptions & RegexOptions.CultureInvariant) != 0;
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
index 5476d45f7b57f8..26e44966008311 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
@@ -2,37 +2,35 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-// This RegexCharClass class provides the "set of Unicode chars" functionality
-// used by the regexp engine.
-
-// The main function of RegexCharClass is as a builder to turn ranges, characters and
-// Unicode categories into a single string.  This string is used as a black box
-// representation of a character class by the rest of Regex.  The format is as follows.
-//
-// Char index   Use
-//      0       Flags - currently this only holds the "negate" flag
-//      1       length of the string representing the "set" portion, e.g. [a-z0-9] only has a "set"
-//      2       length of the string representing the "category" portion, e.g. [\p{Lu}] only has a "category"
-//      3...m   The set.  These are a series of ranges which define the characters included in the set.
-//              To determine if a given character is in the set, we binary search over this set of ranges
-//              and see where the character should go.  Based on whether the ending index is odd or even,
-//              we know if the character is in the set.
-//      m+1...n The categories.  This is a list of UnicodeCategory enum values which describe categories
-//              included in this class.
-
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Globalization;
 
 namespace System.Text.RegularExpressions
 {
+    // The main function of RegexCharClass is as a builder to turn ranges, characters and
+    // Unicode categories into a single string.  This string is used as a black box
+    // representation of a character class by the rest of Regex.  The format is as follows.
+    //
+    // Char index   Use
+    //      0       Flags - currently this only holds the "negate" flag
+    //      1       length of the string representing the "set" portion, e.g. [a-z0-9] only has a "set"
+    //      2       length of the string representing the "category" portion, e.g. [\p{Lu}] only has a "category"
+    //      3...m   The set.  These are a series of ranges which define the characters included in the set.
+    //              To determine if a given character is in the set, we binary search over this set of ranges
+    //              and see where the character should go.  Based on whether the ending index is odd or even,
+    //              we know if the character is in the set.
+    //      m+1...n The categories.  This is a list of UnicodeCategory enum values which describe categories
+    //              included in this class.
+
+    /// <summary>Provides the "set of Unicode chars" functionality used by the regexp engine.</summary>
     internal sealed class RegexCharClass
     {
         // Constants
-        internal const int FLAGS = 0;
-        internal const int SETLENGTH = 1;
-        internal const int CATEGORYLENGTH = 2;
-        internal const int SETSTART = 3;
+        internal const int FlagsIndex = 0;
+        internal const int SetLengthIndex = 1;
+        internal const int CategoryLengthIndex = 2;
+        internal const int SetStartIndex = 3; // must be odd for subsequent logic to work
 
         private const string NullCharString = "\0";
         private const char NullChar = '\0';
@@ -398,9 +396,9 @@ these intervals. It follows from the nature of the L on each interval.
 
         private List<SingleRange>? _rangelist;
         private StringBuilder? _categories;
+        private RegexCharClass? _subtractor;
         private bool _canonical = true;
         private bool _negate;
-        private RegexCharClass? _subtractor;
 
 #if DEBUG
         static RegexCharClass()
@@ -435,23 +433,14 @@ private RegexCharClass(bool negate, List<SingleRange>? ranges, StringBuilder? ca
             _subtractor = subtraction;
         }
 
-        public bool CanMerge
-        {
-            get
-            {
-                return !_negate && _subtractor == null;
-            }
-        }
+        public bool CanMerge => !_negate && _subtractor == null;
 
         public bool Negate
         {
             set { _negate = value; }
         }
 
-        public void AddChar(char c)
-        {
-            AddRange(c, c);
-        }
+        public void AddChar(char c) => AddRange(c, c);
 
         /// <summary>
         /// Adds a regex char class
@@ -540,13 +529,10 @@ public void AddCategoryFromName(string categoryName, bool invert, bool caseInsen
             if (s_definedCategories.TryGetValue(categoryName, out string? category) &&
                 !categoryName.Equals(InternalRegexIgnoreCase))
             {
-                if (caseInsensitive)
+                if (caseInsensitive && (categoryName.Equals("Ll") || categoryName.Equals("Lu") || categoryName.Equals("Lt")))
                 {
-                    if (categoryName.Equals("Ll") || categoryName.Equals("Lu") || categoryName.Equals("Lt"))
-                    {
-                        // when RegexOptions.IgnoreCase is specified then {Ll}, {Lu}, and {Lt} cases should all match
-                        category = s_definedCategories[InternalRegexIgnoreCase];
-                    }
+                    // when RegexOptions.IgnoreCase is specified then {Ll}, {Lu}, and {Lt} cases should all match
+                    category = s_definedCategories[InternalRegexIgnoreCase];
                 }
 
                 StringBuilder categories = EnsureCategories();
@@ -556,7 +542,7 @@ public void AddCategoryFromName(string categoryName, bool invert, bool caseInsen
                     for (int i = 0; i < category.Length; i++)
                     {
                         short ch = (short)category[i];
-                        categories.Append(unchecked((char)-ch));
+                        categories.Append((char)-ch);
                     }
                 }
                 else
@@ -570,10 +556,7 @@ public void AddCategoryFromName(string categoryName, bool invert, bool caseInsen
             }
         }
 
-        private void AddCategory(string category)
-        {
-            EnsureCategories().Append(category);
-        }
+        private void AddCategory(string category) => EnsureCategories().Append(category);
 
         /// <summary>
         /// Adds to the class any lowercase versions of characters already
@@ -597,7 +580,7 @@ public void AddLowercase(CultureInfo culture)
                     }
                     else
                     {
-                        AddLowercaseRange(range.First, range.Last, culture);
+                        AddLowercaseRange(range.First, range.Last);
                     }
                 }
             }
@@ -607,21 +590,27 @@ public void AddLowercase(CultureInfo culture)
         /// For a single range that's in the set, adds any additional ranges
         /// necessary to ensure that lowercase equivalents are also included.
         /// </summary>
-        private void AddLowercaseRange(char chMin, char chMax, CultureInfo culture)
+        private void AddLowercaseRange(char chMin, char chMax)
         {
-            int i, iMax, iMid;
+            int i = 0;
 
-            for (i = 0, iMax = s_lcTable.Length; i < iMax;)
+            for (int iMax = s_lcTable.Length; i < iMax; )
             {
-                iMid = (i + iMax) / 2;
+                int iMid = (i + iMax) >> 1;
                 if (s_lcTable[iMid].ChMax < chMin)
+                {
                     i = iMid + 1;
+                }
                 else
+                {
                     iMax = iMid;
+                }
             }
 
             if (i >= s_lcTable.Length)
+            {
                 return;
+            }
 
             char chMinT, chMaxT;
             LowerCaseMapping lc;
@@ -629,10 +618,14 @@ private void AddLowercaseRange(char chMin, char chMax, CultureInfo culture)
             for (; i < s_lcTable.Length && (lc = s_lcTable[i]).ChMin <= chMax; i++)
             {
                 if ((chMinT = lc.ChMin) < chMin)
+                {
                     chMinT = chMin;
+                }
 
                 if ((chMaxT = lc.ChMax) > chMax)
+                {
                     chMaxT = chMax;
+                }
 
                 switch (lc.LcOp)
                 {
@@ -640,17 +633,17 @@ private void AddLowercaseRange(char chMin, char chMax, CultureInfo culture)
                         chMinT = (char)lc.Data;
                         chMaxT = (char)lc.Data;
                         break;
+
                     case LowercaseAdd:
-                        unchecked
-                        {
-                            chMinT += (char)lc.Data;
-                            chMaxT += (char)lc.Data;
-                        }
+                        chMinT += (char)lc.Data;
+                        chMaxT += (char)lc.Data;
                         break;
+
                     case LowercaseBor:
                         chMinT |= (char)1;
                         chMaxT |= (char)1;
                         break;
+
                     case LowercaseBad:
                         chMinT += (char)(chMinT & 1);
                         chMaxT += (char)(chMaxT & 1);
@@ -658,43 +651,33 @@ private void AddLowercaseRange(char chMin, char chMax, CultureInfo culture)
                 }
 
                 if (chMinT < chMin || chMaxT > chMax)
+                {
                     AddRange(chMinT, chMaxT);
+                }
             }
         }
 
         public void AddWord(bool ecma, bool negate)
         {
-            if (negate)
+            if (ecma)
             {
-                if (ecma)
-                    AddSet(NotECMAWordSet);
-                else
-                    AddCategory(NotWord);
+                AddSet(negate ? NotECMAWordSet : ECMAWordSet);
             }
             else
             {
-                if (ecma)
-                    AddSet(ECMAWordSet);
-                else
-                    AddCategory(Word);
+                AddCategory(negate ? NotWord : Word);
             }
         }
 
         public void AddSpace(bool ecma, bool negate)
         {
-            if (negate)
+            if (ecma)
             {
-                if (ecma)
-                    AddSet(NotECMASpaceSet);
-                else
-                    AddCategory(NotSpace);
+                AddSet(negate ? NotECMASpaceSet : ECMASpaceSet);
             }
             else
             {
-                if (ecma)
-                    AddSet(ECMASpaceSet);
-                else
-                    AddCategory(Space);
+                AddCategory(negate ? NotSpace : Space);
             }
         }
 
@@ -702,21 +685,22 @@ public void AddDigit(bool ecma, bool negate, string pattern, int currentPos)
         {
             if (ecma)
             {
-                if (negate)
-                    AddSet(NotECMADigitSet);
-                else
-                    AddSet(ECMADigitSet);
+                AddSet(negate ? NotECMADigitSet : ECMADigitSet);
             }
             else
-                AddCategoryFromName("Nd", negate, false, pattern, currentPos);
+            {
+                AddCategoryFromName("Nd", negate, caseInsensitive: false, pattern, currentPos);
+            }
         }
 
         public static string ConvertOldStringsToClass(string set, string category)
         {
             bool startsWithNulls = set.Length >= 2 && set[0] == '\0' && set[1] == '\0';
-            int strLength = set.Length + category.Length + 3;
+            int strLength = SetStartIndex + set.Length + category.Length;
             if (startsWithNulls)
+            {
                 strLength -= 2;
+            }
 
             return string.Create(strLength, (set, category, startsWithNulls), (span, state) =>
             {
@@ -724,19 +708,19 @@ public static string ConvertOldStringsToClass(string set, string category)
 
                 if (state.startsWithNulls)
                 {
-                    span[0] = (char)0x1;
-                    span[1] = (char)(state.set.Length - 2);
-                    span[2] = (char)state.category.Length;
-                    state.set.AsSpan(2).CopyTo(span.Slice(3));
-                    index = 3 + state.set.Length - 2;
+                    span[FlagsIndex] = (char)0x1;
+                    span[SetLengthIndex] = (char)(state.set.Length - 2);
+                    span[CategoryLengthIndex] = (char)state.category.Length;
+                    state.set.AsSpan(2).CopyTo(span.Slice(SetStartIndex));
+                    index = SetStartIndex + state.set.Length - 2;
                 }
                 else
                 {
-                    span[0] = (char)0x0;
-                    span[1] = (char)state.set.Length;
-                    span[2] = (char)state.category.Length;
-                    state.set.AsSpan().CopyTo(span.Slice(3));
-                    index = 3 + state.set.Length;
+                    span[FlagsIndex] = '\0';
+                    span[SetLengthIndex] = (char)state.set.Length;
+                    span[CategoryLengthIndex] = (char)state.category.Length;
+                    state.set.AsSpan().CopyTo(span.Slice(SetStartIndex));
+                    index = SetStartIndex + state.set.Length;
                 }
 
                 state.category.AsSpan().CopyTo(span.Slice(index));
@@ -749,63 +733,54 @@ public static string ConvertOldStringsToClass(string set, string category)
         public static char SingletonChar(string set)
         {
             Debug.Assert(IsSingleton(set) || IsSingletonInverse(set), "Tried to get the singleton char out of a non singleton character class");
-            return set[SETSTART];
-        }
-
-        public static bool IsMergeable(string charClass)
-        {
-            return (!IsNegated(charClass) && !IsSubtraction(charClass));
-        }
-
-        public static bool IsEmpty(string charClass)
-        {
-            return (charClass[CATEGORYLENGTH] == 0 && charClass[FLAGS] == 0 && charClass[SETLENGTH] == 0 && !IsSubtraction(charClass));
-        }
-
-        /// <summary>
-        /// <c>true</c> if the set contains a single character only
-        /// </summary>
-        public static bool IsSingleton(string set)
-        {
-            if (set[FLAGS] == 0 && set[CATEGORYLENGTH] == 0 && set[SETLENGTH] == 2 && !IsSubtraction(set) &&
-                (set[SETSTART] == LastChar || set[SETSTART] + 1 == set[SETSTART + 1]))
-                return true;
-            else
-                return false;
-        }
-
-        public static bool IsSingletonInverse(string set)
-        {
-            if (set[FLAGS] == 1 && set[CATEGORYLENGTH] == 0 && set[SETLENGTH] == 2 && !IsSubtraction(set) &&
-                (set[SETSTART] == LastChar || set[SETSTART] + 1 == set[SETSTART + 1]))
-                return true;
-            else
-                return false;
-        }
-
-        internal static bool IsSubtraction(string charClass)
-        {
-            return (charClass.Length > SETSTART + charClass[SETLENGTH] + charClass[CATEGORYLENGTH]);
-        }
-
-        internal static bool IsNegated(string set)
-        {
-            return (set != null && set[FLAGS] == 1);
+            return set[SetStartIndex];
         }
 
-        public static bool IsECMAWordChar(char ch)
-        {
+        public static bool IsMergeable(string? charClass) =>
+            charClass != null &&
+            !IsNegated(charClass) &&
+            !IsSubtraction(charClass);
+
+        public static bool IsEmpty(string charClass) =>
+            charClass[CategoryLengthIndex] == 0 &&
+            charClass[SetLengthIndex] == 0 &&
+            !IsNegated(charClass) &&
+            !IsSubtraction(charClass);
+
+        /// <summary><c>true</c> if the set contains a single character only</summary>
+        public static bool IsSingleton(string set) =>
+            set[CategoryLengthIndex] == 0 &&
+            set[SetLengthIndex] == 2 &&
+            !IsNegated(set) &&
+            !IsSubtraction(set) &&
+            (set[SetStartIndex] == LastChar || set[SetStartIndex] + 1 == set[SetStartIndex + 1]);
+
+        public static bool IsSingletonInverse(string set) =>
+            set[CategoryLengthIndex] == 0 &&
+            set[SetLengthIndex] == 2 &&
+            IsNegated(set) &&
+            !IsSubtraction(set) &&
+            (set[SetStartIndex] == LastChar || set[SetStartIndex] + 1 == set[SetStartIndex + 1]);
+
+        internal static bool IsSubtraction(string charClass) =>
+            charClass.Length > SetStartIndex +
+            charClass[CategoryLengthIndex] +
+            charClass[SetLengthIndex];
+
+        internal static bool IsNegated(string set) => set[FlagsIndex] == 1;
+
+        internal static bool IsNegated(string set, int setOffset) => set[FlagsIndex + setOffset] == 1;
+
+        public static bool IsECMAWordChar(char ch) =>
             // According to ECMA-262, \s, \S, ., ^, and $ use Unicode-based interpretations of
             // whitespace and newline, while \d, \D\, \w, \W, \b, and \B use ASCII-only
             // interpretations of digit, word character, and word boundary.  In other words,
             // no special treatment of Unicode ZERO WIDTH NON-JOINER (ZWNJ U+200C) and
             // ZERO WIDTH JOINER (ZWJ U+200D) is required for ECMA word boundaries.
-            return
-                ((((uint)ch - 'A') & ~0x20) < 26) || // ASCII letter
-                (((uint)ch - '0') < 10) || // digit
-                ch == '_' || // underscore
-                ch == '\u0130'; // latin capital letter I with dot above
-        }
+            ((((uint)ch - 'A') & ~0x20) < 26) || // ASCII letter
+            (((uint)ch - '0') < 10) || // digit
+            ch == '_' || // underscore
+            ch == '\u0130'; // latin capital letter I with dot above
 
         public static bool IsWordChar(char ch)
         {
@@ -848,88 +823,92 @@ public static bool IsWordChar(char ch)
             }
         }
 
-        public static bool CharInClass(char ch, string set)
-        {
-            return CharInClassRecursive(ch, set, 0);
-        }
+        public static bool CharInClass(char ch, string set) =>
+            CharInClassRecursive(ch, set, 0);
 
         private static bool CharInClassRecursive(char ch, string set, int start)
         {
-            int mySetLength = set[start + SETLENGTH];
-            int myCategoryLength = set[start + CATEGORYLENGTH];
-            int myEndPosition = start + SETSTART + mySetLength + myCategoryLength;
+            int setLength = set[start + SetLengthIndex];
+            int categoryLength = set[start + CategoryLengthIndex];
+            int endPosition = start + SetStartIndex + setLength + categoryLength;
 
-            bool subtracted = false;
+            bool inClass = CharInClassInternal(ch, set, start, setLength, categoryLength);
 
-            if (set.Length > myEndPosition)
+            // Note that we apply the negation *before* performing the subtraction.  This is because
+            // the negation only applies to the first char class, not the entire subtraction.
+            if (IsNegated(set, start))
             {
-                subtracted = CharInClassRecursive(ch, set, myEndPosition);
+                inClass = !inClass;
             }
 
-            bool b = CharInClassInternal(ch, set, start, mySetLength, myCategoryLength);
-
-            // Note that we apply the negation *before* performing the subtraction.  This is because
-            // the negation only applies to the first char class, not the entire subtraction.
-            if (set[start + FLAGS] == 1)
-                b = !b;
+            // Subtract if necessary
+            if (inClass && set.Length > endPosition)
+            {
+                inClass = !CharInClassRecursive(ch, set, endPosition);
+            }
 
-            return b && !subtracted;
+            return inClass;
         }
 
         /// <summary>
         /// Determines a character's membership in a character class (via the
         /// string representation of the class).
         /// </summary>
-        private static bool CharInClassInternal(char ch, string set, int start, int mySetLength, int myCategoryLength)
+        private static bool CharInClassInternal(char ch, string set, int start, int setLength, int categoryLength)
         {
-            int min;
-            int max;
-            int mid;
-            min = start + SETSTART;
-            max = min + mySetLength;
+            int min = start + SetStartIndex;
+            int max = min + setLength;
 
             while (min != max)
             {
-                mid = (min + max) / 2;
+                int mid = (min + max) >> 1;
                 if (ch < set[mid])
+                {
                     max = mid;
+                }
                 else
+                {
                     min = mid + 1;
+                }
             }
 
             // The starting position of the set within the character class determines
             // whether what an odd or even ending position means.  If the start is odd,
             // an *even* ending position means the character was in the set.  With recursive
-            // subtractions in the mix, the starting position = start+SETSTART.  Since we know that
-            // SETSTART is odd, we can simplify it out of the equation.  But if it changes we need to
+            // subtractions in the mix, the starting position = start+SetStartIndex.  Since we know that
+            // SetStartIndex is odd, we can simplify it out of the equation.  But if it changes we need to
             // reverse this check.
-            Debug.Assert((SETSTART & 0x1) == 1, "If SETSTART is not odd, the calculation below this will be reversed");
+            Debug.Assert((SetStartIndex & 0x1) == 1, "If SetStartIndex is not odd, the calculation below this will be reversed");
             if ((min & 0x1) == (start & 0x1))
-                return true;
-            else
             {
-                if (myCategoryLength == 0)
-                    return false;
+                return true;
+            }
 
-                return CharInCategory(ch, set, start, mySetLength, myCategoryLength);
+            if (categoryLength == 0)
+            {
+                return false;
             }
+
+            return CharInCategory(ch, set, start, setLength, categoryLength);
         }
 
-        private static bool CharInCategory(char ch, string set, int start, int mySetLength, int myCategoryLength)
+        private static bool CharInCategory(char ch, string set, int start, int setLength, int categoryLength)
         {
             UnicodeCategory chcategory = char.GetUnicodeCategory(ch);
 
-            int i = start + SETSTART + mySetLength;
-            int end = i + myCategoryLength;
+            int i = start + SetStartIndex + setLength;
+            int end = i + categoryLength;
             while (i < end)
             {
-                int curcat = unchecked((short)set[i]);
+                int curcat = (short)set[i];
 
                 if (curcat == 0)
                 {
                     // zero is our marker for a group of categories - treated as a unit
-                    if (CharInCategoryGroup(ch, chcategory, set, ref i))
+                    if (CharInCategoryGroup(chcategory, set, ref i))
+                    {
                         return true;
+                    }
                 }
                 else if (curcat > 0)
                 {
@@ -938,17 +917,14 @@ private static bool CharInCategory(char ch, string set, int start, int mySetLeng
                     if (curcat == SpaceConst)
                     {
                         if (char.IsWhiteSpace(ch))
-                            return true;
-                        else
                         {
-                            i++;
-                            continue;
+                            return true;
                         }
                     }
-                    --curcat;
-
-                    if (chcategory == (UnicodeCategory)curcat)
+                    else if (chcategory == (UnicodeCategory)(curcat - 1))
+                    {
                         return true;
+                    }
                 }
                 else
                 {
@@ -956,23 +932,19 @@ private static bool CharInCategory(char ch, string set, int start, int mySetLeng
                     if (curcat == NotSpaceConst)
                     {
                         if (!char.IsWhiteSpace(ch))
-                            return true;
-                        else
                         {
-                            i++;
-                            continue;
+                            return true;
                         }
                     }
-
-                    //curcat = -curcat;
-                    //--curcat;
-                    curcat = -1 - curcat;
-
-                    if (chcategory != (UnicodeCategory)curcat)
+                    else if (chcategory != (UnicodeCategory)(-1 - curcat))
+                    {
                         return true;
+                    }
                 }
+
                 i++;
             }
+
             return false;
         }
 
@@ -980,69 +952,59 @@ private static bool CharInCategory(char ch, string set, int start, int mySetLeng
         /// This is used for categories which are composed of other categories - L, N, Z, W...
         /// These groups need special treatment when they are negated
         /// </summary>
-        private static bool CharInCategoryGroup(char ch, UnicodeCategory chcategory, string category, ref int i)
+        private static bool CharInCategoryGroup(UnicodeCategory chcategory, string category, ref int i)
         {
-            i++;
+            int pos = i + 1;
+            int curcat = (short)category[pos];
+
+            bool result;
 
-            int curcat = unchecked((short)category[i]);
             if (curcat > 0)
             {
                 // positive case - the character must be in ANY of the categories in the group
-                bool answer = false;
-
-                while (curcat != 0)
+                result = false;
+                for (; curcat != 0; curcat = (short)category[pos])
                 {
-                    if (!answer)
+                    pos++;
+                    if (!result && chcategory == (UnicodeCategory)(curcat - 1))
                     {
-                        --curcat;
-                        if (chcategory == (UnicodeCategory)curcat)
-                            answer = true;
+                        result = true;
                     }
-                    i++;
-                    curcat = (short)category[i];
                 }
-                return answer;
             }
             else
             {
                 // negative case - the character must be in NONE of the categories in the group
-                bool answer = true;
-
-                while (curcat != 0)
+                result = true;
+                for (; curcat != 0; curcat = (short)category[pos])
                 {
-                    if (answer)
+                    pos++;
+                    if (result && chcategory == (UnicodeCategory)(-1 - curcat))
                     {
-                        //curcat = -curcat;
-                        //--curcat;
-                        curcat = -1 - curcat;
-                        if (chcategory == (UnicodeCategory)curcat)
-                            answer = false;
+                        result = false;
                     }
-                    i++;
-                    curcat = unchecked((short)category[i]);
                 }
-                return answer;
             }
-        }
 
-        public static RegexCharClass Parse(string charClass)
-        {
-            return ParseRecursive(charClass, 0);
+            i = pos;
+            return result;
         }
 
+        public static RegexCharClass Parse(string charClass) => ParseRecursive(charClass, 0);
+
         private static RegexCharClass ParseRecursive(string charClass, int start)
         {
-            int mySetLength = charClass[start + SETLENGTH];
-            int myCategoryLength = charClass[start + CATEGORYLENGTH];
-            int myEndPosition = start + SETSTART + mySetLength + myCategoryLength;
+            int setLength = charClass[start + SetLengthIndex];
+            int categoryLength = charClass[start + CategoryLengthIndex];
+            int endPosition = start + SetStartIndex + setLength + categoryLength;
 
-            int i = start + SETSTART;
-            int end = i + mySetLength;
+            int i = start + SetStartIndex;
+            int end = i + setLength;
 
             List<SingleRange>? ranges = null;
-            if (mySetLength > 0)
+            if (setLength > 0)
             {
-                ranges = new List<SingleRange>(mySetLength);
+                ranges = new List<SingleRange>(setLength);
                 while (i < end)
                 {
                     char first = charClass[i];
@@ -1058,19 +1020,18 @@ private static RegexCharClass ParseRecursive(string charClass, int start)
             }
 
             RegexCharClass? sub = null;
-            if (charClass.Length > myEndPosition)
+            if (charClass.Length > endPosition)
             {
-                sub = ParseRecursive(charClass, myEndPosition);
+                sub = ParseRecursive(charClass, endPosition);
             }
 
             StringBuilder? categoriesBuilder = null;
-            if (myCategoryLength > 0)
+            if (categoryLength > 0)
             {
-                categoriesBuilder = new StringBuilder();
-                categoriesBuilder.Append(charClass.AsSpan(end, myCategoryLength));
+                categoriesBuilder = new StringBuilder().Append(charClass.AsSpan(end, categoryLength));
             }
 
-            return new RegexCharClass(charClass[start + FLAGS] == 1, ranges, categoriesBuilder, sub);
+            return new RegexCharClass(IsNegated(charClass, start), ranges, categoriesBuilder, sub);
         }
 
         /// <summary>
@@ -1092,9 +1053,10 @@ private void ToStringClass(ref ValueStringBuilder vsb)
 
             int initialLength = vsb.Length;
             int categoriesLength = _categories?.Length ?? 0;
-            vsb.Append((char)(_negate ? 1 : 0)); // FLAGS
-            vsb.Append('\0'); // SETLENGTH (will be replaced once we know how long a range we've added)
-            vsb.Append((char)categoriesLength); // CATEGORYLENGTH
+            Span<char> headerSpan = vsb.AppendSpan(SetStartIndex);
+            headerSpan[FlagsIndex] = (char)(_negate ? 1 : 0);
+            headerSpan[SetLengthIndex] = '\0'; // (will be replaced once we know how long a range we've added)
+            headerSpan[CategoryLengthIndex] = (char)categoriesLength;
 
             // Append ranges
             List<SingleRange>? rangelist = _rangelist;
@@ -1113,7 +1075,7 @@ private void ToStringClass(ref ValueStringBuilder vsb)
 
             // Update the range length.  The ValueStringBuilder may have already had some
             // contents (if this is a subtactor), so we need to offset by the initial length.
-            vsb[initialLength + SETLENGTH] = (char)((vsb.Length - initialLength) - SETSTART);
+            vsb[initialLength + SetLengthIndex] = (char)((vsb.Length - initialLength) - SetStartIndex);
 
             // Append categories
             if (categoriesLength != 0)
@@ -1140,9 +1102,7 @@ private void Canonicalize()
             {
                 rangelist.Sort((x, y) => x.First.CompareTo(y.First));
 
-                //
                 // Find and eliminate overlapping or abutting ranges
-                //
 
                 bool done = false;
                 int i, j;
@@ -1160,10 +1120,14 @@ private void Canonicalize()
 
                         SingleRange currentRange;
                         if ((currentRange = rangelist[i]).First > last + 1)
+                        {
                             break;
+                        }
 
                         if (last < currentRange.Last)
+                        {
                             last = currentRange.Last;
+                        }
                     }
 
                     rangelist[j] = new SingleRange(rangelist[j].First, last);
@@ -1171,10 +1135,14 @@ private void Canonicalize()
                     j++;
 
                     if (done)
+                    {
                         break;
+                    }
 
                     if (j < i)
+                    {
                         rangelist[j] = rangelist[i];
+                    }
                 }
 
                 rangelist.RemoveRange(j, rangelist.Count - j);
@@ -1213,7 +1181,6 @@ private static ReadOnlySpan<char> SetFromProperty(string capname, bool invert, s
         }
 
 #if DEBUG
-
         public static readonly char[] Hex = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
         public static readonly string[] Categories = new string[] {"Lu", "Ll", "Lt", "Lm", "Lo", InternalRegexIgnoreCase,
                                                                      "Mn", "Mc", "Me",
@@ -1229,41 +1196,45 @@ private static ReadOnlySpan<char> SetFromProperty(string capname, bool invert, s
         /// </summary>
         public static string SetDescription(string set)
         {
-            int mySetLength = set[SETLENGTH];
-            int myCategoryLength = set[CATEGORYLENGTH];
-            int myEndPosition = SETSTART + mySetLength + myCategoryLength;
+            int setLength = set[SetLengthIndex];
+            int categoryLength = set[CategoryLengthIndex];
+            int endPosition = SetStartIndex + setLength + categoryLength;
 
             StringBuilder desc = new StringBuilder();
 
             desc.Append('[');
 
-            int index = SETSTART;
+            int index = SetStartIndex;
             char ch1;
             char ch2;
 
             if (IsNegated(set))
+            {
                 desc.Append('^');
+            }
 
-            while (index < SETSTART + set[SETLENGTH])
+            while (index < SetStartIndex + set[SetLengthIndex])
             {
                 ch1 = set[index];
-                if (index + 1 < set.Length)
-                    ch2 = (char)(set[index + 1] - 1);
-                else
-                    ch2 = LastChar;
+                ch2 = index + 1 < set.Length ?
+                    (char)(set[index + 1] - 1) :
+                    LastChar;
 
                 desc.Append(CharDescription(ch1));
 
                 if (ch2 != ch1)
                 {
                     if (ch1 + 1 != ch2)
+                    {
                         desc.Append('-');
+                    }
+
                     desc.Append(CharDescription(ch2));
                 }
                 index += 2;
             }
 
-            while (index < SETSTART + set[SETLENGTH] + set[CATEGORYLENGTH])
+            while (index < SetStartIndex + set[SetLengthIndex] + set[CategoryLengthIndex])
             {
                 ch1 = set[index];
                 if (ch1 == 0)
@@ -1278,14 +1249,7 @@ public static string SetDescription(string set)
                     {
                         if (group.Equals(kvp.Value))
                         {
-                            if ((short)set[index + 1] > 0)
-                                desc.Append("\\p{");
-                            else
-                                desc.Append("\\P{");
-
-                            desc.Append(kvp.Key);
-                            desc.Append('}');
-
+                            desc.Append((short)set[index + 1] > 0 ? "\\p{" : "\\P{").Append(kvp.Key).Append('}');
                             found = true;
                             break;
                         }
@@ -1294,11 +1258,17 @@ public static string SetDescription(string set)
                     if (!found)
                     {
                         if (group.Equals(Word))
+                        {
                             desc.Append("\\w");
+                        }
                         else if (group.Equals(NotWord))
+                        {
                             desc.Append("\\W");
+                        }
                         else
-                            Debug.Fail("Couldn't find a group to match '" + group + "'");
+                        {
+                            Debug.Fail($"Couldn't find a group to match '{group}'");
+                        }
                     }
 
                     index = lastindex;
@@ -1311,15 +1281,12 @@ public static string SetDescription(string set)
                 index++;
             }
 
-            if (set.Length > myEndPosition)
+            if (set.Length > endPosition)
             {
-                desc.Append('-');
-                desc.Append(SetDescription(set.Substring(myEndPosition)));
+                desc.Append('-').Append(SetDescription(set.Substring(endPosition)));
             }
 
-            desc.Append(']');
-
-            return desc.ToString();
+            return desc.Append(']').ToString();
         }
 
         /// <summary>
@@ -1328,7 +1295,9 @@ public static string SetDescription(string set)
         public static string CharDescription(char ch)
         {
             if (ch == '\\')
+            {
                 return "\\\\";
+            }
 
             if (ch >= ' ' && ch <= '~')
             {
@@ -1361,19 +1330,22 @@ public static string CharDescription(char ch)
         private static string CategoryDescription(char ch)
         {
             if (ch == SpaceConst)
+            {
                 return "\\s";
-            else if ((short)ch == NotSpaceConst)
-                return "\\S";
-            else if ((short)ch < 0)
+            }
+
+            if ((short)ch == NotSpaceConst)
             {
-                return "\\P{" + Categories[(-((short)ch) - 1)] + "}";
+                return "\\S";
             }
-            else
+
+            if ((short)ch < 0)
             {
-                return "\\p{" + Categories[(ch - 1)] + "}";
+                return "\\P{" + Categories[(-((short)ch) - 1)] + "}";
             }
-        }
 
+            return "\\p{" + Categories[(ch - 1)] + "}";
+        }
 #endif
 
         /// <summary>
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompilationInfo.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompilationInfo.cs
index e023e78bdc6e86..388b03065c2469 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompilationInfo.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompilationInfo.cs
@@ -1,7 +1,6 @@
 ﻿// Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
-using System.Diagnostics.CodeAnalysis;
 
 namespace System.Text.RegularExpressions
 {
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs
index 2322c72f82d254..4ece103bf74808 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCompiler.cs
@@ -6,7 +6,6 @@
 using System.Globalization;
 using System.Reflection;
 using System.Reflection.Emit;
-using System.Runtime.InteropServices;
 
 namespace System.Text.RegularExpressions
 {
@@ -2882,15 +2881,15 @@ private void EmitCallCharInClass(string charClass, LocalBuilder tempLocal)
             }
 
             // Next, handle simple sets of one range, e.g. [A-Z], [0-9], etc.  This includes some built-in classes, like ECMADigitClass.
-            if (charClass.Length == RegexCharClass.SETSTART + 2 && // one set of two values
-                charClass[RegexCharClass.SETLENGTH] == 2 && // validate we have the right number of ranges
-                charClass[RegexCharClass.CATEGORYLENGTH] == 0 && // must not have any categories
-                charClass[RegexCharClass.SETSTART] < charClass[RegexCharClass.SETSTART + 1]) // valid range
+            if (charClass.Length == RegexCharClass.SetStartIndex + 2 && // one set of two values
+                charClass[RegexCharClass.SetLengthIndex] == 2 && // validate we have the right number of ranges
+                charClass[RegexCharClass.CategoryLengthIndex] == 0 && // must not have any categories
+                charClass[RegexCharClass.SetStartIndex] < charClass[RegexCharClass.SetStartIndex + 1]) // valid range
             {
                 // (uint)ch - charClass[3] < charClass[4] - charClass[3]
-                Ldc(charClass[RegexCharClass.SETSTART]);
+                Ldc(charClass[RegexCharClass.SetStartIndex]);
                 Sub();
-                Ldc(charClass[RegexCharClass.SETSTART + 1] - charClass[RegexCharClass.SETSTART]);
+                Ldc(charClass[RegexCharClass.SetStartIndex + 1] - charClass[RegexCharClass.SetStartIndex]);
                 CltUn();
 
                 // Negate the answer if the negation flag was set
@@ -2937,14 +2936,14 @@ private void EmitCallCharInClass(string charClass, LocalBuilder tempLocal)
             // We currently go with (2).  We may sometimes generate a fallback when we don't need one, but the cost of
             // doing so once in a while is minimal.
             bool asciiOnly =
-                charClass.Length > RegexCharClass.SETSTART &&
-                charClass[RegexCharClass.CATEGORYLENGTH] == 0 && // if there are any categories, assume there's unicode
-                charClass[RegexCharClass.SETLENGTH] % 2 == 0 && // range limits must come in pairs
+                charClass.Length > RegexCharClass.SetStartIndex &&
+                charClass[RegexCharClass.CategoryLengthIndex] == 0 && // if there are any categories, assume there's unicode
+                charClass[RegexCharClass.SetLengthIndex] % 2 == 0 && // range limits must come in pairs
                 !RegexCharClass.IsNegated(charClass) && // if there's negation, assume there's unicode
                 !RegexCharClass.IsSubtraction(charClass); // if it's subtraction, assume there's unicode
             if (asciiOnly)
             {
-                for (int i = RegexCharClass.SETSTART; i < charClass.Length; i++)
+                for (int i = RegexCharClass.SetStartIndex; i < charClass.Length; i++)
                 {
                     if (charClass[i] >= 128) // validate all characters in the set are ASCII
                     {
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexFCD.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexFCD.cs
index 234af27f67b31e..b69a6e8c419345 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexFCD.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexFCD.cs
@@ -12,6 +12,7 @@
 // sequences of codes.
 
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.Globalization;
 
 namespace System.Text.RegularExpressions
@@ -54,16 +55,21 @@ private RegexFCD(Span<int> intStack)
         /// </summary>
         public static RegexPrefix? FirstChars(RegexTree t)
         {
-            RegexFCD s = new RegexFCD(stackalloc int[StackBufferSize]);
+            var s = new RegexFCD(stackalloc int[StackBufferSize]);
             RegexFC? fc = s.RegexFCFromRegexTree(t);
             s.Dispose();
 
             if (fc == null || fc._nullable)
+            {
                 return null;
+            }
 
-            CultureInfo culture = ((t.Options & RegexOptions.CultureInvariant) != 0) ? CultureInfo.InvariantCulture : CultureInfo.CurrentCulture;
+            if (fc.CaseInsensitive)
+            {
+                fc.AddLowercase(((t.Options & RegexOptions.CultureInvariant) != 0) ? CultureInfo.InvariantCulture : CultureInfo.CurrentCulture);
+            }
 
-            return new RegexPrefix(fc.GetFirstChars(culture), fc.CaseInsensitive);
+            return new RegexPrefix(fc.GetFirstChars(), fc.CaseInsensitive);
         }
 
         /// <summary>
@@ -250,54 +256,32 @@ public static string AnchorDescription(int anchors)
         /// <summary>
         /// To avoid recursion, we use a simple integer stack.
         /// </summary>
-        private void PushInt(int i)
-        {
-            _intStack.Append(i);
-        }
+        private void PushInt(int i) => _intStack.Append(i);
 
-        private bool IntIsEmpty()
-        {
-            return _intStack.Length == 0;
-        }
+        private bool IntIsEmpty() => _intStack.Length == 0;
 
-        private int PopInt()
-        {
-            return _intStack.Pop();
-        }
+        private int PopInt() => _intStack.Pop();
 
         /// <summary>
         /// We also use a stack of RegexFC objects.
         /// </summary>
-        private void PushFC(RegexFC fc)
-        {
-            _fcStack.Add(fc);
-        }
+        private void PushFC(RegexFC fc) => _fcStack.Add(fc);
 
-        private bool FCIsEmpty()
-        {
-            return _fcStack.Count == 0;
-        }
+        private bool FCIsEmpty() => _fcStack.Count == 0;
 
         private RegexFC PopFC()
         {
             RegexFC item = TopFC();
             _fcStack.RemoveAt(_fcStack.Count - 1);
-
             return item;
         }
 
-        private RegexFC TopFC()
-        {
-            return _fcStack[_fcStack.Count - 1];
-        }
+        private RegexFC TopFC() => _fcStack[_fcStack.Count - 1];
 
         /// <summary>
         /// Return rented buffers.
         /// </summary>
-        public void Dispose()
-        {
-            _intStack.Dispose();
-        }
+        public void Dispose() => _intStack.Dispose();
 
         /// <summary>
         /// The main FC computation. It does a shortcutted depth-first walk
@@ -362,10 +346,7 @@ public void Dispose()
         /// <summary>
         /// Called in Beforechild to prevent further processing of the current child
         /// </summary>
-        private void SkipChild()
-        {
-            _skipchild = true;
-        }
+        private void SkipChild() => _skipchild = true;
 
         /// <summary>
         /// FC computation and shortcut cases for each node type
@@ -534,9 +515,14 @@ public RegexFC(char ch, bool not, bool nullable, bool caseInsensitive)
             if (not)
             {
                 if (ch > 0)
+                {
                     _cc.AddRange('\0', (char)(ch - 1));
+                }
+
                 if (ch < 0xFFFF)
+                {
                     _cc.AddRange((char)(ch + 1), '\uFFFF');
+                }
             }
             else
             {
@@ -583,12 +569,12 @@ public bool AddFC(RegexFC fc, bool concatenate)
 
         public bool CaseInsensitive { get; private set; }
 
-        public string GetFirstChars(CultureInfo culture)
+        public void AddLowercase(CultureInfo culture)
         {
-            if (CaseInsensitive)
-                _cc.AddLowercase(culture);
-
-            return _cc.ToStringClass();
+            Debug.Assert(CaseInsensitive);
+            _cc.AddLowercase(culture);
         }
+
+        public string GetFirstChars() => _cc.ToStringClass();
     }
 }
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexNode.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexNode.cs
index f22fa884f0acb9..13cff7efde8d2f 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexNode.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexNode.cs
@@ -354,13 +354,12 @@ private RegexNode ReduceAlternation()
                         // Cannot merge sets if L or I options differ, or if either are negated.
                         optionsAt = at.Options & (RegexOptions.RightToLeft | RegexOptions.IgnoreCase);
 
-
                         if (at.NType == Set)
                         {
-                            if (!wasLastSet || optionsLast != optionsAt || lastNodeCannotMerge || !RegexCharClass.IsMergeable(at.Str!))
+                            if (!wasLastSet || optionsLast != optionsAt || lastNodeCannotMerge || !RegexCharClass.IsMergeable(at.Str))
                             {
                                 wasLastSet = true;
-                                lastNodeCannotMerge = !RegexCharClass.IsMergeable(at.Str!);
+                                lastNodeCannotMerge = !RegexCharClass.IsMergeable(at.Str);
                                 optionsLast = optionsAt;
                                 break;
                             }
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs
index ed87d0ae9be8e6..4f3ae32b231636 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexParser.cs
@@ -2,14 +2,6 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-// This RegexParser class is internal to the Regex package.
-// It builds a tree of RegexNodes from a regular expression
-
-// Implementation notes:
-//
-// It would be nice to get rid of the comment modes, since the
-// ScanBlank() calls are just kind of duct-taped in.
-
 using System.Collections;
 using System.Collections.Generic;
 using System.Diagnostics;
@@ -17,8 +9,13 @@
 
 namespace System.Text.RegularExpressions
 {
+    /// <summary>Builds a tree of RegexNodes from a regular expression.</summary>
     internal ref struct RegexParser
     {
+        // Implementation notes:
+        // It would be nice to get rid of the comment modes, since the
+        // ScanBlank() calls are just kind of duct-taped in.
+
         private const int EscapeMaxBufferSize = 256;
         private const int OptionStackDefaultSize = 32;
         private const int MaxValueDiv10 = int.MaxValue / 10;
@@ -159,6 +156,7 @@ private static string EscapeImpl(string input, int i)
                         ch = 'f';
                         break;
                 }
+
                 vsb.Append(ch);
                 i++;
                 int lastpos = i;
@@ -167,7 +165,9 @@ private static string EscapeImpl(string input, int i)
                 {
                     ch = input[i];
                     if (IsMetachar(ch))
+                    {
                         break;
+                    }
 
                     i++;
                 }
@@ -205,11 +205,17 @@ private static string UnescapeImpl(string input, int i)
                 i++;
                 parser.Textto(i);
                 if (i < input.Length)
+                {
                     vsb.Append(parser.ScanCharEscape());
+                }
+
                 i = parser.Textpos();
                 int lastpos = i;
                 while (i < input.Length && input[i] != '\\')
+                {
                     i++;
+                }
+
                 vsb.Append(input.AsSpan(lastpos, i - lastpos));
             } while (i < input.Length);
 
@@ -259,25 +265,33 @@ private RegexNode ScanRegex()
                 // move past all of the normal characters.  We'll stop when we hit some kind of control character,
                 // or if IgnorePatternWhiteSpace is on, we'll stop when we see some whitespace.
                 if (UseOptionX())
+                {
                     while (CharsRight() > 0 && (!IsStopperX(ch = RightChar()) || (ch == '{' && !IsTrueQuantifier())))
                         MoveRight();
+                }
                 else
+                {
                     while (CharsRight() > 0 && (!IsSpecial(ch = RightChar()) || (ch == '{' && !IsTrueQuantifier())))
                         MoveRight();
+                }
 
                 int endpos = Textpos();
 
                 ScanBlank();
 
                 if (CharsRight() == 0)
+                {
                     ch = '!'; // nonspecial, means at end
+                }
                 else if (IsSpecial(ch = RightChar()))
                 {
                     isQuantifier = IsQuantifier(ch);
                     MoveRight();
                 }
                 else
+                {
                     ch = ' '; // nonspecial, means at ordinary char
+                }
 
                 if (startpos < endpos)
                 {
@@ -286,10 +300,14 @@ private RegexNode ScanRegex()
                     wasPrevQuantifier = false;
 
                     if (cchUnquantified > 0)
+                    {
                         AddConcatenate(startpos, cchUnquantified, false);
+                    }
 
                     if (isQuantifier)
+                    {
                         AddUnitOne(CharAt(endpos - 1));
+                    }
                 }
 
                 switch (ch)
@@ -328,19 +346,25 @@ private RegexNode ScanRegex()
 
                     case ')':
                         if (EmptyStack())
+                        {
                             throw MakeException(RegexParseError.TooManyParentheses, SR.TooManyParens);
+                        }
 
                         AddGroup();
                         PopGroup();
                         PopOptions();
 
                         if (Unit() == null)
+                        {
                             goto ContinueOuterScan;
+                        }
                         break;
 
                     case '\\':
                         if (CharsRight() == 0)
+                        {
                             throw MakeException(RegexParseError.IllegalEndEscape, SR.IllegalEndEscape);
+                        }
 
                         AddUnitNode(ScanBackslash(scanOnly: false)!);
                         break;
@@ -355,9 +379,13 @@ private RegexNode ScanRegex()
 
                     case '.':
                         if (UseOptionS())
+                        {
                             AddUnitSet(RegexCharClass.AnyClass);
+                        }
                         else
+                        {
                             AddUnitNotone('\n');
+                        }
                         break;
 
                     case '{':
@@ -366,10 +394,9 @@ private RegexNode ScanRegex()
                     case '?':
                         if (Unit() == null)
                         {
-                            if (wasPrevQuantifier)
-                                throw MakeException(RegexParseError.NestedQuantify, SR.Format(SR.NestedQuantify, ch));
-                            else
-                                throw MakeException(RegexParseError.QuantifyAfterNothing, SR.QuantifyAfterNothing);
+                            throw wasPrevQuantifier ?
+                                MakeException(RegexParseError.NestedQuantify, SR.Format(SR.NestedQuantify, ch)) :
+                                MakeException(RegexParseError.QuantifyAfterNothing, SR.QuantifyAfterNothing);
                         }
                         MoveLeft();
                         break;
@@ -393,7 +420,6 @@ private RegexNode ScanRegex()
                 {
                     int min;
                     int max;
-                    bool lazy;
 
                     switch (ch)
                     {
@@ -444,16 +470,17 @@ private RegexNode ScanRegex()
 
                     ScanBlank();
 
-                    if (CharsRight() == 0 || RightChar() != '?')
-                        lazy = false;
-                    else
+                    bool lazy = false;
+                    if (CharsRight() != 0 && RightChar() == '?')
                     {
                         MoveRight();
                         lazy = true;
                     }
 
                     if (min > max)
+                    {
                         throw MakeException(RegexParseError.IllegalRange, SR.IllegalRange);
+                    }
 
                     AddConcatenate(lazy, min, max);
                 }
@@ -466,7 +493,9 @@ private RegexNode ScanRegex()
             ;
 
             if (!EmptyStack())
+            {
                 throw MakeException(RegexParseError.NotEnoughParentheses, SR.NotEnoughParens);
+            }
 
             AddGroup();
 
@@ -484,7 +513,9 @@ private RegexNode ScanReplacement()
             {
                 int c = CharsRight();
                 if (c == 0)
+                {
                     break;
+                }
 
                 int startpos = Textpos();
 
@@ -499,7 +530,10 @@ private RegexNode ScanReplacement()
                 if (c > 0)
                 {
                     if (RightCharMoveRight() == '$')
+                    {
                         AddUnitNode(ScanDollar());
+                    }
+
                     AddConcatenate();
                 }
             }
@@ -519,20 +553,20 @@ private RegexNode ScanReplacement()
             bool firstChar = true;
             bool closed = false;
 
-            RegexCharClass? cc;
-
-            cc = scanOnly ? null : new RegexCharClass();
+            RegexCharClass? charClass = scanOnly ? null : new RegexCharClass();
 
             if (CharsRight() > 0 && RightChar() == '^')
             {
                 MoveRight();
                 if (!scanOnly)
-                    cc!.Negate = true;
+                {
+                    charClass!.Negate = true;
+                }
             }
 
             for (; CharsRight() > 0; firstChar = false)
             {
-                bool fTranslatedChar = false;
+                bool translatedChar = false;
                 ch = RightCharMoveRight();
                 if (ch == ']')
                 {
@@ -551,8 +585,10 @@ private RegexNode ScanReplacement()
                             if (!scanOnly)
                             {
                                 if (inRange)
+                                {
                                     throw MakeException(RegexParseError.BadClassInCharRange, SR.Format(SR.BadClassInCharRange, ch));
-                                cc!.AddDigit(UseOptionE(), ch == 'D', _pattern, _currentPos);
+                                }
+                                charClass!.AddDigit(UseOptionE(), ch == 'D', _pattern, _currentPos);
                             }
                             continue;
 
@@ -561,8 +597,10 @@ private RegexNode ScanReplacement()
                             if (!scanOnly)
                             {
                                 if (inRange)
+                                {
                                     throw MakeException(RegexParseError.BadClassInCharRange, SR.Format(SR.BadClassInCharRange, ch));
-                                cc!.AddSpace(UseOptionE(), ch == 'S');
+                                }
+                                charClass!.AddSpace(UseOptionE(), ch == 'S');
                             }
                             continue;
 
@@ -571,9 +609,11 @@ private RegexNode ScanReplacement()
                             if (!scanOnly)
                             {
                                 if (inRange)
+                                {
                                     throw MakeException(RegexParseError.BadClassInCharRange, SR.Format(SR.BadClassInCharRange, ch));
+                                }
 
-                                cc!.AddWord(UseOptionE(), ch == 'W');
+                                charClass!.AddWord(UseOptionE(), ch == 'W');
                             }
                             continue;
 
@@ -582,24 +622,30 @@ private RegexNode ScanReplacement()
                             if (!scanOnly)
                             {
                                 if (inRange)
+                                {
                                     throw MakeException(RegexParseError.BadClassInCharRange, SR.Format(SR.BadClassInCharRange, ch));
-                                cc!.AddCategoryFromName(ParseProperty(), (ch != 'p'), caseInsensitive, _pattern, _currentPos);
+                                }
+
+                                charClass!.AddCategoryFromName(ParseProperty(), ch != 'p', caseInsensitive, _pattern, _currentPos);
                             }
                             else
+                            {
                                 ParseProperty();
-
+                            }
                             continue;
 
                         case '-':
                             if (!scanOnly)
-                                cc!.AddRange(ch, ch);
+                            {
+                                charClass!.AddRange(ch, ch);
+                            }
                             continue;
 
                         default:
                             MoveLeft();
                             ch = ScanCharEscape(); // non-literal character
-                            fTranslatedChar = true;
-                            break;          // this break will only break out of the switch
+                            translatedChar = true;
+                            break; // this break will only break out of the switch
                     }
                 }
                 else if (ch == '[')
@@ -612,7 +658,9 @@ private RegexNode ScanReplacement()
 
                         MoveRight();
                         if (CharsRight() < 2 || RightCharMoveRight() != ':' || RightCharMoveRight() != ']')
+                        {
                             Textto(savePos);
+                        }
                     }
                 }
 
@@ -621,23 +669,27 @@ private RegexNode ScanReplacement()
                     inRange = false;
                     if (!scanOnly)
                     {
-                        if (ch == '[' && !fTranslatedChar && !firstChar)
+                        if (ch == '[' && !translatedChar && !firstChar)
                         {
                             // We thought we were in a range, but we're actually starting a subtraction.
                             // In that case, we'll add chPrev to our char class, skip the opening [, and
                             // scan the new character class recursively.
-                            cc!.AddChar(chPrev);
-                            cc.AddSubtraction(ScanCharClass(caseInsensitive, scanOnly)!);
+                            charClass!.AddChar(chPrev);
+                            charClass.AddSubtraction(ScanCharClass(caseInsensitive, scanOnly)!);
 
                             if (CharsRight() > 0 && RightChar() != ']')
+                            {
                                 throw MakeException(RegexParseError.SubtractionMustBeLast, SR.SubtractionMustBeLast);
+                            }
                         }
                         else
                         {
                             // a regular range, like a-z
                             if (chPrev > ch)
+                            {
                                 throw MakeException(RegexParseError.ReversedCharRange, SR.ReversedCharRange);
-                            cc!.AddRange(chPrev, ch);
+                            }
+                            charClass!.AddRange(chPrev, ch);
                         }
                     }
                 }
@@ -648,17 +700,19 @@ private RegexNode ScanReplacement()
                     inRange = true;
                     MoveRight();
                 }
-                else if (CharsRight() >= 1 && ch == '-' && !fTranslatedChar && RightChar() == '[' && !firstChar)
+                else if (CharsRight() >= 1 && ch == '-' && !translatedChar && RightChar() == '[' && !firstChar)
                 {
                     // we aren't in a range, and now there is a subtraction.  Usually this happens
                     // only when a subtraction follows a range, like [a-z-[b]]
                     if (!scanOnly)
                     {
                         MoveRight(1);
-                        cc!.AddSubtraction(ScanCharClass(caseInsensitive, scanOnly)!);
+                        charClass!.AddSubtraction(ScanCharClass(caseInsensitive, scanOnly)!);
 
                         if (CharsRight() > 0 && RightChar() != ']')
+                        {
                             throw MakeException(RegexParseError.SubtractionMustBeLast, SR.SubtractionMustBeLast);
+                        }
                     }
                     else
                     {
@@ -669,17 +723,23 @@ private RegexNode ScanReplacement()
                 else
                 {
                     if (!scanOnly)
-                        cc!.AddRange(ch, ch);
+                    {
+                        charClass!.AddRange(ch, ch);
+                    }
                 }
             }
 
             if (!closed)
+            {
                 throw MakeException(RegexParseError.UnterminatedBracket, SR.UnterminatedBracket);
+            }
 
             if (!scanOnly && caseInsensitive)
-                cc!.AddLowercase(_culture);
+            {
+                charClass!.AddLowercase(_culture);
+            }
 
-            return cc;
+            return charClass;
         }
 
         /*
@@ -693,7 +753,7 @@ private RegexNode ScanReplacement()
             // 1. "(" followed by nothing
             // 2. "(x" where x != ?
             // 3. "(?)"
-            if (CharsRight() == 0 || RightChar() != '?' || (RightChar() == '?' && (CharsRight() > 1 && RightChar(1) == ')')))
+            if (CharsRight() == 0 || RightChar() != '?' || (RightChar() == '?' && CharsRight() > 1 && RightChar(1) == ')'))
             {
                 if (UseOptionN() || _ignoreNextParen)
                 {
@@ -701,7 +761,9 @@ private RegexNode ScanReplacement()
                     return new RegexNode(RegexNode.Group, _options);
                 }
                 else
+                {
                     return new RegexNode(RegexNode.Capture, _options, _autocap++, -1);
+                }
             }
 
             MoveRight();
@@ -709,62 +771,69 @@ private RegexNode ScanReplacement()
             while (true)
             {
                 if (CharsRight() == 0)
+                {
                     break;
+                }
 
-                int NodeType;
+                int nodeType;
                 char close = '>';
-                char ch;
-                switch (ch = RightCharMoveRight())
+                char ch = RightCharMoveRight();
+                switch (ch)
                 {
                     case ':':
                         // noncapturing group
-                        NodeType = RegexNode.Group;
+                        nodeType = RegexNode.Group;
                         break;
 
                     case '=':
                         // lookahead assertion
-                        _options &= ~(RegexOptions.RightToLeft);
-                        NodeType = RegexNode.Require;
+                        _options &= ~RegexOptions.RightToLeft;
+                        nodeType = RegexNode.Require;
                         break;
 
                     case '!':
                         // negative lookahead assertion
-                        _options &= ~(RegexOptions.RightToLeft);
-                        NodeType = RegexNode.Prevent;
+                        _options &= ~RegexOptions.RightToLeft;
+                        nodeType = RegexNode.Prevent;
                         break;
 
                     case '>':
                         // greedy subexpression
-                        NodeType = RegexNode.Greedy;
+                        nodeType = RegexNode.Greedy;
                         break;
 
                     case '\'':
                         close = '\'';
-                        goto case '<';
-                    // fallthrough
+                        goto case '<'; // fallthrough
 
                     case '<':
                         if (CharsRight() == 0)
+                        {
                             goto BreakRecognize;
+                        }
 
                         switch (ch = RightCharMoveRight())
                         {
                             case '=':
                                 if (close == '\'')
+                                {
                                     goto BreakRecognize;
+                                }
 
                                 // lookbehind assertion
                                 _options |= RegexOptions.RightToLeft;
-                                NodeType = RegexNode.Require;
+                                nodeType = RegexNode.Require;
                                 break;
 
                             case '!':
                                 if (close == '\'')
+                                {
                                     goto BreakRecognize;
+                                }
 
                                 // negative lookbehind assertion
                                 _options |= RegexOptions.RightToLeft;
-                                NodeType = RegexNode.Prevent;
+                                nodeType = RegexNode.Prevent;
                                 break;
 
                             default:
@@ -775,29 +844,40 @@ private RegexNode ScanReplacement()
 
                                 // grab part before -
 
-                                if (ch >= '0' && ch <= '9')
+                                if ((uint)(ch - '0') <= 9)
                                 {
                                     capnum = ScanDecimal();
 
                                     if (!IsCaptureSlot(capnum))
+                                    {
                                         capnum = -1;
+                                    }
 
                                     // check if we have bogus characters after the number
                                     if (CharsRight() > 0 && !(RightChar() == close || RightChar() == '-'))
+                                    {
                                         throw MakeException(RegexParseError.InvalidGroupName, SR.InvalidGroupName);
+                                    }
+
                                     if (capnum == 0)
+                                    {
                                         throw MakeException(RegexParseError.CapnumNotZero, SR.CapnumNotZero);
+                                    }
                                 }
                                 else if (RegexCharClass.IsWordChar(ch))
                                 {
                                     string capname = ScanCapname();
 
                                     if (IsCaptureName(capname))
+                                    {
                                         capnum = CaptureSlotFromName(capname);
+                                    }
 
                                     // check if we have bogus character after the name
                                     if (CharsRight() > 0 && !(RightChar() == close || RightChar() == '-'))
+                                    {
                                         throw MakeException(RegexParseError.InvalidGroupName, SR.InvalidGroupName);
+                                    }
                                 }
                                 else if (ch == '-')
                                 {
@@ -816,29 +896,39 @@ private RegexNode ScanReplacement()
                                     MoveRight();
                                     ch = RightChar();
 
-                                    if (ch >= '0' && ch <= '9')
+                                    if ((uint)(ch - '0') <= 9)
                                     {
                                         uncapnum = ScanDecimal();
 
                                         if (!IsCaptureSlot(uncapnum))
+                                        {
                                             throw MakeException(RegexParseError.UndefinedBackref, SR.Format(SR.UndefinedBackref, uncapnum));
+                                        }
 
                                         // check if we have bogus characters after the number
                                         if (CharsRight() > 0 && RightChar() != close)
+                                        {
                                             throw MakeException(RegexParseError.InvalidGroupName, SR.InvalidGroupName);
+                                        }
                                     }
                                     else if (RegexCharClass.IsWordChar(ch))
                                     {
                                         string uncapname = ScanCapname();
 
                                         if (IsCaptureName(uncapname))
+                                        {
                                             uncapnum = CaptureSlotFromName(uncapname);
+                                        }
                                         else
+                                        {
                                             throw MakeException(RegexParseError.UndefinedNameRef, SR.Format(SR.UndefinedNameRef, uncapname));
+                                        }
 
                                         // check if we have bogus character after the name
                                         if (CharsRight() > 0 && RightChar() != close)
+                                        {
                                             throw MakeException(RegexParseError.InvalidGroupName, SR.InvalidGroupName);
+                                        }
                                     }
                                     else
                                     {
@@ -872,23 +962,27 @@ private RegexNode ScanReplacement()
                                 if (CharsRight() > 0 && RightCharMoveRight() == ')')
                                 {
                                     if (IsCaptureSlot(capnum))
+                                    {
                                         return new RegexNode(RegexNode.Testref, _options, capnum);
-                                    else
-                                        throw MakeException(RegexParseError.UndefinedReference, SR.Format(SR.UndefinedReference, capnum.ToString()));
+                                    }
+
+                                    throw MakeException(RegexParseError.UndefinedReference, SR.Format(SR.UndefinedReference, capnum.ToString()));
                                 }
-                                else
-                                    throw MakeException(RegexParseError.MalformedReference, SR.Format(SR.MalformedReference, capnum.ToString()));
+
+                                throw MakeException(RegexParseError.MalformedReference, SR.Format(SR.MalformedReference, capnum.ToString()));
                             }
                             else if (RegexCharClass.IsWordChar(ch))
                             {
                                 string capname = ScanCapname();
 
                                 if (IsCaptureName(capname) && CharsRight() > 0 && RightCharMoveRight() == ')')
+                                {
                                     return new RegexNode(RegexNode.Testref, _options, CaptureSlotFromName(capname));
+                                }
                             }
                         }
                         // not a backref
-                        NodeType = RegexNode.Testgroup;
+                        nodeType = RegexNode.Testgroup;
                         Textto(parenPos - 1);       // jump to the start of the parentheses
                         _ignoreNextParen = true;    // but make sure we don't try to capture the insides
 
@@ -896,42 +990,55 @@ private RegexNode ScanReplacement()
                         if (charsRight >= 3 && RightChar(1) == '?')
                         {
                             char rightchar2 = RightChar(2);
+
                             // disallow comments in the condition
                             if (rightchar2 == '#')
+                            {
                                 throw MakeException(RegexParseError.AlternationCantHaveComment, SR.AlternationCantHaveComment);
+                            }
 
                             // disallow named capture group (?<..>..) in the condition
                             if (rightchar2 == '\'')
+                            {
                                 throw MakeException(RegexParseError.AlternationCantCapture, SR.AlternationCantCapture);
-                            else
+                            }
+
+                            if (charsRight >= 4 && rightchar2 == '<' && RightChar(3) != '!' && RightChar(3) != '=')
                             {
-                                if (charsRight >= 4 && (rightchar2 == '<' && RightChar(3) != '!' && RightChar(3) != '='))
-                                    throw MakeException(RegexParseError.AlternationCantCapture, SR.AlternationCantCapture);
+                                throw MakeException(RegexParseError.AlternationCantCapture, SR.AlternationCantCapture);
                             }
                         }
 
                         break;
 
-
                     default:
                         MoveLeft();
 
-                        NodeType = RegexNode.Group;
+                        nodeType = RegexNode.Group;
                         // Disallow options in the children of a testgroup node
                         if (_group!.NType != RegexNode.Testgroup)
+                        {
                             ScanOptions();
+                        }
+
                         if (CharsRight() == 0)
+                        {
                             goto BreakRecognize;
+                        }
 
                         if ((ch = RightCharMoveRight()) == ')')
+                        {
                             return null;
+                        }
 
                         if (ch != ':')
+                        {
                             goto BreakRecognize;
+                        }
                         break;
                 }
 
-                return new RegexNode(NodeType, _options);
+                return new RegexNode(nodeType, _options);
             }
 
         BreakRecognize:
@@ -951,51 +1058,71 @@ private void ScanBlank()
                 while (true)
                 {
                     while (CharsRight() > 0 && IsSpace(RightChar()))
+                    {
                         MoveRight();
+                    }
 
                     if (CharsRight() == 0)
+                    {
                         break;
+                    }
 
                     if (RightChar() == '#')
                     {
                         while (CharsRight() > 0 && RightChar() != '\n')
+                        {
                             MoveRight();
+                        }
                     }
-                    else if (CharsRight() >= 3 && RightChar(2) == '#' &&
-                             RightChar(1) == '?' && RightChar() == '(')
+                    else if (CharsRight() >= 3 && RightChar(2) == '#' && RightChar(1) == '?' && RightChar() == '(')
                     {
                         while (CharsRight() > 0 && RightChar() != ')')
+                        {
                             MoveRight();
+                        }
+
                         if (CharsRight() == 0)
+                        {
                             throw MakeException(RegexParseError.UnterminatedComment, SR.UnterminatedComment);
+                        }
+
                         MoveRight();
                     }
                     else
+                    {
                         break;
+                    }
                 }
             }
             else
             {
                 while (true)
                 {
-                    if (CharsRight() < 3 || RightChar(2) != '#' ||
-                        RightChar(1) != '?' || RightChar() != '(')
+                    if (CharsRight() < 3 || RightChar(2) != '#' || RightChar(1) != '?' || RightChar() != '(')
+                    {
                         return;
+                    }
 
                     // skip comment (?# ...)
                     while (CharsRight() > 0 && RightChar() != ')')
+                    {
                         MoveRight();
+                    }
+
                     if (CharsRight() == 0)
+                    {
                         throw MakeException(RegexParseError.UnterminatedComment, SR.UnterminatedComment);
+                    }
+
                     MoveRight();
                 }
             }
         }
 
-        /*
-         * Scans chars following a '\' (not counting the '\'), and returns
-         * a RegexNode for the type of atom scanned.
-         */
+        /// <summary>
+        /// Scans chars following a '\' (not counting the '\'), and returns
+        /// a RegexNode for the type of atom scanned.
+        /// </summary>
         private RegexNode? ScanBackslash(bool scanOnly)
         {
             Debug.Assert(CharsRight() > 0, "The current reading position must not be at the end of the pattern");
@@ -1010,67 +1137,53 @@ private void ScanBlank()
                 case 'Z':
                 case 'z':
                     MoveRight();
-                    if (scanOnly)
-                        return null;
-                    return new RegexNode(TypeFromCode(ch), _options);
+                    return scanOnly ? null :
+                        new RegexNode(TypeFromCode(ch), _options);
 
                 case 'w':
                     MoveRight();
-                    if (scanOnly)
-                        return null;
-                    if (UseOptionE())
-                        return new RegexNode(RegexNode.Set, _options, RegexCharClass.ECMAWordClass);
-                    return new RegexNode(RegexNode.Set, _options, RegexCharClass.WordClass);
+                    return scanOnly ? null :
+                        new RegexNode(RegexNode.Set, _options, UseOptionE() ? RegexCharClass.ECMAWordClass : RegexCharClass.WordClass);
 
                 case 'W':
                     MoveRight();
-                    if (scanOnly)
-                        return null;
-                    if (UseOptionE())
-                        return new RegexNode(RegexNode.Set, _options, RegexCharClass.NotECMAWordClass);
-                    return new RegexNode(RegexNode.Set, _options, RegexCharClass.NotWordClass);
+                    return scanOnly ? null :
+                        new RegexNode(RegexNode.Set, _options, UseOptionE() ? RegexCharClass.NotECMAWordClass : RegexCharClass.NotWordClass);
 
                 case 's':
                     MoveRight();
-                    if (scanOnly)
-                        return null;
-                    if (UseOptionE())
-                        return new RegexNode(RegexNode.Set, _options, RegexCharClass.ECMASpaceClass);
-                    return new RegexNode(RegexNode.Set, _options, RegexCharClass.SpaceClass);
+                    return scanOnly ? null :
+                        new RegexNode(RegexNode.Set, _options, UseOptionE() ? RegexCharClass.ECMASpaceClass : RegexCharClass.SpaceClass);
 
                 case 'S':
                     MoveRight();
-                    if (scanOnly)
-                        return null;
-                    if (UseOptionE())
-                        return new RegexNode(RegexNode.Set, _options, RegexCharClass.NotECMASpaceClass);
-                    return new RegexNode(RegexNode.Set, _options, RegexCharClass.NotSpaceClass);
+                    return scanOnly ? null :
+                        new RegexNode(RegexNode.Set, _options, UseOptionE() ? RegexCharClass.NotECMASpaceClass : RegexCharClass.NotSpaceClass);
 
                 case 'd':
                     MoveRight();
-                    if (scanOnly)
-                        return null;
-                    if (UseOptionE())
-                        return new RegexNode(RegexNode.Set, _options, RegexCharClass.ECMADigitClass);
-                    return new RegexNode(RegexNode.Set, _options, RegexCharClass.DigitClass);
+                    return scanOnly ? null :
+                        new RegexNode(RegexNode.Set, _options, UseOptionE() ? RegexCharClass.ECMADigitClass : RegexCharClass.DigitClass);
 
                 case 'D':
                     MoveRight();
-                    if (scanOnly)
-                        return null;
-                    if (UseOptionE())
-                        return new RegexNode(RegexNode.Set, _options, RegexCharClass.NotECMADigitClass);
-                    return new RegexNode(RegexNode.Set, _options, RegexCharClass.NotDigitClass);
+                    return scanOnly ? null :
+                        new RegexNode(RegexNode.Set, _options, UseOptionE() ? RegexCharClass.NotECMADigitClass : RegexCharClass.NotDigitClass);
 
                 case 'p':
                 case 'P':
                     MoveRight();
                     if (scanOnly)
+                    {
                         return null;
+                    }
+
                     var cc = new RegexCharClass();
-                    cc.AddCategoryFromName(ParseProperty(), (ch != 'p'), UseOptionI(), _pattern, _currentPos);
+                    cc.AddCategoryFromName(ParseProperty(), ch != 'p', UseOptionI(), _pattern, _currentPos);
                     if (UseOptionI())
+                    {
                         cc.AddLowercase(_culture);
+                    }
 
                     return new RegexNode(RegexNode.Set, _options, cc.ToStringClass());
 
@@ -1079,13 +1192,13 @@ private void ScanBlank()
             }
         }
 
-        /*
-         * Scans \-style backreferences and character escapes
-         */
+        /// <summary>Scans \-style backreferences and character escapes</summary>
         private RegexNode? ScanBasicBackslash(bool scanOnly)
         {
             if (CharsRight() == 0)
+            {
                 throw MakeException(RegexParseError.IllegalEndEscape, SR.IllegalEndEscape);
+            }
 
             int backpos = Textpos();
             char close = '\0';
@@ -1100,7 +1213,6 @@ private void ScanBlank()
                 {
                     MoveRight();
                     ch = RightCharMoveRight();
-
                     if (ch == '<' || ch == '\'')
                     {
                         angled = true;
@@ -1109,7 +1221,9 @@ private void ScanBlank()
                 }
 
                 if (!angled || CharsRight() <= 0)
+                {
                     throw MakeException(RegexParseError.MalformedNameRef, SR.MalformedNameRef);
+                }
 
                 ch = RightChar();
             }
@@ -1120,7 +1234,6 @@ private void ScanBlank()
             {
                 angled = true;
                 close = (ch == '\'') ? '\'' : '>';
-
                 MoveRight();
                 ch = RightChar();
             }
@@ -1133,11 +1246,9 @@ private void ScanBlank()
 
                 if (CharsRight() > 0 && RightCharMoveRight() == close)
                 {
-                    if (scanOnly)
-                        return null;
-                    if (IsCaptureSlot(capnum))
-                        return new RegexNode(RegexNode.Ref, _options, capnum);
-                    else
+                    return
+                        scanOnly ? null :
+                        IsCaptureSlot(capnum) ? new RegexNode(RegexNode.Ref, _options, capnum) :
                         throw MakeException(RegexParseError.UndefinedBackref, SR.Format(SR.UndefinedBackref, capnum.ToString()));
                 }
             }
@@ -1149,33 +1260,50 @@ private void ScanBlank()
                 if (UseOptionE())
                 {
                     int capnum = -1;
-                    int newcapnum = (int)(ch - '0');
+                    int newcapnum = ch - '0';
                     int pos = Textpos() - 1;
                     while (newcapnum <= _captop)
                     {
                         if (IsCaptureSlot(newcapnum) && (_caps == null || (int)_caps[newcapnum]! < pos))
+                        {
                             capnum = newcapnum;
+                        }
+
                         MoveRight();
                         if (CharsRight() == 0 || (ch = RightChar()) < '0' || ch > '9')
+                        {
                             break;
-                        newcapnum = newcapnum * 10 + (int)(ch - '0');
+                        }
+
+                        newcapnum = newcapnum * 10 + (ch - '0');
                     }
+
                     if (capnum >= 0)
+                    {
                         return scanOnly ? null : new RegexNode(RegexNode.Ref, _options, capnum);
+                    }
                 }
                 else
                 {
                     int capnum = ScanDecimal();
+
                     if (scanOnly)
+                    {
                         return null;
+                    }
+
                     if (IsCaptureSlot(capnum))
+                    {
                         return new RegexNode(RegexNode.Ref, _options, capnum);
-                    else if (capnum <= 9)
+                    }
+
+                    if (capnum <= 9)
+                    {
                         throw MakeException(RegexParseError.UndefinedBackref, SR.Format(SR.UndefinedBackref, capnum.ToString()));
+                    }
                 }
             }
 
-
             // Try to parse backreference: \<foo>
 
             else if (angled && RegexCharClass.IsWordChar(ch))
@@ -1184,11 +1312,9 @@ private void ScanBlank()
 
                 if (CharsRight() > 0 && RightCharMoveRight() == close)
                 {
-                    if (scanOnly)
-                        return null;
-                    if (IsCaptureName(capname))
-                        return new RegexNode(RegexNode.Ref, _options, CaptureSlotFromName(capname));
-                    else
+                    return
+                        scanOnly ? null :
+                        IsCaptureName(capname) ? new RegexNode(RegexNode.Ref, _options, CaptureSlotFromName(capname)) :
                         throw MakeException(RegexParseError.UndefinedNameRef, SR.Format(SR.UndefinedNameRef, capname));
                 }
             }
@@ -1199,7 +1325,9 @@ private void ScanBlank()
             ch = ScanCharEscape();
 
             if (UseOptionI())
+            {
                 ch = _culture.TextInfo.ToLower(ch);
+            }
 
             return scanOnly ? null : new RegexNode(RegexNode.One, _options, ch);
         }
@@ -1210,7 +1338,9 @@ private void ScanBlank()
         private RegexNode ScanDollar()
         {
             if (CharsRight() == 0)
+            {
                 return new RegexNode(RegexNode.One, _options, '$');
+            }
 
             char ch = RightChar();
             bool angled;
@@ -1237,7 +1367,7 @@ private RegexNode ScanDollar()
                 if (!angled && UseOptionE())
                 {
                     int capnum = -1;
-                    int newcapnum = (int)(ch - '0');
+                    int newcapnum = ch - '0';
                     MoveRight();
                     if (IsCaptureSlot(newcapnum))
                     {
@@ -1247,9 +1377,11 @@ private RegexNode ScanDollar()
 
                     while (CharsRight() > 0 && (ch = RightChar()) >= '0' && ch <= '9')
                     {
-                        int digit = (int)(ch - '0');
-                        if (newcapnum > (MaxValueDiv10) || (newcapnum == (MaxValueDiv10) && digit > (MaxValueMod10)))
+                        int digit = ch - '0';
+                        if (newcapnum > MaxValueDiv10 || (newcapnum == MaxValueDiv10 && digit > MaxValueMod10))
+                        {
                             throw MakeException(RegexParseError.CaptureGroupOutOfRange, SR.CaptureGroupOutOfRange);
+                        }
 
                         newcapnum = newcapnum * 10 + digit;
 
@@ -1262,7 +1394,9 @@ private RegexNode ScanDollar()
                     }
                     Textto(lastEndPos);
                     if (capnum >= 0)
+                    {
                         return new RegexNode(RegexNode.Ref, _options, capnum);
+                    }
                 }
                 else
                 {
@@ -1270,18 +1404,18 @@ private RegexNode ScanDollar()
                     if (!angled || CharsRight() > 0 && RightCharMoveRight() == '}')
                     {
                         if (IsCaptureSlot(capnum))
+                        {
                             return new RegexNode(RegexNode.Ref, _options, capnum);
+                        }
                     }
                 }
             }
             else if (angled && RegexCharClass.IsWordChar(ch))
             {
                 string capname = ScanCapname();
-
-                if (CharsRight() > 0 && RightCharMoveRight() == '}')
+                if (CharsRight() > 0 && RightCharMoveRight() == '}' && IsCaptureName(capname))
                 {
-                    if (IsCaptureName(capname))
-                        return new RegexNode(RegexNode.Ref, _options, CaptureSlotFromName(capname));
+                    return new RegexNode(RegexNode.Ref, _options, CaptureSlotFromName(capname));
                 }
             }
             else if (!angled)
@@ -1355,19 +1489,21 @@ private char ScanOctal()
         {
             // Consume octal chars only up to 3 digits and value 0377
             int c = 3;
-            int d;
-            int i;
-
             if (c > CharsRight())
+            {
                 c = CharsRight();
+            }
 
-            for (i = 0; c > 0 && unchecked((uint)(d = RightChar() - '0')) <= 7; c -= 1)
+            int d;
+            int i;
+            for (i = 0; c > 0 && (uint)(d = RightChar() - '0') <= 7; c -= 1)
             {
                 MoveRight();
-                i *= 8;
-                i += d;
+                i = (i * 8) + d;
                 if (UseOptionE() && i >= 0x20)
+                {
                     break;
+                }
             }
 
             // Octal codes only go up to 255.  Any larger and the behavior that Perl follows
@@ -1385,15 +1521,16 @@ private int ScanDecimal()
             int i = 0;
             int d;
 
-            while (CharsRight() > 0 && unchecked((uint)(d = (char)(RightChar() - '0'))) <= 9)
+            while (CharsRight() > 0 && (uint)(d = (char)(RightChar() - '0')) <= 9)
             {
                 MoveRight();
 
-                if (i > (MaxValueDiv10) || (i == (MaxValueDiv10) && d > (MaxValueMod10)))
+                if (i > MaxValueDiv10 || (i == MaxValueDiv10 && d > MaxValueMod10))
+                {
                     throw MakeException(RegexParseError.CaptureGroupOutOfRange, SR.CaptureGroupOutOfRange);
+                }
 
-                i *= 10;
-                i += d;
+                i = (i * 10) + d;
             }
 
             return i;
@@ -1411,13 +1548,14 @@ private char ScanHex(int c)
             {
                 for (; c > 0 && ((d = HexDigit(RightCharMoveRight())) >= 0); c -= 1)
                 {
-                    i *= 0x10;
-                    i += d;
+                    i = (i * 0x10) + d;
                 }
             }
 
             if (c > 0)
+            {
                 throw MakeException(RegexParseError.TooFewHex, SR.TooFewHex);
+            }
 
             return (char)i;
         }
@@ -1432,7 +1570,7 @@ private static int HexDigit(char ch)
             if ((uint)(d = ch - '0') <= 9)
                 return d;
 
-            if (unchecked((uint)(d = ch - 'a')) <= 5)
+            if ((uint)(d = ch - 'a') <= 5)
                 return d + 0xa;
 
             if ((uint)(d = ch - 'A') <= 5)
@@ -1447,34 +1585,34 @@ private static int HexDigit(char ch)
         private char ScanControl()
         {
             if (CharsRight() == 0)
+            {
                 throw MakeException(RegexParseError.MissingControl, SR.MissingControl);
+            }
 
             char ch = RightCharMoveRight();
 
             // \ca interpreted as \cA
 
-            if (ch >= 'a' && ch <= 'z')
+            if ((uint)(ch - 'a') <= 'z' - 'a')
+            {
                 ch = (char)(ch - ('a' - 'A'));
+            }
 
-            if (unchecked(ch = (char)(ch - '@')) < ' ')
+            if ((ch = (char)(ch - '@')) < ' ')
+            {
                 return ch;
+            }
 
             throw MakeException(RegexParseError.UnrecognizedControl, SR.UnrecognizedControl);
         }
 
-        /*
-         * Returns true for options allowed only at the top level
-         */
-        private bool IsOnlyTopOption(RegexOptions options)
-        {
-            return options == RegexOptions.RightToLeft ||
-                options == RegexOptions.CultureInvariant ||
-                options == RegexOptions.ECMAScript;
-        }
+        /// <summary>Returns true for options allowed only at the top level</summary>
+        private bool IsOnlyTopOption(RegexOptions options) =>
+            options == RegexOptions.RightToLeft ||
+            options == RegexOptions.CultureInvariant ||
+            options == RegexOptions.ECMAScript;
 
-        /*
-         * Scans cimsx-cimsx option string, stops at the first unrecognized char.
-         */
+        /// <summary>Scans cimsx-cimsx option string, stops at the first unrecognized char.</summary>
         private void ScanOptions()
         {
             for (bool off = false; CharsRight() > 0; MoveRight())
@@ -1493,19 +1631,23 @@ private void ScanOptions()
                 {
                     RegexOptions options = OptionFromCode(ch);
                     if (options == 0 || IsOnlyTopOption(options))
+                    {
                         return;
+                    }
 
                     if (off)
+                    {
                         _options &= ~options;
+                    }
                     else
+                    {
                         _options |= options;
+                    }
                 }
             }
         }
 
-        /*
-         * Scans \ code for escape codes that map to single Unicode chars.
-         */
+        /// <summary>Scans \ code for escape codes that map to single Unicode chars.</summary>
         private char ScanCharEscape()
         {
             char ch = RightCharMoveRight();
@@ -1542,14 +1684,14 @@ private char ScanCharEscape()
                     return ScanControl();
                 default:
                     if (!UseOptionE() && RegexCharClass.IsWordChar(ch))
+                    {
                         throw MakeException(RegexParseError.UnrecognizedEscape, SR.Format(SR.UnrecognizedEscape, ch));
+                    }
                     return ch;
             }
         }
 
-        /*
-         * Scans X for \p{X} or \P{X}
-         */
+        /// <summary>Scans X for \p{X} or \P{X}</summary>
         private string ParseProperty()
         {
             if (CharsRight() < 3)
@@ -1573,17 +1715,18 @@ private string ParseProperty()
                     break;
                 }
             }
+
             string capname = _pattern.Substring(startpos, Textpos() - startpos);
 
             if (CharsRight() == 0 || RightCharMoveRight() != '}')
+            {
                 throw MakeException(RegexParseError.IncompleteSlashP, SR.IncompleteSlashP);
+            }
 
             return capname;
         }
 
-        /*
-         * Returns ReNode type for zero-length assertions with a \ code.
-         */
+        /// <summary>Returns ReNode type for zero-length assertions with a \ code.</summary>
         private int TypeFromCode(char ch) =>
             ch switch
             {
@@ -1596,14 +1739,14 @@ private int TypeFromCode(char ch) =>
                 _ => RegexNode.Nothing,
             };
 
-        /*
-         * Returns option bit from single-char (?cimsx) code.
-         */
+        /// <summary>Returns option bit from single-char (?cimsx) code.</summary>
         private static RegexOptions OptionFromCode(char ch)
         {
             // case-insensitive
-            if (ch >= 'A' && ch <= 'Z')
+            if ((uint)(ch - 'A') <= 'Z' - 'A')
+            {
                 ch += (char)('a' - 'A');
+            }
 
             return ch switch
             {
@@ -1621,10 +1764,9 @@ private static RegexOptions OptionFromCode(char ch)
             };
         }
 
-        /*
-         * a prescanner for deducing the slots used for
-         * captures by doing a partial tokenization of the pattern.
-         */
+        /// <summary>
+        /// A prescanner for deducing the slots used for captures by doing a partial tokenization of the pattern.
+        /// </summary>
         private void CountCaptures()
         {
             NoteCaptureSlot(0, 0);
@@ -1639,7 +1781,9 @@ private void CountCaptures()
                 {
                     case '\\':
                         if (CharsRight() > 0)
+                        {
                             ScanBackslash(scanOnly: true);
+                        }
                         break;
 
                     case '#':
@@ -1656,7 +1800,9 @@ private void CountCaptures()
 
                     case ')':
                         if (!EmptyOptionsStack())
+                        {
                             PopOptions();
+                        }
                         break;
 
                     case '(':
@@ -1683,12 +1829,14 @@ private void CountCaptures()
 
                                     if (ch != '0' && RegexCharClass.IsWordChar(ch))
                                     {
-                                        //if (_ignoreNextParen)
-                                        //    throw MakeException(SR.AlternationCantCapture);
-                                        if (ch >= '1' && ch <= '9')
+                                        if ((uint)(ch - '1') <= '9' - '1')
+                                        {
                                             NoteCaptureSlot(ScanDecimal(), pos);
+                                        }
                                         else
+                                        {
                                             NoteCaptureName(ScanCapname(), pos);
+                                        }
                                     }
                                 }
                                 else
@@ -1724,7 +1872,9 @@ private void CountCaptures()
                                 // Add unnamend parentheses if ExplicitCapture is not set
                                 // and the next parentheses is not ignored.
                                 if (!UseOptionN() && !_ignoreNextParen)
+                                {
                                     NoteCaptureSlot(_autocap++, pos);
+                                }
                             }
                         }
 
@@ -1736,9 +1886,7 @@ private void CountCaptures()
             AssignNameSlots();
         }
 
-        /*
-         * Notes a used capture slot
-         */
+        /// <summary>Notes a used capture slot</summary>
         private void NoteCaptureSlot(int i, int pos)
         {
             object boxedI = i; // workaround to remove a boxed int when adding to the hashtable
@@ -1756,9 +1904,7 @@ private void NoteCaptureSlot(int i, int pos)
             }
         }
 
-        /*
-         * Notes a used capture slot
-         */
+        /// <summary>Notes a used capture slot</summary>
         private void NoteCaptureName(string name, int pos)
         {
             if (_capnames == null)
@@ -1774,9 +1920,7 @@ private void NoteCaptureName(string name, int pos)
             }
         }
 
-        /*
-         * Assigns unused slot numbers to the capture names
-         */
+        /// <summary>Assigns unused slot numbers to the capture names.</summary>
         private void AssignNameSlots()
         {
             if (_capnames != null)
@@ -1784,7 +1928,10 @@ private void AssignNameSlots()
                 for (int i = 0; i < _capnamelist!.Count; i++)
                 {
                     while (IsCaptureSlot(_autocap))
+                    {
                         _autocap++;
+                    }
+
                     string name = _capnamelist[i];
                     int pos = (int)_capnames[name]!;
                     _capnames[name] = _autocap;
@@ -1852,83 +1999,40 @@ private void AssignNameSlots()
             }
         }
 
-        /*
-         * Looks up the slot number for a given name
-         */
-        private int CaptureSlotFromName(string capname)
-        {
-            return (int)_capnames![capname]!;
-        }
+        /// <summary>Looks up the slot number for a given name.</summary>
+        private int CaptureSlotFromName(string capname) => (int)_capnames![capname]!;
 
-        /*
-         * True if the capture slot was noted
-         */
+        /// <summary>True if the capture slot was noted</summary>
         private bool IsCaptureSlot(int i)
         {
             if (_caps != null)
+            {
                 return _caps.ContainsKey(i);
+            }
 
-            return (i >= 0 && i < _capsize);
+            return i >= 0 && i < _capsize;
         }
 
-        /*
-         * Looks up the slot number for a given name
-         */
-        private bool IsCaptureName(string capname)
-        {
-            if (_capnames == null)
-                return false;
+        /// <summary>Looks up the slot number for a given name</summary>
+        private bool IsCaptureName(string capname) => _capnames != null && _capnames.ContainsKey(capname);
 
-            return _capnames.ContainsKey(capname);
-        }
+        /// <summary>True if N option disabling '(' autocapture is on.</summary>
+        private bool UseOptionN() => (_options & RegexOptions.ExplicitCapture) != 0;
 
-        /*
-         * True if N option disabling '(' autocapture is on.
-         */
-        private bool UseOptionN()
-        {
-            return (_options & RegexOptions.ExplicitCapture) != 0;
-        }
+        /// <summary>True if I option enabling case-insensitivity is on.</summary>
+        private bool UseOptionI() => (_options & RegexOptions.IgnoreCase) != 0;
 
-        /*
-         * True if I option enabling case-insensitivity is on.
-         */
-        private bool UseOptionI()
-        {
-            return (_options & RegexOptions.IgnoreCase) != 0;
-        }
+        /// <summary>True if M option altering meaning of $ and ^ is on.</summary>
+        private bool UseOptionM() => (_options & RegexOptions.Multiline) != 0;
 
-        /*
-         * True if M option altering meaning of $ and ^ is on.
-         */
-        private bool UseOptionM()
-        {
-            return (_options & RegexOptions.Multiline) != 0;
-        }
+        /// <summary>True if S option altering meaning of . is on.</summary>
+        private bool UseOptionS() => (_options & RegexOptions.Singleline) != 0;
 
-        /*
-         * True if S option altering meaning of . is on.
-         */
-        private bool UseOptionS()
-        {
-            return (_options & RegexOptions.Singleline) != 0;
-        }
+        /// <summary> True if X option enabling whitespace/comment mode is on.</summary>
+        private bool UseOptionX() => (_options & RegexOptions.IgnorePatternWhitespace) != 0;
 
-        /*
-         * True if X option enabling whitespace/comment mode is on.
-         */
-        private bool UseOptionX()
-        {
-            return (_options & RegexOptions.IgnorePatternWhitespace) != 0;
-        }
-
-        /*
-         * True if E option enabling ECMAScript behavior is on.
-         */
-        private bool UseOptionE()
-        {
-            return (_options & RegexOptions.ECMAScript) != 0;
-        }
+        /// <summary>True if E option enabling ECMAScript behavior is on.</summary>
+        private bool UseOptionE() => (_options & RegexOptions.ECMAScript) != 0;
 
         private const byte Q = 5;    // quantifier
         private const byte S = 4;    // ordinary stopper
@@ -1936,9 +2040,7 @@ private bool UseOptionE()
         private const byte X = 2;    // whitespace
         private const byte E = 1;    // should be escaped
 
-        /*
-         * For categorizing ASCII characters.
-        */
+        /// <summary>For categorizing ASCII characters.</summary>
         private static ReadOnlySpan<byte> Category => new byte[] {
             // 0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
                0, 0, 0, 0, 0, 0, 0, 0, 0, X, X, 0, X, X, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -1949,29 +2051,14 @@ private bool UseOptionE()
             // '  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};
 
-        /*
-         * Returns true for those characters that terminate a string of ordinary chars.
-         */
-        private static bool IsSpecial(char ch)
-        {
-            return (ch <= '|' && Category[ch] >= S);
-        }
+        /// <summary>Returns true for those characters that terminate a string of ordinary chars.</summary>
+        private static bool IsSpecial(char ch) => ch <= '|' && Category[ch] >= S;
 
-        /*
-         * Returns true for those characters that terminate a string of ordinary chars.
-         */
-        private static bool IsStopperX(char ch)
-        {
-            return (ch <= '|' && Category[ch] >= X);
-        }
+        /// <summary>Returns true for those characters that terminate a string of ordinary chars.</summary>
+        private static bool IsStopperX(char ch) => ch <= '|' && Category[ch] >= X;
 
-        /*
-         * Returns true for those characters that begin a quantifier.
-         */
-        private static bool IsQuantifier(char ch)
-        {
-            return (ch <= '{' && Category[ch] >= Q);
-        }
+        /// <summary>Returns true for those characters that begin a quantifier.</summary>
+        private static bool IsQuantifier(char ch) => ch <= '{' && Category[ch] >= Q;
 
         private bool IsTrueQuantifier()
         {
@@ -1980,64 +2067,54 @@ private bool IsTrueQuantifier()
             int startpos = Textpos();
             char ch = CharAt(startpos);
             if (ch != '{')
+            {
                 return ch <= '{' && Category[ch] >= Q;
+            }
 
             int pos = startpos;
             int nChars = CharsRight();
-            while (--nChars > 0 && (ch = CharAt(++pos)) >= '0' && ch <= '9');
+            while (--nChars > 0 && (uint)((ch = CharAt(++pos)) - '0') <= 9) ;
 
             if (nChars == 0 || pos - startpos == 1)
+            {
                 return false;
+            }
 
             if (ch == '}')
+            {
                 return true;
+            }
 
             if (ch != ',')
+            {
                 return false;
+            }
 
-            while (--nChars > 0 && (ch = CharAt(++pos)) >= '0' && ch <= '9');
+            while (--nChars > 0 && (uint)((ch = CharAt(++pos)) - '0') <= 9) ;
 
             return nChars > 0 && ch == '}';
         }
 
-        /*
-         * Returns true for whitespace.
-         */
-        private static bool IsSpace(char ch)
-        {
-            return (ch <= ' ' && Category[ch] == X);
-        }
-
-        /*
-         * Returns true for chars that should be escaped.
-         */
-        private static bool IsMetachar(char ch)
-        {
-            return (ch <= '|' && Category[ch] >= E);
-        }
+        /// <summary>Returns true for whitespace.</summary>
+        private static bool IsSpace(char ch) => ch <= ' ' && Category[ch] == X;
 
+        /// <summary>Returns true for chars that should be escaped.</summary>
+        private static bool IsMetachar(char ch) => ch <= '|' && Category[ch] >= E;
 
-        /*
-         * Add a string to the last concatenate.
-         */
+        /// <summary>Add a string to the last concatenate.</summary>
         private void AddConcatenate(int pos, int cch, bool isReplacement)
         {
             if (cch == 0)
+            {
                 return;
+            }
 
             RegexNode node;
             if (cch > 1)
             {
-                string str;
-                if (UseOptionI() && !isReplacement)
-                {
-                    str = string.Create(cch, (_pattern, _culture, pos, cch), (span, state) =>
-                        state._pattern.AsSpan(state.pos, state.cch).ToLower(span, state._culture));
-                }
-                else
-                {
-                    str = _pattern.Substring(pos, cch);
-                }
+                string str = UseOptionI() && !isReplacement ?
+                    string.Create(cch, (_pattern, _culture, pos, cch), (span, state) => state._pattern.AsSpan(state.pos, state.cch).ToLower(span, state._culture)) :
+                    _pattern.Substring(pos, cch);
 
                 node = new RegexNode(RegexNode.Multi, _options, str);
             }
@@ -2046,7 +2123,9 @@ private void AddConcatenate(int pos, int cch, bool isReplacement)
                 char ch = _pattern[pos];
 
                 if (UseOptionI() && !isReplacement)
+                {
                     ch = _culture.TextInfo.ToLower(ch);
+                }
 
                 node = new RegexNode(RegexNode.One, _options, ch);
             }
@@ -2054,9 +2133,7 @@ private void AddConcatenate(int pos, int cch, bool isReplacement)
             _concatenation!.AddChild(node);
         }
 
-        /*
-         * Push the parser state (in response to an open paren)
-         */
+        /// <summary>Push the parser state (in response to an open paren)</summary>
         private void PushGroup()
         {
             _group!.Next = _stack;
@@ -2065,9 +2142,7 @@ private void PushGroup()
             _stack = _concatenation;
         }
 
-        /*
-         * Remember the pushed state (in response to a ')')
-         */
+        /// <summary>Remember the pushed state (in response to a ')')</summary>
         private void PopGroup()
         {
             _concatenation = _stack;
@@ -2079,24 +2154,19 @@ private void PopGroup()
             if (_group.Type() == RegexNode.Testgroup && _group.ChildCount() == 0)
             {
                 if (_unit == null)
+                {
                     throw MakeException(RegexParseError.IllegalCondition, SR.IllegalCondition);
+                }
 
                 _group.AddChild(_unit);
                 _unit = null;
             }
         }
 
-        /*
-         * True if the group stack is empty.
-         */
-        private bool EmptyStack()
-        {
-            return _stack == null;
-        }
+        /// <summary>True if the group stack is empty.</summary>
+        private bool EmptyStack() => _stack == null;
 
-        /*
-         * Start a new round for the parser state (in response to an open paren or string start)
-         */
+        /// <summary>Start a new round for the parser state (in response to an open paren or string start)</summary>
         private void StartGroup(RegexNode openGroup)
         {
             _group = openGroup;
@@ -2104,9 +2174,7 @@ private void StartGroup(RegexNode openGroup)
             _concatenation = new RegexNode(RegexNode.Concatenate, _options);
         }
 
-        /*
-         * Finish the current concatenation (in response to a |)
-         */
+        /// <summary>Finish the current concatenation (in response to a |)</summary>
         private void AddAlternate()
         {
             // The | parts inside a Testgroup group go directly to the group
@@ -2123,9 +2191,7 @@ private void AddAlternate()
             _concatenation = new RegexNode(RegexNode.Concatenate, _options);
         }
 
-        /*
-         * Finish the current quantifiable (when a quantifier is not found or is not possible)
-         */
+        /// <summary>Finish the current quantifiable (when a quantifier is not found or is not possible)</summary>
         private void AddConcatenate()
         {
             // The first (| inside a Testgroup group goes directly to the group
@@ -2134,72 +2200,57 @@ private void AddConcatenate()
             _unit = null;
         }
 
-        /*
-         * Finish the current quantifiable (when a quantifier is found)
-         */
+        /// <summary>Finish the current quantifiable (when a quantifier is found)</summary>
         private void AddConcatenate(bool lazy, int min, int max)
         {
             _concatenation!.AddChild(_unit!.MakeQuantifier(lazy, min, max));
             _unit = null;
         }
 
-        /*
-         * Returns the current unit
-         */
-        private RegexNode? Unit()
-        {
-            return _unit;
-        }
+        /// <summary>Returns the current unit</summary>
+        private RegexNode? Unit() => _unit;
 
-        /*
-         * Sets the current unit to a single char node
-         */
+        /// <summary>Sets the current unit to a single char node</summary>
         private void AddUnitOne(char ch)
         {
             if (UseOptionI())
+            {
                 ch = _culture.TextInfo.ToLower(ch);
+            }
 
             _unit = new RegexNode(RegexNode.One, _options, ch);
         }
 
-        /*
-         * Sets the current unit to a single inverse-char node
-         */
+        /// <summary>Sets the current unit to a single inverse-char node</summary>
         private void AddUnitNotone(char ch)
         {
             if (UseOptionI())
+            {
                 ch = _culture.TextInfo.ToLower(ch);
+            }
 
             _unit = new RegexNode(RegexNode.Notone, _options, ch);
         }
 
-        /*
-         * Sets the current unit to a single set node
-         */
+        /// <summary>Sets the current unit to a single set node</summary>
         private void AddUnitSet(string cc)
         {
             _unit = new RegexNode(RegexNode.Set, _options, cc);
         }
 
-        /*
-         * Sets the current unit to a subtree
-         */
+        /// <summary>Sets the current unit to a subtree</summary>
         private void AddUnitNode(RegexNode node)
         {
             _unit = node;
         }
 
-        /*
-         * Sets the current unit to an assertion of the specified type
-         */
+        /// <summary>Sets the current unit to an assertion of the specified type</summary>
         private void AddUnitType(int type)
         {
             _unit = new RegexNode(type, _options);
         }
 
-        /*
-         * Finish the current group (in response to a ')' or end)
-         */
+        /// <summary>Finish the current group (in response to a ')' or end)</summary>
         private void AddGroup()
         {
             if (_group!.Type() == RegexNode.Testgroup || _group.Type() == RegexNode.Testref)
@@ -2207,7 +2258,9 @@ private void AddGroup()
                 _group.AddChild(_concatenation!.ReverseLeft());
 
                 if (_group.Type() == RegexNode.Testref && _group.ChildCount() > 2 || _group.ChildCount() > 3)
+                {
                     throw MakeException(RegexParseError.TooManyAlternates, SR.TooManyAlternates);
+                }
             }
             else
             {
@@ -2218,121 +2271,49 @@ private void AddGroup()
             _unit = _group;
         }
 
-        /*
-         * Saves options on a stack.
-         */
-        private void PushOptions()
-        {
-            _optionsStack.Append(_options);
-        }
+        /// <summary>Saves options on a stack.</summary>
+        private void PushOptions() => _optionsStack.Append(_options);
 
-        /*
-         * Recalls options from the stack.
-         */
-        private void PopOptions()
-        {
-            _options = _optionsStack.Pop();
-        }
+        /// <summary>Recalls options from the stack.</summary>
+        private void PopOptions() => _options = _optionsStack.Pop();
 
-        /*
-         * True if options stack is empty.
-         */
-        private bool EmptyOptionsStack()
-        {
-            return _optionsStack.Length == 0;
-        }
+        /// <summary>True if options stack is empty.</summary>
+        private bool EmptyOptionsStack() => _optionsStack.Length == 0;
 
-        /*
-         * Pops the options stack, but keeps the current options unchanged.
-         */
-        private void PopKeepOptions()
-        {
-            _optionsStack.Length--;
-        }
+        /// <summary>Pops the options stack, but keeps the current options unchanged.</summary>
+        private void PopKeepOptions() => _optionsStack.Length--;
 
-        /*
-         * Fills in a RegexParseException
-         */
-        private RegexParseException MakeException(RegexParseError error, string message)
-        {
-            return new RegexParseException(error, _currentPos, SR.Format(SR.MakeException, _pattern, _currentPos, message));
-        }
+        /// <summary>Fills in a RegexParseException</summary>
+        private RegexParseException MakeException(RegexParseError error, string message) =>
+            new RegexParseException(error, _currentPos, SR.Format(SR.MakeException, _pattern, _currentPos, message));
 
-        /*
-         * Returns the current parsing position.
-         */
-        private int Textpos()
-        {
-            return _currentPos;
-        }
+        /// <summary>Returns the current parsing position.</summary>
+        private int Textpos() => _currentPos;
 
-        /*
-         * Zaps to a specific parsing position.
-         */
-        private void Textto(int pos)
-        {
-            _currentPos = pos;
-        }
+        /// <summary>Zaps to a specific parsing position.</summary>
+        private void Textto(int pos) => _currentPos = pos;
 
-        /*
-         * Returns the char at the right of the current parsing position and advances to the right.
-         */
-        private char RightCharMoveRight()
-        {
-            return _pattern[_currentPos++];
-        }
+        /// <summary>Returns the char at the right of the current parsing position and advances to the right.</summary>
+        private char RightCharMoveRight() => _pattern[_currentPos++];
 
-        /*
-         * Moves the current position to the right.
-         */
-        private void MoveRight()
-        {
-            MoveRight(1);
-        }
+        /// <summary>Moves the current position to the right.</summary>
+        private void MoveRight() => _currentPos++;
 
-        private void MoveRight(int i)
-        {
-            _currentPos += i;
-        }
+        private void MoveRight(int i) => _currentPos += i;
 
-        /*
-         * Moves the current parsing position one to the left.
-         */
-        private void MoveLeft()
-        {
-            --_currentPos;
-        }
+        /// <summary>Moves the current parsing position one to the left.</summary>
+        private void MoveLeft() => --_currentPos;
 
-        /*
-         * Returns the char left of the current parsing position.
-         */
-        private char CharAt(int i)
-        {
-            return _pattern[i];
-        }
+        /// <summary>Returns the char left of the current parsing position.</summary>
+        private char CharAt(int i) => _pattern[i];
 
-        /*
-         * Returns the char right of the current parsing position.
-         */
-        internal char RightChar()
-        {
-            return _pattern[_currentPos];
-        }
+        /// <summary>Returns the char right of the current parsing position.</summary>
+        internal char RightChar() => _pattern[_currentPos];
 
-        /*
-         * Returns the char i chars right of the current parsing position.
-         */
-        private char RightChar(int i)
-        {
-            return _pattern[_currentPos + i];
-        }
+        /// <summary>Returns the char i chars right of the current parsing position.</summary>
+        private char RightChar(int i) => _pattern[_currentPos + i];
 
-        /*
-         * Number of characters to the right of the current parsing position.
-         */
-        private int CharsRight()
-        {
-            return _pattern.Length - _currentPos;
-        }
+        /// <summary>Number of characters to the right of the current parsing position.</summary>
+        private int CharsRight() => _pattern.Length - _currentPos;
     }
 }
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexPrefix.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexPrefix.cs
index 081cace866b0a1..88234cf112ea95 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexPrefix.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexPrefix.cs
@@ -6,6 +6,8 @@ namespace System.Text.RegularExpressions
 {
     internal readonly struct RegexPrefix
     {
+        internal static RegexPrefix Empty { get; } = new RegexPrefix(string.Empty, false);
+
         internal RegexPrefix(string prefix, bool ci)
         {
             Prefix = prefix;
@@ -14,8 +16,6 @@ internal RegexPrefix(string prefix, bool ci)
 
         internal bool CaseInsensitive { get; }
 
-        internal static RegexPrefix Empty { get; } = new RegexPrefix(string.Empty, false);
-
         internal string Prefix { get; }
     }
 }
diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexRunner.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexRunner.cs
index 6cce01045d4818..af52dccb664e1a 100644
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexRunner.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexRunner.cs
@@ -195,10 +195,7 @@ private void StartTimeoutWatch()
             // Environment.TickCount is an int that cycles. We intentionally let timeoutOccursAt
             // overflow it will still stay ahead of Environment.TickCount for comparisons made
             // in DoCheckTimeout():
-            unchecked
-            {
-                _timeoutOccursAt = Environment.TickCount + _timeout;
-            }
+            _timeoutOccursAt = Environment.TickCount + _timeout;
         }
 
         protected void CheckTimeout()
diff --git a/src/libraries/System.Text.RegularExpressions/tests/RegexGroupNameTests.cs b/src/libraries/System.Text.RegularExpressions/tests/RegexGroupNameTests.cs
index d84ee1a72f9b93..b927230c2c8300 100644
--- a/src/libraries/System.Text.RegularExpressions/tests/RegexGroupNameTests.cs
+++ b/src/libraries/System.Text.RegularExpressions/tests/RegexGroupNameTests.cs
@@ -6,6 +6,7 @@
 using System;
 using System.Text.RegularExpressions;
 using System.Collections;
+using System.Diagnostics;
 
 namespace System.Text.RegularExpressionsTests
 {
@@ -17,6 +18,8 @@ public class RegexGroupNameTests
         [Fact]
         public static void NameTests()
         {
+            //Debugger.Launch();
+
             string pattern = @"\b(?<FirstWord>\w+)\s?((\w+)\s)*(?<LastWord>\w+)?(?<Punctuation>\p{Po})";
             string input = "The cow jumped over the moon.";
             Regex regex = new Regex(pattern);
