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 MemoryExtensions.AsSpan(x, IsLeft ? Index : (x.Length + Index), Count).SequenceEqual(MemoryExtensions.AsSpan(y, IsLeft ? Index : (y.Length + Index), Count));
}
public override int GetHashCode(string s)
{
return Hashing.GetHashCodeOrdinal(MemoryExtensions.AsSpan(s, IsLeft ? Index : (s.Length + Index), Count));
}
}
private sealed class JustifiedCaseInsensitiveSubstringComparer : SubstringComparer
{
public override bool Equals(string x, string y)
{
return MemoryExtensions.Equals(MemoryExtensions.AsSpan(x, IsLeft ? Index : (x.Length + Index), Count), MemoryExtensions.AsSpan(y, IsLeft ? Index : (y.Length + Index), Count), StringComparison.OrdinalIgnoreCase);
}
public override int GetHashCode(string s)
{
return Hashing.GetHashCodeOrdinalIgnoreCase(MemoryExtensions.AsSpan(s, IsLeft ? Index : (s.Length + Index), Count));
}
}
private sealed class JustifiedCaseInsensitiveAsciiSubstringComparer : SubstringComparer
{
public override bool Equals(string x, string y)
{
return MemoryExtensions.Equals(MemoryExtensions.AsSpan(x, IsLeft ? Index : (x.Length + Index), Count), MemoryExtensions.AsSpan(y, IsLeft ? Index : (y.Length + Index), Count), StringComparison.OrdinalIgnoreCase);
}
public override int GetHashCode(string s)
{
return Hashing.GetHashCodeOrdinalIgnoreCaseAscii(MemoryExtensions.AsSpan(s, 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)
{
bool allUniqueStringsAreConfirmedAscii = ignoreCase && AreAllAscii(uniqueStrings);
if (minLength != 0 && TryUseSubstring(uniqueStrings, allUniqueStringsAreConfirmedAscii, ignoreCase, minLength, maxLength, out AnalysisResults results))
return results;
results = CreateAnalysisResults(uniqueStrings, allUniqueStringsAreConfirmedAscii, ignoreCase, minLength, maxLength, 0, 0, (string s, int _, int _) => MemoryExtensions.AsSpan(s));
return results;
}
private static bool TryUseSubstring(ReadOnlySpan<string> uniqueStrings, bool allUniqueStringsAreConfirmedAscii, bool ignoreCase, int minLength, int maxLength, out AnalysisResults results)
{
int acceptableNonUniqueCount = uniqueStrings.Length / 20;
SubstringComparer substringComparer = (!ignoreCase) ? new JustifiedSubstringComparer() : (allUniqueStringsAreConfirmedAscii ? ((SubstringComparer)new JustifiedCaseInsensitiveAsciiSubstringComparer()) : ((SubstringComparer)new JustifiedCaseInsensitiveSubstringComparer()));
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, allUniqueStringsAreConfirmedAscii, ignoreCase, minLength, maxLength, j, i, (string s, int index, int count) => MemoryExtensions.AsSpan(s, 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, allUniqueStringsAreConfirmedAscii, ignoreCase, minLength, maxLength, substringComparer.Index, i, (string s, int index, int count) => MemoryExtensions.AsSpan(s, s.Length + index, count));
return true;
}
}
}
}
results = default(AnalysisResults);
return false;
}
private static AnalysisResults CreateAnalysisResults(ReadOnlySpan<string> uniqueStrings, bool allUniqueStringsAreConfirmedAscii, bool ignoreCase, int minLength, int maxLength, int index, int count, GetSpan getHashString)
{
bool allAsciiIfIgnoreCase = true;
if (ignoreCase) {
bool flag = true;
ReadOnlySpan<string> readOnlySpan = uniqueStrings;
foreach (string text in readOnlySpan) {
if (!allUniqueStringsAreConfirmedAscii && !IsAllAscii(getHashString(text, index, count))) {
allAsciiIfIgnoreCase = false;
flag = false;
break;
}
if (flag && ((count > 0 && !allUniqueStringsAreConfirmedAscii && !IsAllAscii(MemoryExtensions.AsSpan(text))) || ContainsAnyAsciiLetters(MemoryExtensions.AsSpan(text)))) {
flag = false;
if (allUniqueStringsAreConfirmedAscii)
break;
}
}
if (flag)
ignoreCase = false;
}
return new AnalysisResults(ignoreCase, allAsciiIfIgnoreCase, index, count, minLength, maxLength);
}
private static bool AreAllAscii(ReadOnlySpan<string> strings)
{
ReadOnlySpan<string> readOnlySpan = strings;
for (int i = 0; i < readOnlySpan.Length; i++) {
if (!IsAllAscii(MemoryExtensions.AsSpan(readOnlySpan[i])))
return false;
}
return true;
}
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|5_0(*ptr2 | ptr2[1]))
return false;
ptr2 += 2;
}
char* ptr3 = (char*)ptr2;
while (num-- > 0) {
char* intPtr = ptr3;
ptr3 = intPtr + 1;
if (*intPtr >= '')
return false;
}
}
return true;
}
internal static bool ContainsAnyAsciiLetters(ReadOnlySpan<char> s)
{
ReadOnlySpan<char> readOnlySpan = s;
for (int i = 0; i < readOnlySpan.Length; i++) {
if ((uint)((readOnlySpan[i] | 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;
}
}
}