From aa01724738e959a7ca8585208e3ece9b1993c1d3 Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Mon, 5 Dec 2022 05:32:45 +0100 Subject: [PATCH] Use ranges for short sets where applicable, avoid negated IndexOfAnyValues primary sets --- .../gen/RegexGenerator.Emitter.cs | 50 +++--- .../Text/RegularExpressions/RegexCompiler.cs | 80 +++++----- .../RegexFindOptimizations.cs | 15 +- .../RegularExpressions/RegexPrefixAnalyzer.cs | 143 +++++++++--------- 4 files changed, 148 insertions(+), 140 deletions(-) diff --git a/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Emitter.cs b/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Emitter.cs index 5b29445d350ff..12683cbe7ec94 100644 --- a/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Emitter.cs +++ b/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Emitter.cs @@ -887,19 +887,21 @@ void EmitFixedSet_LeftToRight() }; string indexOf = - primarySet.Chars is not null ? primarySet.Chars!.Length switch + primarySet.Chars is not null ? (primarySet.Negated, primarySet.Chars.Length) switch { - 1 => $"{span}.IndexOf({Literal(primarySet.Chars[0])})", - 2 => $"{span}.IndexOfAny({Literal(primarySet.Chars[0])}, {Literal(primarySet.Chars[1])})", - 3 => $"{span}.IndexOfAny({Literal(primarySet.Chars[0])}, {Literal(primarySet.Chars[1])}, {Literal(primarySet.Chars[2])})", - _ => $"{span}.IndexOfAny({EmitIndexOfAnyValuesOrLiteral(primarySet.Chars, requiredHelpers)})", + (false, 1) => $"{span}.IndexOf({Literal(primarySet.Chars[0])})", + (false, 2) => $"{span}.IndexOfAny({Literal(primarySet.Chars[0])}, {Literal(primarySet.Chars[1])})", + (false, 3) => $"{span}.IndexOfAny({Literal(primarySet.Chars[0])}, {Literal(primarySet.Chars[1])}, {Literal(primarySet.Chars[2])})", + (false, _) => $"{span}.IndexOfAny({EmitIndexOfAnyValuesOrLiteral(primarySet.Chars, requiredHelpers)})", + (true, 1) => $"{span}.IndexOfAnyExcept({Literal(primarySet.Chars[0])})", + _ => throw new InvalidOperationException("Expected that negated sets will have at most 1 value in Chars."), } : - primarySet.AsciiSet is not null ? primarySet.AsciiSet.Value.Negated switch + primarySet.AsciiSet is not null ? primarySet.Negated switch { - false => $"{span}.IndexOfAny({EmitIndexOfAnyValues(primarySet.AsciiSet.Value.Chars, requiredHelpers)})", - true => $"{span}.IndexOfAnyExcept({EmitIndexOfAnyValues(primarySet.AsciiSet.Value.Chars, requiredHelpers)})", + false => $"{span}.IndexOfAny({EmitIndexOfAnyValues(primarySet.AsciiSet, requiredHelpers)})", + _ => throw new InvalidOperationException("Expected AsciiSets not to be negated."), } : - (primarySet.Range.Value.LowInclusive == primarySet.Range.Value.HighInclusive, primarySet.Range.Value.Negated) switch + (primarySet.Range.Value.LowInclusive == primarySet.Range.Value.HighInclusive, primarySet.Negated) switch { (false, false) => $"{span}.IndexOfAnyInRange({Literal(primarySet.Range.Value.LowInclusive)}, {Literal(primarySet.Range.Value.HighInclusive)})", (true, false) => $"{span}.IndexOf({Literal(primarySet.Range.Value.LowInclusive)})", @@ -4440,8 +4442,22 @@ private static bool TryEmitIndexOf( bool negated = RegexCharClass.IsNegated(node.Str) ^ negate; Span setChars = stackalloc char[5]; // current max that's vectorized - int setCharsCount; - if ((setCharsCount = RegexCharClass.GetSetChars(node.Str, setChars)) > 0) + int setCharsCount = RegexCharClass.GetSetChars(node.Str, setChars); + + // Prefer IndexOfAnyInRange over IndexOfAny for sets of 2-5 values that fit in a single range + if (setCharsCount != 1 && RegexCharClass.TryGetSingleRange(node.Str, out char lowInclusive, out char highInclusive)) + { + string indexOfAnyInRangeName = !negated ? + "IndexOfAnyInRange" : + "IndexOfAnyExceptInRange"; + + indexOfExpr = $"{last}{indexOfAnyInRangeName}({Literal(lowInclusive)}, {Literal(highInclusive)})"; + + literalLength = 1; + return true; + } + + if (setCharsCount > 0) { (string indexOfName, string indexOfAnyName) = !negated ? ("IndexOf", "IndexOfAny") : @@ -4460,18 +4476,6 @@ private static bool TryEmitIndexOf( return true; } - if (RegexCharClass.TryGetSingleRange(node.Str, out char lowInclusive, out char highInclusive)) - { - string indexOfAnyInRangeName = !negated ? - "IndexOfAnyInRange" : - "IndexOfAnyExceptInRange"; - - indexOfExpr = $"{last}{indexOfAnyInRangeName}({Literal(lowInclusive)}, {Literal(highInclusive)})"; - - literalLength = 1; - return true; - } - if (RegexCharClass.TryGetAsciiSetChars(node.Str, out char[]? asciiChars)) { string indexOfAnyName = !negated ? 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 0e14fb2278c0b..e3115ddc3665b 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 @@ -888,12 +888,13 @@ void EmitFixedSet_LeftToRight() if (primarySet.Chars is not null) { + Debug.Assert(!primarySet.Negated || primarySet.Chars.Length == 1); switch (primarySet.Chars.Length) { case 1: - // tmp = ...IndexOf(setChars[0]); + // tmp = ...IndexOf{AnyExcept}(setChars[0]); Ldc(primarySet.Chars[0]); - Call(s_spanIndexOfChar); + Call(primarySet.Negated ? s_spanIndexOfAnyExceptChar : s_spanIndexOfChar); break; case 2: @@ -920,8 +921,9 @@ void EmitFixedSet_LeftToRight() } else if (primarySet.AsciiSet is not null) { - LoadIndexOfAnyValues(primarySet.AsciiSet.Value.Chars); - Call(primarySet.AsciiSet.Value.Negated ? s_spanIndexOfAnyExceptIndexOfAnyValues : s_spanIndexOfAnyIndexOfAnyValues); + Debug.Assert(!primarySet.Negated); + LoadIndexOfAnyValues(primarySet.AsciiSet); + Call(s_spanIndexOfAnyIndexOfAnyValues); } else { @@ -929,14 +931,14 @@ void EmitFixedSet_LeftToRight() { // tmp = ...IndexOf{AnyExcept}(low); Ldc(primarySet.Range.Value.LowInclusive); - Call(primarySet.Range.Value.Negated ? s_spanIndexOfAnyExceptChar : s_spanIndexOfChar); + Call(primarySet.Negated ? s_spanIndexOfAnyExceptChar : s_spanIndexOfChar); } else { // tmp = ...IndexOfAny{Except}InRange(low, high); Ldc(primarySet.Range.Value.LowInclusive); Ldc(primarySet.Range.Value.HighInclusive); - Call(primarySet.Range.Value.Negated ? s_spanIndexOfAnyExceptInRange : s_spanIndexOfAnyInRange); + Call(primarySet.Negated ? s_spanIndexOfAnyExceptInRange : s_spanIndexOfAnyInRange); } } @@ -1060,7 +1062,7 @@ void EmitFixedSet_RightToLeft() RegexFindOptimizations.FixedDistanceSet set = _regexTree.FindOptimizations.FixedDistanceSets![0]; Debug.Assert(set.Distance == 0); - if (set.Chars is { Length: 1 }) + if (set.Chars is { Length: 1 } && !set.Negated) { // pos = inputSpan.Slice(0, pos).LastIndexOf(set.Chars[0]); Ldloca(inputSpan); @@ -4999,10 +5001,40 @@ void EmitIndexOf(RegexNode node, bool useLast, bool negate) { bool negated = RegexCharClass.IsNegated(node.Str) ^ negate; - // IndexOfAny{Except}(ch1, ...) Span setChars = stackalloc char[5]; // current max that's vectorized - int setCharsCount; - if ((setCharsCount = RegexCharClass.GetSetChars(node.Str, setChars)) > 0) + int setCharsCount = RegexCharClass.GetSetChars(node.Str, setChars); + + // IndexOfAny{Except}InRange + // Prefer IndexOfAnyInRange over IndexOfAny for sets of 2-5 values that fit in a single range + if (setCharsCount != 1 && RegexCharClass.TryGetSingleRange(node.Str, out char lowInclusive, out char highInclusive)) + { + if (lowInclusive == highInclusive) + { + Ldc(lowInclusive); + Call((useLast, negated) switch + { + (false, false) => s_spanIndexOfChar, + (false, true) => s_spanIndexOfAnyExceptChar, + (true, false) => s_spanLastIndexOfChar, + (true, true) => s_spanLastIndexOfAnyExceptChar, + }); + return; + } + + Ldc(lowInclusive); + Ldc(highInclusive); + Call((useLast, negated) switch + { + (false, false) => s_spanIndexOfAnyInRange, + (false, true) => s_spanIndexOfAnyExceptInRange, + (true, false) => s_spanLastIndexOfAnyInRange, + (true, true) => s_spanLastIndexOfAnyExceptInRange, + }); + return; + } + + // IndexOfAny{Except}(ch1, ...) + if (setCharsCount > 0) { setChars = setChars.Slice(0, setCharsCount); switch (setChars.Length) @@ -5057,34 +5089,6 @@ void EmitIndexOf(RegexNode node, bool useLast, bool negate) } } - // IndexOfAny{Except}InRange - if (RegexCharClass.TryGetSingleRange(node.Str, out char lowInclusive, out char highInclusive)) - { - if (lowInclusive == highInclusive) - { - Ldc(lowInclusive); - Call((useLast, negated) switch - { - (false, false) => s_spanIndexOfChar, - (false, true) => s_spanIndexOfAnyExceptChar, - (true, false) => s_spanLastIndexOfChar, - (true, true) => s_spanLastIndexOfAnyExceptChar, - }); - return; - } - - Ldc(lowInclusive); - Ldc(highInclusive); - Call((useLast, negated) switch - { - (false, false) => s_spanIndexOfAnyInRange, - (false, true) => s_spanIndexOfAnyExceptInRange, - (true, false) => s_spanLastIndexOfAnyInRange, - (true, true) => s_spanLastIndexOfAnyExceptInRange, - }); - return; - } - // IndexOfAny{Except}(IndexOfAnyValues) if (RegexCharClass.TryGetAsciiSetChars(node.Str, out char[]? asciiChars)) { diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexFindOptimizations.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexFindOptimizations.cs index 75e16fbf8a545..14b4c80a308cf 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexFindOptimizations.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexFindOptimizations.cs @@ -159,7 +159,8 @@ public RegexFindOptimizations(RegexNode root, RegexOptions options) // the set's characteristics. if (!compiled && fixedDistanceSets.Count == 1 && - fixedDistanceSets[0].Chars is { Length: 1 }) + fixedDistanceSets[0].Chars is { Length: 1 } && + !fixedDistanceSets[0].Negated) { FixedDistanceLiteral = (fixedDistanceSets[0].Chars![0], null, fixedDistanceSets[0].Distance); FindMode = FindNextStartingPositionMode.FixedDistanceChar_LeftToRight; @@ -241,14 +242,16 @@ public FixedDistanceSet(char[]? chars, string set, int distance) /// The character class description. public string Set; + /// Whether the is negated. + public bool Negated; /// Small list of all of the characters that make up the set, if known; otherwise, null. public char[]? Chars; /// The distance of the set from the beginning of the match. public int Distance; /// As an alternative to , a description of the single range the set represents, if it does. - public (char LowInclusive, char HighInclusive, bool Negated)? Range; + public (char LowInclusive, char HighInclusive)? Range; /// As an alternative to , a description of the set of ASCII characters it represents, if it does. - public (char[] Chars, bool Negated)? AsciiSet; + public char[]? AsciiSet; } /// When in literal after set loop node, gets the literal to search for and the RegexNode representing the leading loop. @@ -273,7 +276,7 @@ private static (string String, int Distance)? FindFixedDistanceString(List textSpan, string set = primarySet.Set; ReadOnlySpan span = textSpan.Slice(pos); - if (chars is not null) + if (chars is not null && !primarySet.Negated) { int i = span.IndexOfAny(chars); if (i >= 0) @@ -621,7 +624,7 @@ public bool TryFindNextStartingPositionLeftToRight(ReadOnlySpan textSpan, int endMinusRequiredLength = textSpan.Length - Math.Max(1, MinRequiredLength); - if (primarySet.Chars is not null) + if (primarySet.Chars is not null && !primarySet.Negated) { for (int inputPosition = pos; inputPosition <= endMinusRequiredLength; inputPosition++) { diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexPrefixAnalyzer.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexPrefixAnalyzer.cs index 88e21595c29a1..c769f9463de10 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexPrefixAnalyzer.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexPrefixAnalyzer.cs @@ -200,31 +200,29 @@ static bool Process(RegexNode node, ref ValueStringBuilder vsb) for (int i = 0; i < results.Count; i++) { RegexFindOptimizations.FixedDistanceSet result = results[i]; - bool negated = RegexCharClass.IsNegated(result.Set); + result.Negated = RegexCharClass.IsNegated(result.Set); - if (!negated) + int count = RegexCharClass.GetSetChars(result.Set, scratch); + if (result.Negated ? (count == 1) : (count > 0)) { - int count = RegexCharClass.GetSetChars(result.Set, scratch); - if (count != 0) - { - result.Chars = scratch.Slice(0, count).ToArray(); - results[i] = result; - } + result.Chars = scratch.Slice(0, count).ToArray(); } - if (thorough && result.Chars is null) + if (thorough) { - if (RegexCharClass.TryGetSingleRange(result.Set, out char lowInclusive, out char highInclusive)) + // Prefer IndexOfAnyInRange over IndexOfAny for sets of 2-5 values that fit in a single range + if (count != 1 && RegexCharClass.TryGetSingleRange(result.Set, out char lowInclusive, out char highInclusive)) { - result.Range = (lowInclusive, highInclusive, negated); - results[i] = result; + result.Chars = null; + result.Range = (lowInclusive, highInclusive); } - else if (RegexCharClass.TryGetAsciiSetChars(result.Set, out char[]? asciiChars)) + else if (result.Chars is null && !result.Negated && RegexCharClass.TryGetAsciiSetChars(result.Set, out char[]? asciiChars)) { - result.AsciiSet = (asciiChars, negated); - results[i] = result; + result.AsciiSet = asciiChars; } } + + results[i] = result; } return results; @@ -440,52 +438,53 @@ static bool TryFindFixedSets(RegexNode node, List results) => // Finally, try to move the "best" results to be earlier. "best" here are ones we're able to search // for the fastest and that have the best chance of matching as few false positives as possible. - results.Sort((s1, s2) => + results.Sort(static (s1, s2) => { - char[]? s1Chars = s1.Chars ?? s1.AsciiSet?.Chars; - char[]? s2Chars = s2.Chars ?? s2.AsciiSet?.Chars; + char[]? s1Chars = s1.Chars ?? s1.AsciiSet; + char[]? s2Chars = s2.Chars ?? s2.AsciiSet; int s1CharsLength = s1Chars?.Length ?? 0; int s2CharsLength = s2Chars?.Length ?? 0; - bool s1Negated = s1.AsciiSet.GetValueOrDefault().Negated; - bool s2Negated = s2.AsciiSet.GetValueOrDefault().Negated; + bool s1Negated = s1.Negated; + bool s2Negated = s2.Negated; + int s1RangeLength = s1.Range is not null ? GetRangeLength(s1.Range.Value, s1Negated) : 0; + int s2RangeLength = s2.Range is not null ? GetRangeLength(s2.Range.Value, s2Negated) : 0; - if (s1Negated) + if (s1Negated && s1CharsLength > 0) { - s1CharsLength = char.MaxValue - s1CharsLength; + s1CharsLength = char.MaxValue + 1 - s1CharsLength; } - if (s2Negated) + if (s2Negated && s2CharsLength > 0) { - s2CharsLength = char.MaxValue - s2CharsLength; + s2CharsLength = char.MaxValue + 1 - s2CharsLength; } // If both have chars, prioritize the one with the smaller frequency for those chars. if (s1Chars is not null && s2Chars is not null) { - // If they have different lengths, prefer the shorter one. - if (s1CharsLength != s2CharsLength) + // If one is negated and the other isn't, prefer the non-negated one. + if (s1Negated != s2Negated) { - return s1CharsLength.CompareTo(s2CharsLength); + return s1Negated ? 1 : -1; } - Debug.Assert(s1Negated == s2Negated, "The lengths should have been different"); - - // Then of the ones that are the same length, prefer those with less frequent values. The frequency is - // only an approximation, used as a tie-breaker when we'd otherwise effectively be picking randomly. True - // frequencies will vary widely based on the actual data being searched, the language of the data, etc. + // Prefer sets with less frequent values. The frequency is only an approximation, + // used as a tie-breaker when we'd otherwise effectively be picking randomly. + // True frequencies will vary widely based on the actual data being searched, the language of the data, etc. float s1Frequency = SumFrequencies(s1Chars); float s2Frequency = SumFrequencies(s2Chars); - if (s1Negated) + if (s1Frequency != s2Frequency) { - s1Frequency = -s1Frequency; - s2Frequency = -s2Frequency; + return s1Negated + ? s2Frequency.CompareTo(s1Frequency) + : s1Frequency.CompareTo(s2Frequency); } - int c = s1Frequency.CompareTo(s2Frequency); - if (c != 0) + if (!RegexCharClass.IsAscii(s1Chars) && !RegexCharClass.IsAscii(s2Chars)) { - return c; + // Prefer the set with fewer values. + return s1CharsLength.CompareTo(s2CharsLength); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -494,45 +493,59 @@ static float SumFrequencies(char[] chars) float sum = 0; foreach (char c in chars) { - // Lookup each character in the table. For values > 255, this will end up truncating + // Lookup each character in the table. Values >= 128 are ignored // and thus we'll get skew in the data. It's already a gross approximation, though, // and it is primarily meant for disambiguation of ASCII letters. - sum += s_frequency[(byte)c]; + if (c < 128) + { + sum += s_frequency[c]; + } } return sum; } } + // If one has chars and the other has a range, prefer the shorter set. + if ((s1CharsLength > 0 && s2RangeLength > 0) || (s1RangeLength > 0 && s2CharsLength > 0)) + { + int c = Math.Max(s1CharsLength, s1RangeLength).CompareTo(Math.Max(s2CharsLength, s2RangeLength)); + if (c != 0) + { + return c; + } + + // If lengths are the same, prefer the chars. + return s1CharsLength > 0 ? -1 : 1; + } + // If one has chars and the other doesn't, prioritize the one with chars. - if ((s1.Chars is not null) != (s2.Chars is not null)) + if ((s1CharsLength > 0) != (s2CharsLength > 0)) { - return s1.Chars is not null ? -1 : 1; + return s1CharsLength > 0 ? -1 : 1; } // If one has a range and the other doesn't, prioritize the one with a range. - if ((s1.Range is not null) != (s2.Range is not null)) + if ((s1RangeLength > 0) != (s2RangeLength > 0)) { - return s1.Range is not null ? -1 : 1; + return s1RangeLength > 0 ? -1 : 1; } // If both have ranges, prefer the one that includes fewer characters. - if (s1.Range is not null) + if (s1RangeLength > 0) { - return - GetRangeLength(s1.Range.GetValueOrDefault()).CompareTo( - GetRangeLength(s2.Range.GetValueOrDefault())); - - static int GetRangeLength((char LowInclusive, char HighInclusive, bool Negated) range) - { - int length = range.HighInclusive - range.LowInclusive + 1; - return range.Negated ? - char.MaxValue + 1 - length : - length; - } + return s1RangeLength.CompareTo(s2RangeLength); } // As a tiebreaker, prioritize the earlier one. return s1.Distance.CompareTo(s2.Distance); + + static int GetRangeLength((char LowInclusive, char HighInclusive) range, bool negated) + { + int length = range.HighInclusive - range.LowInclusive + 1; + return negated ? + char.MaxValue + 1 - length : + length; + } }); /// @@ -947,22 +960,6 @@ private static RegexNodeKind FindLeadingOrTrailingAnchor(RegexNode node, bool le 1.024f /* ' h' */, 3.750f /* ' i' */, 0.286f /* ' j' */, 0.439f /* ' k' */, 2.913f /* ' l' */, 1.459f /* ' m' */, 3.908f /* ' n' */, 3.230f /* ' o' */, 1.444f /* ' p' */, 0.231f /* ' q' */, 4.220f /* ' r' */, 3.924f /* ' s' */, 5.312f /* ' t' */, 2.112f /* ' u' */, 0.737f /* ' v' */, 0.573f /* ' w' */, 0.992f /* ' x' */, 1.067f /* ' y' */, 0.181f /* ' z' */, 0.391f /* ' {' */, 0.056f /* ' |' */, 0.391f /* ' }' */, 0.002f /* ' ~' */, 0.000f /* '\x7F' */, - 0.000f /* '\x80' */, 0.000f /* '\x81' */, 0.000f /* '\x82' */, 0.000f /* '\x83' */, 0.000f /* '\x84' */, 0.000f /* '\x85' */, 0.000f /* '\x86' */, 0.000f /* '\x87' */, - 0.000f /* '\x88' */, 0.000f /* '\x89' */, 0.000f /* '\x8A' */, 0.000f /* '\x8B' */, 0.000f /* '\x8C' */, 0.000f /* '\x8D' */, 0.000f /* '\x8E' */, 0.000f /* '\x8F' */, - 0.000f /* '\x90' */, 0.000f /* '\x91' */, 0.000f /* '\x92' */, 0.000f /* '\x93' */, 0.000f /* '\x94' */, 0.000f /* '\x95' */, 0.000f /* '\x96' */, 0.000f /* '\x97' */, - 0.000f /* '\x98' */, 0.000f /* '\x99' */, 0.000f /* '\x9A' */, 0.000f /* '\x9B' */, 0.000f /* '\x9C' */, 0.000f /* '\x9D' */, 0.000f /* '\x9E' */, 0.000f /* '\x9F' */, - 0.000f /* '\xA0' */, 0.000f /* '\xA1' */, 0.000f /* '\xA2' */, 0.000f /* '\xA3' */, 0.000f /* '\xA4' */, 0.000f /* '\xA5' */, 0.000f /* '\xA6' */, 0.000f /* '\xA7' */, - 0.000f /* '\xA8' */, 0.000f /* '\xA9' */, 0.000f /* '\xAA' */, 0.000f /* '\xAB' */, 0.000f /* '\xAC' */, 0.000f /* '\xAD' */, 0.000f /* '\xAE' */, 0.000f /* '\xAF' */, - 0.000f /* '\xB0' */, 0.000f /* '\xB1' */, 0.000f /* '\xB2' */, 0.000f /* '\xB3' */, 0.000f /* '\xB4' */, 0.000f /* '\xB5' */, 0.000f /* '\xB6' */, 0.000f /* '\xB7' */, - 0.000f /* '\xB8' */, 0.000f /* '\xB9' */, 0.000f /* '\xBA' */, 0.000f /* '\xBB' */, 0.000f /* '\xBC' */, 0.000f /* '\xBD' */, 0.000f /* '\xBE' */, 0.000f /* '\xBF' */, - 0.000f /* '\xC0' */, 0.000f /* '\xC1' */, 0.000f /* '\xC2' */, 0.000f /* '\xC3' */, 0.000f /* '\xC4' */, 0.000f /* '\xC5' */, 0.000f /* '\xC6' */, 0.000f /* '\xC7' */, - 0.000f /* '\xC8' */, 0.000f /* '\xC9' */, 0.000f /* '\xCA' */, 0.000f /* '\xCB' */, 0.000f /* '\xCC' */, 0.000f /* '\xCD' */, 0.000f /* '\xCE' */, 0.000f /* '\xCF' */, - 0.000f /* '\xD0' */, 0.000f /* '\xD1' */, 0.000f /* '\xD2' */, 0.000f /* '\xD3' */, 0.000f /* '\xD4' */, 0.000f /* '\xD5' */, 0.000f /* '\xD6' */, 0.000f /* '\xD7' */, - 0.000f /* '\xD8' */, 0.000f /* '\xD9' */, 0.000f /* '\xDA' */, 0.000f /* '\xDB' */, 0.000f /* '\xDC' */, 0.000f /* '\xDD' */, 0.000f /* '\xDE' */, 0.000f /* '\xDF' */, - 0.000f /* '\xE0' */, 0.000f /* '\xE1' */, 0.000f /* '\xE2' */, 0.000f /* '\xE3' */, 0.000f /* '\xE4' */, 0.000f /* '\xE5' */, 0.000f /* '\xE6' */, 0.000f /* '\xE7' */, - 0.000f /* '\xE8' */, 0.000f /* '\xE9' */, 0.000f /* '\xEA' */, 0.000f /* '\xEB' */, 0.000f /* '\xEC' */, 0.000f /* '\xED' */, 0.000f /* '\xEE' */, 0.000f /* '\xEF' */, - 0.000f /* '\xF0' */, 0.000f /* '\xF1' */, 0.000f /* '\xF2' */, 0.000f /* '\xF3' */, 0.000f /* '\xF4' */, 0.000f /* '\xF5' */, 0.000f /* '\xF6' */, 0.000f /* '\xF7' */, - 0.000f /* '\xF8' */, 0.000f /* '\xF9' */, 0.000f /* '\xFA' */, 0.000f /* '\xFB' */, 0.000f /* '\xFC' */, 0.000f /* '\xFD' */, 0.000f /* '\xFE' */, 0.000f /* '\xFF' */, }; // The above table was generated programmatically with the following. This can be augmented to incorporate additional data sources, @@ -992,7 +989,7 @@ private static RegexNodeKind FindLeadingOrTrailingAnchor(RegexNode node, bool le // Console.WriteLine("private static readonly float[] s_frequency = new float[]"); // Console.WriteLine("{"); // int i = 0; - // for (int row = 0; row < 32; row++) + // for (int row = 0; row < 16; row++) // { // Console.Write(" "); // for (int col = 0; col < 8; col++)