Skip to content

Commit

Permalink
Port dotnet/coreclr#2933 to corert
Browse files Browse the repository at this point in the history
Improve performance of Enum.Parse/TryParse
  • Loading branch information
stephentoub committed Feb 6, 2016
1 parent 7e782f4 commit 6ab4ee3
Showing 1 changed file with 43 additions and 16 deletions.
59 changes: 43 additions & 16 deletions src/System.Private.CoreLib/src/System/Enum.cs
Expand Up @@ -888,8 +888,16 @@ private static bool TryParseEnum(Type enumType, String value, bool ignoreCase, o
return false;
}

value = value.Trim();
if (value.Length == 0)
int firstNonWhitespaceIndex = -1;
for (int i = 0; i < value.Length; i++)
{
if (!char.IsWhiteSpace(value[i]))
{
firstNonWhitespaceIndex = i;
break;
}
}
if (firstNonWhitespaceIndex == -1)
{
exception = new ArgumentException(SR.Arg_MustContainEnumInfo);
return false;
Expand All @@ -899,7 +907,7 @@ private static bool TryParseEnum(Type enumType, String value, bool ignoreCase, o
if (!enumEEType.IsEnum)
throw new ArgumentException(SR.Arg_MustBeEnum, "enumType");

if (TryParseAsInteger(enumEEType, value, out result))
if (TryParseAsInteger(enumEEType, value, firstNonWhitespaceIndex, out result))
return true;

// Parse as string. Now (and only now) do we look for metadata information.
Expand All @@ -911,20 +919,33 @@ private static bool TryParseEnum(Type enumType, String value, bool ignoreCase, o
// Port note: The docs are silent on how multiple matches are resolved when doing case-insensitive parses.
// The desktop's ad-hoc behavior is to pick the one with the smallest value after doing a value-preserving cast
// to a ulong, so we'll follow that here.
StringComparison comparison = ignoreCase ?
StringComparison.OrdinalIgnoreCase :
StringComparison.Ordinal;
KeyValuePair<String, ulong>[] actualNamesAndValues = enumInfo.NamesAndValues;
foreach (String untrimmedName in value.Split(','))
int valueIndex = firstNonWhitespaceIndex;
while (valueIndex <= value.Length) // '=' is to handle invalid case of an ending comma
{
String expectedName = untrimmedName.Trim();
// Find the next separator, if there is one, otherwise the end of the string.
int endIndex = value.IndexOf(',', valueIndex);
if (endIndex == -1)
{
endIndex = value.Length;
}

// Shift the starting and ending indices to eliminate whitespace
int endIndexNoWhitespace = endIndex;
while (valueIndex < endIndex && char.IsWhiteSpace(value[valueIndex])) valueIndex++;
while (endIndexNoWhitespace > valueIndex && char.IsWhiteSpace(value[endIndexNoWhitespace - 1])) endIndexNoWhitespace--;
int valueSubstringLength = endIndexNoWhitespace - valueIndex;

// Try to match this substring against each enum name
bool foundMatch = false;
foreach (KeyValuePair<String, ulong> kv in actualNamesAndValues)
{
String actualName = kv.Key;
bool match;
if (ignoreCase)
match = (0 == String.Compare(expectedName, actualName, StringComparison.OrdinalIgnoreCase));
else
match = (expectedName.Equals(actualName));
if (match)
if (actualName.Length == valueSubstringLength &&
String.Compare(actualName[i], 0, value, valueIndex, valueSubstringLength, comparison) == 0)
{
v |= kv.Value;
foundMatch = true;
Expand All @@ -936,6 +957,9 @@ private static bool TryParseEnum(Type enumType, String value, bool ignoreCase, o
exception = new ArgumentException(SR.Format(SR.Arg_EnumValueNotFound, value));
return false;
}

// Move our pointer to the ending index to go again.
valueIndex = endIndex + 1;
}
unsafe
{
Expand All @@ -944,17 +968,20 @@ private static bool TryParseEnum(Type enumType, String value, bool ignoreCase, o
return true;
}

private static bool TryParseAsInteger(EETypePtr enumEEType, String value, out Object result)
private static bool TryParseAsInteger(EETypePtr enumEEType, String value, int valueOffset, out Object result)
{
result = null;
Debug.Assert(value != null, "Expected non-null value");
Debug.Assert(value.Length > 0, "Expected non-empty value");
Debug.Assert(valueOffset >= 0 && valueOffset < value.Length, "Expected valueOffset to be within value");

if (value.Length == 0)
return false;
result = null;

if (!(Char.IsDigit(value[0]) || value[0] == '+' || value[0] == '-'))
char firstNonWhitespaceChar = value[valueOffset];
if (!(Char.IsDigit(firstNonWhitespaceChar) || firstNonWhitespaceChar == '+' || firstNonWhitespaceChar == '-'))
return false;
RuntimeImports.RhCorElementType corElementType = enumEEType.CorElementType;

value = value.Trim();
unsafe
{
switch (corElementType)
Expand Down

0 comments on commit 6ab4ee3

Please sign in to comment.