KeyAnalyzer
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace System.Collections.Frozen
{
internal static class KeyAnalyzer
{
internal struct AnalysisResults
{
public bool AllAscii {
[System.Runtime.CompilerServices.IsReadOnly]
get;
}
public bool IgnoreCase {
[System.Runtime.CompilerServices.IsReadOnly]
get;
}
public int MinimumLength {
[System.Runtime.CompilerServices.IsReadOnly]
get;
set;
}
public int MaximumLengthDiff {
[System.Runtime.CompilerServices.IsReadOnly]
get;
set;
}
public int HashIndex {
[System.Runtime.CompilerServices.IsReadOnly]
get;
}
public int HashCount {
[System.Runtime.CompilerServices.IsReadOnly]
get;
}
public bool SubstringHashing => HashCount != 0;
public bool RightJustifiedSubstring => HashIndex < 0;
public AnalysisResults(bool allAscii, bool ignoreCase, int minimumLength, int maximumLengthDiff, int hashIndex, int hashCount)
{
AllAscii = allAscii;
IgnoreCase = ignoreCase;
MinimumLength = minimumLength;
MaximumLengthDiff = maximumLengthDiff;
HashIndex = hashIndex;
HashCount = hashCount;
}
}
private abstract class SubstringComparer : IEqualityComparer<string>
{
public int Index;
public int Count;
public abstract bool Equals(string x, string y);
public abstract int GetHashCode(string s);
}
private sealed class LeftJustifiedSubstringComparer : SubstringComparer
{
public override bool Equals(string x, string y)
{
return MemoryExtensions.AsSpan(x, Index, Count).SequenceEqual(MemoryExtensions.AsSpan(y, Index, Count));
}
public override int GetHashCode(string s)
{
return Hashing.GetHashCodeOrdinal(MemoryExtensions.AsSpan(s, Index, Count));
}
}
private sealed class LeftJustifiedCaseInsensitiveSubstringComparer : SubstringComparer
{
public override bool Equals(string x, string y)
{
return MemoryExtensions.Equals(MemoryExtensions.AsSpan(x, Index, Count), MemoryExtensions.AsSpan(y, Index, Count), StringComparison.OrdinalIgnoreCase);
}
public override int GetHashCode(string s)
{
return Hashing.GetHashCodeOrdinalIgnoreCase(MemoryExtensions.AsSpan(s, Index, Count));
}
}
private sealed class RightJustifiedSubstringComparer : SubstringComparer
{
public override bool Equals(string x, string y)
{
return MemoryExtensions.AsSpan(x, x.Length + Index, Count).SequenceEqual(MemoryExtensions.AsSpan(y, y.Length + Index, Count));
}
public override int GetHashCode(string s)
{
return Hashing.GetHashCodeOrdinal(MemoryExtensions.AsSpan(s, s.Length + Index, Count));
}
}
private sealed class RightJustifiedCaseInsensitiveSubstringComparer : SubstringComparer
{
public override bool Equals(string x, string y)
{
return MemoryExtensions.Equals(MemoryExtensions.AsSpan(x, x.Length + Index, Count), MemoryExtensions.AsSpan(y, y.Length + Index, Count), StringComparison.OrdinalIgnoreCase);
}
public override int GetHashCode(string s)
{
return Hashing.GetHashCodeOrdinalIgnoreCase(MemoryExtensions.AsSpan(s, s.Length + Index, Count));
}
}
public static void Analyze([System.Runtime.CompilerServices.Nullable(new byte[] {
0,
1
})] ReadOnlySpan<string> uniqueStrings, bool ignoreCase, out AnalysisResults results)
{
if (!UseSubstring(uniqueStrings, ignoreCase, out results))
UseFullString(uniqueStrings, ignoreCase, out results);
int num = 2147483647;
int num2 = 0;
ReadOnlySpan<string> readOnlySpan = uniqueStrings;
foreach (string text in readOnlySpan) {
if (text.Length < num)
num = text.Length;
if (text.Length > num2)
num2 = text.Length;
}
results.MinimumLength = num;
results.MaximumLengthDiff = num2 - num;
}
private static bool UseSubstring(ReadOnlySpan<string> uniqueStrings, bool ignoreCase, out AnalysisResults results)
{
int num = 2147483647;
ReadOnlySpan<string> readOnlySpan = uniqueStrings;
foreach (string text in readOnlySpan) {
if (text.Length < num)
num = text.Length;
}
SubstringComparer substringComparer = ignoreCase ? ((SubstringComparer)new LeftJustifiedCaseInsensitiveSubstringComparer()) : ((SubstringComparer)new LeftJustifiedSubstringComparer());
SubstringComparer substringComparer2 = ignoreCase ? ((SubstringComparer)new RightJustifiedCaseInsensitiveSubstringComparer()) : ((SubstringComparer)new RightJustifiedSubstringComparer());
HashSet<string> set = new HashSet<string>(substringComparer);
HashSet<string> set2 = new HashSet<string>(substringComparer2);
for (int j = 1; j <= num; j++) {
for (int k = 0; k <= num - j; k++) {
substringComparer.Index = k;
substringComparer.Count = j;
double uniquenessFactor = GetUniquenessFactor(set, uniqueStrings);
if (uniquenessFactor >= 0.95) {
bool allAscii = true;
ReadOnlySpan<string> readOnlySpan2 = uniqueStrings;
foreach (string text2 in readOnlySpan2) {
if (!IsAllAscii(MemoryExtensions.AsSpan(text2, substringComparer.Index, substringComparer.Count))) {
allAscii = false;
break;
}
}
results = new AnalysisResults(allAscii, ignoreCase, 0, 0, substringComparer.Index, substringComparer.Count);
return true;
}
substringComparer2.Index = -k - j;
substringComparer2.Count = j;
uniquenessFactor = GetUniquenessFactor(set2, uniqueStrings);
if (uniquenessFactor >= 0.95) {
bool allAscii2 = true;
ReadOnlySpan<string> readOnlySpan3 = uniqueStrings;
foreach (string text3 in readOnlySpan3) {
if (!IsAllAscii(MemoryExtensions.AsSpan(text3, text3.Length + substringComparer2.Index, substringComparer2.Count))) {
allAscii2 = false;
break;
}
}
results = new AnalysisResults(allAscii2, ignoreCase, 0, 0, substringComparer2.Index, substringComparer2.Count);
return true;
}
}
}
results = default(AnalysisResults);
return false;
}
private static void UseFullString(ReadOnlySpan<string> uniqueStrings, bool ignoreCase, out AnalysisResults results)
{
bool allAscii = true;
ReadOnlySpan<string> readOnlySpan = uniqueStrings;
foreach (string text in readOnlySpan) {
if (!IsAllAscii(MemoryExtensions.AsSpan(text))) {
allAscii = false;
break;
}
}
results = new AnalysisResults(allAscii, ignoreCase, 0, 0, 0, 0);
}
internal unsafe static bool IsAllAscii(ReadOnlySpan<char> s)
{
fixed (char* ptr = &s.GetPinnableReference()) {
uint* ptr2 = (uint*)ptr;
int num;
for (num = s.Length; num > 3; num -= 4) {
if (!<IsAllAscii>g__AllCharsInUInt32AreAscii|3_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;
}
private static double GetUniquenessFactor(HashSet<string> set, ReadOnlySpan<string> uniqueStrings)
{
set.Clear();
ReadOnlySpan<string> readOnlySpan = uniqueStrings;
foreach (string item in readOnlySpan) {
set.Add(item);
}
return (double)set.Count / (double)uniqueStrings.Length;
}
}
}