Files
2026-08-27 10:56:38 -06:00

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;
}
}