KeyAnalyzer
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace System.Collections.Frozen
{
internal static class KeyAnalyzer
{
private delegate ReadOnlySpan<char> GetSpan (string s, int index, int count);
internal readonly struct AnalysisResults
{
public bool IgnoreCase { get; }
public bool AllAsciiIfIgnoreCase { get; }
public int HashIndex { get; }
public int HashCount { get; }
public int MinimumLength { get; }
public int MaximumLengthDiff { get; }
public bool SubstringHashing => HashCount != 0;
public bool RightJustifiedSubstring => HashIndex < 0;
public AnalysisResults(bool ignoreCase, bool allAsciiIfIgnoreCase, int hashIndex, int hashCount, int minLength, int maxLength)
{
IgnoreCase = ignoreCase;
AllAsciiIfIgnoreCase = allAsciiIfIgnoreCase;
HashIndex = hashIndex;
HashCount = hashCount;
MinimumLength = minLength;
MaximumLengthDiff = maxLength - minLength;
}
}
private abstract class SubstringComparer : IEqualityComparer<string>
{
public int Index;
public int Count;
public bool IsLeft;
public abstract bool Equals(string x, string y);
public abstract int GetHashCode(string s);
}
private sealed class JustifiedSubstringComparer : SubstringComparer
{
public override bool Equals(string x, string y)
{
return x.AsSpan(IsLeft ? Index : (x.Length + Index), Count).SequenceEqual(y.AsSpan(IsLeft ? Index : (y.Length + Index), Count));
}
public override int GetHashCode(string s)
{
return Hashing.GetHashCodeOrdinal(s.AsSpan(IsLeft ? Index : (s.Length + Index), Count));
}
}
private sealed class JustifiedCaseInsensitiveSubstringComparer : SubstringComparer
{
public override bool Equals(string x, string y)
{
return MemoryExtensions.Equals(x.AsSpan(IsLeft ? Index : (x.Length + Index), Count), y.AsSpan(IsLeft ? Index : (y.Length + Index), Count), StringComparison.OrdinalIgnoreCase);
}
public override int GetHashCode(string s)
{
return Hashing.GetHashCodeOrdinalIgnoreCase(s.AsSpan(IsLeft ? Index : (s.Length + Index), Count));
}
}
public static AnalysisResults Analyze([System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})] ReadOnlySpan<string> uniqueStrings, bool ignoreCase, int minLength, int maxLength)
{
if (minLength != 0 && TryUseSubstring(uniqueStrings, ignoreCase, minLength, maxLength, out AnalysisResults results))
return results;
results = CreateAnalysisResults(uniqueStrings, ignoreCase, minLength, maxLength, 0, 0, false, (string s, int _, int _) => s.AsSpan());
return results;
}
private static bool TryUseSubstring(ReadOnlySpan<string> uniqueStrings, bool ignoreCase, int minLength, int maxLength, out AnalysisResults results)
{
int length = uniqueStrings.Length;
int acceptableNonUniqueCount = length / 20;
SubstringComparer substringComparer = ignoreCase ? ((SubstringComparer)new JustifiedCaseInsensitiveSubstringComparer()) : ((SubstringComparer)new JustifiedSubstringComparer());
HashSet<string> set = new HashSet<string>(substringComparer);
int num = Math.Min(minLength, 8);
for (int i = 1; i <= num; i++) {
substringComparer.IsLeft = true;
substringComparer.Count = i;
for (int j = 0; j <= minLength - i; j++) {
substringComparer.Index = j;
if (HasSufficientUniquenessFactor(set, uniqueStrings, acceptableNonUniqueCount)) {
results = CreateAnalysisResults(uniqueStrings, ignoreCase, minLength, maxLength, j, i, true, (string s, int index, int count) => s.AsSpan(index, count));
return true;
}
}
if (minLength != maxLength) {
substringComparer.IsLeft = false;
for (int k = 0; k <= minLength - i; k++) {
substringComparer.Index = -k - i;
if (HasSufficientUniquenessFactor(set, uniqueStrings, acceptableNonUniqueCount)) {
results = CreateAnalysisResults(uniqueStrings, ignoreCase, minLength, maxLength, substringComparer.Index, i, true, (string s, int index, int count) => s.AsSpan(s.Length + index, count));
return true;
}
}
}
}
results = default(AnalysisResults);
return false;
}
private static AnalysisResults CreateAnalysisResults(ReadOnlySpan<string> uniqueStrings, bool ignoreCase, int minLength, int maxLength, int index, int count, bool isSubstring, GetSpan getSubstringSpan)
{
bool allAsciiIfIgnoreCase = true;
if (ignoreCase) {
bool flag = !isSubstring;
ReadOnlySpan<string> readOnlySpan = uniqueStrings;
foreach (string s in readOnlySpan) {
ReadOnlySpan<char> s2 = getSubstringSpan(s, index, count);
if (!IsAllAscii(s2)) {
allAsciiIfIgnoreCase = false;
flag = false;
break;
}
if (flag && ContainsAnyLetters(s2))
flag = false;
}
if (flag)
ignoreCase = false;
}
return new AnalysisResults(ignoreCase, allAsciiIfIgnoreCase, index, count, minLength, maxLength);
}
internal unsafe static bool IsAllAscii(ReadOnlySpan<char> s)
{
fixed (char* ptr = &s.GetPinnableReference()) {
uint* ptr2 = (uint*)ptr;
int num;
for (num = s.Length; num >= 4; num -= 4) {
if (!<IsAllAscii>g__AllCharsInUInt32AreAscii|4_0(*ptr2 | ptr2[1]))
return false;
ptr2 += 2;
}
char* ptr3 = (char*)ptr2;
while (num-- > 0) {
char* intPtr = ptr3;
ptr3 = intPtr + 1;
char c = *intPtr;
if (c >= '')
return false;
}
}
return true;
}
internal static bool ContainsAnyLetters(ReadOnlySpan<char> s)
{
ReadOnlySpan<char> readOnlySpan = s;
foreach (char c in readOnlySpan) {
if ((uint)((c | 32) - 97) <= 25)
return true;
}
return false;
}
[System.Runtime.CompilerServices.NullableContext(1)]
internal static bool HasSufficientUniquenessFactor(HashSet<string> set, [System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})] ReadOnlySpan<string> uniqueStrings, int acceptableNonUniqueCount)
{
set.Clear();
ReadOnlySpan<string> readOnlySpan = uniqueStrings;
foreach (string item in readOnlySpan) {
if (!set.Add(item) && --acceptableNonUniqueCount < 0)
return false;
}
return true;
}
}
}