167 lines
6.6 KiB
C#
167 lines
6.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Immutable;
|
|
using System.Linq;
|
|
using Microsoft.CodeAnalysis.CSharp.Symbols;
|
|
using Microsoft.CodeAnalysis.CodeGen;
|
|
using Microsoft.CodeAnalysis.PooledObjects;
|
|
using Roslyn.Utilities;
|
|
|
|
namespace Microsoft.CodeAnalysis.CSharp;
|
|
|
|
internal sealed class LengthBasedStringSwitchData
|
|
{
|
|
internal struct LengthJumpTable(LabelSymbol? nullCaseLabel, ImmutableArray<(int value, LabelSymbol label)> lengthCaseLabels)
|
|
{
|
|
public readonly LabelSymbol? NullCaseLabel = nullCaseLabel;
|
|
|
|
public readonly ImmutableArray<(int value, LabelSymbol label)> LengthCaseLabels = lengthCaseLabels;
|
|
}
|
|
|
|
internal struct CharJumpTable
|
|
{
|
|
public readonly LabelSymbol Label;
|
|
|
|
public readonly int SelectedCharPosition;
|
|
|
|
public readonly ImmutableArray<(char value, LabelSymbol label)> CharCaseLabels;
|
|
|
|
internal CharJumpTable(LabelSymbol label, int selectedCharPosition, ImmutableArray<(char value, LabelSymbol label)> charCaseLabels)
|
|
{
|
|
Label = label;
|
|
SelectedCharPosition = selectedCharPosition;
|
|
CharCaseLabels = charCaseLabels;
|
|
}
|
|
}
|
|
|
|
internal struct StringJumpTable
|
|
{
|
|
public readonly LabelSymbol Label;
|
|
|
|
public readonly ImmutableArray<(string value, LabelSymbol label)> StringCaseLabels;
|
|
|
|
internal StringJumpTable(LabelSymbol label, ImmutableArray<(string value, LabelSymbol label)> stringCaseLabels)
|
|
{
|
|
Label = label;
|
|
StringCaseLabels = stringCaseLabels;
|
|
}
|
|
}
|
|
|
|
internal readonly LengthJumpTable LengthBasedJumpTable;
|
|
|
|
internal readonly ImmutableArray<CharJumpTable> CharBasedJumpTables;
|
|
|
|
internal readonly ImmutableArray<StringJumpTable> StringBasedJumpTables;
|
|
|
|
internal LengthBasedStringSwitchData(LengthJumpTable lengthJumpTable, ImmutableArray<CharJumpTable> charJumpTables, ImmutableArray<StringJumpTable> stringJumpTables)
|
|
{
|
|
LengthBasedJumpTable = lengthJumpTable;
|
|
CharBasedJumpTables = charJumpTables;
|
|
StringBasedJumpTables = stringJumpTables;
|
|
}
|
|
|
|
internal bool ShouldGenerateLengthBasedSwitch(int labelsCount)
|
|
{
|
|
if (SwitchStringJumpTableEmitter.ShouldGenerateHashTableSwitch(labelsCount))
|
|
{
|
|
return StringBasedJumpTables.All((StringJumpTable t) => t.StringCaseLabels.Length <= 5);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
internal static LengthBasedStringSwitchData Create(ImmutableArray<(ConstantValue value, LabelSymbol label)> inputCases)
|
|
{
|
|
LabelSymbol nullCaseLabel = null;
|
|
ImmutableArray<(ConstantValue, LabelSymbol)>.Enumerator enumerator = inputCases.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
(ConstantValue, LabelSymbol) current = enumerator.Current;
|
|
if (current.Item1.IsNull)
|
|
{
|
|
nullCaseLabel = current.Item2;
|
|
}
|
|
}
|
|
ArrayBuilder<(int, LabelSymbol)> instance = ArrayBuilder<(int, LabelSymbol)>.GetInstance();
|
|
ArrayBuilder<CharJumpTable> instance2 = ArrayBuilder<CharJumpTable>.GetInstance();
|
|
ArrayBuilder<StringJumpTable> instance3 = ArrayBuilder<StringJumpTable>.GetInstance();
|
|
foreach (IGrouping<int, (ConstantValue, LabelSymbol)> item2 in from c in inputCases
|
|
where !c.value.IsNull
|
|
group c by c.value.StringValue.Length)
|
|
{
|
|
int key = item2.Key;
|
|
LabelSymbol item = CreateAndRegisterCharJumpTables(key, EnumerableExtensions.SelectAsArray<(ConstantValue, LabelSymbol), (string, LabelSymbol)>((IEnumerable<(ConstantValue, LabelSymbol)>)item2, (Func<(ConstantValue, LabelSymbol), (string, LabelSymbol)>)(((ConstantValue value, LabelSymbol label) c) => (c.value.StringValue, label: c.label))), instance2, instance3);
|
|
instance.Add((key, item));
|
|
}
|
|
return new LengthBasedStringSwitchData(new LengthJumpTable(nullCaseLabel, instance.ToImmutableAndFree()), instance2.ToImmutableAndFree(), instance3.ToImmutableAndFree());
|
|
}
|
|
|
|
private static LabelSymbol CreateAndRegisterCharJumpTables(int stringLength, ImmutableArray<(string value, LabelSymbol label)> casesWithGivenLength, ArrayBuilder<CharJumpTable> charJumpTables, ArrayBuilder<StringJumpTable> stringJumpTables)
|
|
{
|
|
if (stringLength == 0)
|
|
{
|
|
return casesWithGivenLength.Single().label;
|
|
}
|
|
if (casesWithGivenLength.Length == 1)
|
|
{
|
|
return CreateAndRegisterStringJumpTable(casesWithGivenLength, stringJumpTables);
|
|
}
|
|
int bestCharacterPosition = selectBestCharacterIndex(stringLength, casesWithGivenLength);
|
|
ArrayBuilder<(char, LabelSymbol)> instance = ArrayBuilder<(char, LabelSymbol)>.GetInstance();
|
|
foreach (IGrouping<char, (string, LabelSymbol)> item4 in from c in casesWithGivenLength
|
|
group c by c.value[bestCharacterPosition])
|
|
{
|
|
LabelSymbol item = ((stringLength == 1) ? item4.Single().Item2 : CreateAndRegisterStringJumpTable(item4.ToImmutableArray(), stringJumpTables));
|
|
char key = item4.Key;
|
|
instance.Add((key, item));
|
|
}
|
|
CharJumpTable charJumpTable = new CharJumpTable(new GeneratedLabelSymbol("char-dispatch"), bestCharacterPosition, instance.ToImmutableAndFree());
|
|
charJumpTables.Add(charJumpTable);
|
|
return charJumpTable.Label;
|
|
static (int singleEntryCount, int largestBucket) positionScore(int position, ImmutableArray<(string value, LabelSymbol label)> caseLabels)
|
|
{
|
|
PooledDictionary<char, int> instance2 = PooledDictionary<char, int>.GetInstance();
|
|
ImmutableArray<(string, LabelSymbol)>.Enumerator enumerator2 = caseLabels.GetEnumerator();
|
|
while (enumerator2.MoveNext())
|
|
{
|
|
char key2 = enumerator2.Current.Item1[position];
|
|
if (((Dictionary<char, int>)(object)instance2).TryGetValue(key2, out int value))
|
|
{
|
|
((Dictionary<char, int>)(object)instance2)[key2] = value + 1;
|
|
}
|
|
else
|
|
{
|
|
((Dictionary<char, int>)(object)instance2)[key2] = 1;
|
|
}
|
|
}
|
|
int item2 = ((Dictionary<char, int>)(object)instance2).Values.Count((int c) => c == 1);
|
|
int item3 = ((Dictionary<char, int>)(object)instance2).Values.Max();
|
|
instance2.Free();
|
|
return (singleEntryCount: item2, largestBucket: item3);
|
|
}
|
|
static int selectBestCharacterIndex(int num3, ImmutableArray<(string value, LabelSymbol label)> caseLabels)
|
|
{
|
|
int result = -1;
|
|
int num = -1;
|
|
int num2 = int.MaxValue;
|
|
for (int i = 0; i < num3; i++)
|
|
{
|
|
var (num4, num5) = positionScore(i, caseLabels);
|
|
if (num4 > num || (num4 == num && num5 < num2))
|
|
{
|
|
num = num4;
|
|
num2 = num5;
|
|
result = i;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
private static LabelSymbol CreateAndRegisterStringJumpTable(ImmutableArray<(string value, LabelSymbol label)> cases, ArrayBuilder<StringJumpTable> stringJumpTables)
|
|
{
|
|
StringJumpTable stringJumpTable = new StringJumpTable(new GeneratedLabelSymbol("string-dispatch"), ImmutableArrayExtensions.SelectAsArray<(string, LabelSymbol), (string, LabelSymbol)>(cases, (Func<(string, LabelSymbol), (string, LabelSymbol)>)(((string value, LabelSymbol label) c) => (value: c.value, label: c.label))));
|
|
stringJumpTables.Add(stringJumpTable);
|
|
return stringJumpTable.Label;
|
|
}
|
|
}
|