private static string InternalFlagsFormat(RuntimeType eT, ulong result) { // These values are sorted by value. Don't change this TypeValuesAndNames entry = GetCachedValuesAndNames(eT, true); return(InternalFlagsFormat(eT, entry, result)); }
private static string InternalFormat(RuntimeType eT, ulong value) { Debug.Assert(eT != null); // These values are sorted by value. Don't change this TypeValuesAndNames entry = GetCachedValuesAndNames(eT, true); if (!entry.IsFlag) // Not marked with Flags attribute { return(Enum.GetEnumName(eT, value)); } else // These are flags OR'ed together (We treat everything as unsigned types) { return(InternalFlagsFormat(eT, entry, value)); } }
static TypeValuesAndNames GetCachedValuesAndNames(RuntimeType enumType, bool getNames) { var entry = enumType.GenericCache as TypeValuesAndNames; if (entry == null || (getNames && entry.Names == null)) { ulong[] values = null; String[] names = null; if (!GetEnumValuesAndNames(enumType, out values, out names)) { Array.Sort(values, names, System.Collections.Generic.Comparer <ulong> .Default); } bool isFlags = enumType.IsDefined(typeof(FlagsAttribute), inherit: false); entry = new TypeValuesAndNames(isFlags, values, names); enumType.GenericCache = entry; } return(entry); }
private static TypeValuesAndNames GetCachedValuesAndNames(RuntimeType enumType, bool getNames) { TypeValuesAndNames entry = enumType.GenericCache as TypeValuesAndNames; if (entry == null || (getNames && entry.Names == null)) { ulong[] values = null; string[] names = null; GetEnumValuesAndNames( enumType.GetTypeHandleInternal(), JitHelpers.GetObjectHandleOnStack(ref values), JitHelpers.GetObjectHandleOnStack(ref names), getNames); bool isFlags = enumType.IsDefined(typeof(FlagsAttribute), inherit: false); entry = new TypeValuesAndNames(isFlags, values, names); enumType.GenericCache = entry; } return(entry); }
private static bool TryParseEnum(Type enumType, string value, bool ignoreCase, ref EnumResult parseResult) { if (enumType == null) { throw new ArgumentNullException(nameof(enumType)); } RuntimeType rtType = enumType as RuntimeType; if (rtType == null) { throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType)); } if (!enumType.IsEnum) { throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType)); } if (value == null) { parseResult.SetFailure(ParseFailureKind.ArgumentNull, nameof(value)); return(false); } int firstNonWhitespaceIndex = -1; for (int i = 0; i < value.Length; i++) { if (!char.IsWhiteSpace(value[i])) { firstNonWhitespaceIndex = i; break; } } if (firstNonWhitespaceIndex == -1) { parseResult.SetFailure(ParseFailureKind.Argument, nameof(SR.Arg_MustContainEnumInfo), null); return(false); } // We have 2 code paths here. One if they are values else if they are Strings. // values will have the first character as as number or a sign. ulong result = 0; char firstNonWhitespaceChar = value[firstNonWhitespaceIndex]; if (char.IsDigit(firstNonWhitespaceChar) || firstNonWhitespaceChar == '-' || firstNonWhitespaceChar == '+') { Type underlyingType = GetUnderlyingType(enumType); object temp; try { value = value.Trim(); temp = Convert.ChangeType(value, underlyingType, CultureInfo.InvariantCulture); parseResult.parsedEnum = ToObject(enumType, temp); return(true); } catch (FormatException) { // We need to Parse this as a String instead. There are cases // when you tlbimp enums that can have values of the form "3D". // Don't fix this code. } catch (Exception ex) { if (parseResult.canThrow) { throw; } else { parseResult.SetFailure(ex); return(false); } } } // Find the field. Let's assume that these are always static classes // because the class is an enum. TypeValuesAndNames entry = GetCachedValuesAndNames(rtType, true); string[] enumNames = entry.Names; ulong[] enumValues = entry.Values; StringComparison comparison = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; int valueIndex = firstNonWhitespaceIndex; while (valueIndex <= value.Length) // '=' is to handle invalid case of an ending comma { // Find the next separator, if there is one, otherwise the end of the string. int endIndex = value.IndexOf(enumSeparatorChar, 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 success = false; for (int i = 0; i < enumNames.Length; i++) { if (enumNames[i].Length == valueSubstringLength && string.Compare(enumNames[i], 0, value, valueIndex, valueSubstringLength, comparison) == 0) { result |= enumValues[i]; success = true; break; } } // If we couldn't find a match, throw an argument exception. if (!success) { // Not found, throw an argument exception. parseResult.SetFailure(ParseFailureKind.ArgumentWithParameter, nameof(SR.Arg_EnumValueNotFound), value); return(false); } // Move our pointer to the ending index to go again. valueIndex = endIndex + 1; } try { parseResult.parsedEnum = ToObject(enumType, result); return(true); } catch (Exception ex) { if (parseResult.canThrow) { throw; } else { parseResult.SetFailure(ex); return(false); } } }
private static string InternalFlagsFormat(RuntimeType eT, TypeValuesAndNames entry, ulong result) { Debug.Assert(eT != null); string[] names = entry.Names; ulong[] values = entry.Values; Debug.Assert(names.Length == values.Length); int index = values.Length - 1; StringBuilder sb = StringBuilderCache.Acquire(); bool firstTime = true; ulong saveResult = result; // We will not optimize this code further to keep it maintainable. There are some boundary checks that can be applied // to minimize the comparsions required. This code works the same for the best/worst case. In general the number of // items in an enum are sufficiently small and not worth the optimization. while (index >= 0) { if ((index == 0) && (values[index] == 0)) { break; } if ((result & values[index]) == values[index]) { result -= values[index]; if (!firstTime) { sb.Insert(0, enumSeparatorString); } sb.Insert(0, names[index]); firstTime = false; } index--; } string returnString; if (result != 0) { // We were unable to represent this number as a bitwise or of valid flags returnString = null; // return null so the caller knows to .ToString() the input } else if (saveResult == 0) { // For the cases when we have zero if (values.Length > 0 && values[0] == 0) { returnString = names[0]; // Zero was one of the enum values. } else { returnString = "0"; } } else { returnString = sb.ToString(); // Return the string representation } StringBuilderCache.Release(sb); return(returnString); }
private static string InternalFlagsFormat(RuntimeType eT, TypeValuesAndNames entry, ulong resultValue) { Debug.Assert(eT != null); string[] names = entry.Names; ulong[] values = entry.Values; Debug.Assert(names.Length == values.Length); // Values are sorted, so if the incoming value is 0, we can check to see whether // the first entry matches it, in which case we can return its name; otherwise, // we can just return "0". if (resultValue == 0) { return(values.Length > 0 && values[0] == 0 ? names[0] : "0"); } // With a ulong result value, regardless of the enum's base type, the maximum // possible number of consistent name/values we could have is 64, since every // value is made up of one or more bits, and when we see values and incorporate // their names, we effectively switch off those bits. Span <int> foundItems = stackalloc int[64]; // Walk from largest to smallest. It's common to have a flags enum with a single // value that matches a single entry, in which case we can just return the existing // name string. int index = values.Length - 1; while (index >= 0) { if (values[index] == resultValue) { return(names[index]); } if (values[index] < resultValue) { break; } index--; } // Now look for multiple matches, storing the indices of the values // into our span. int resultLength = 0, foundItemsCount = 0; while (index >= 0) { ulong currentValue = values[index]; if (index == 0 && currentValue == 0) { break; } if ((resultValue & currentValue) == currentValue) { resultValue -= currentValue; foundItems[foundItemsCount++] = index; resultLength = checked (resultLength + names[index].Length); } index--; } // If we exhausted looking through all the values and we still have // a non-zero result, we couldn't match the result to only named values. // In that case, we return null and let the call site just generate // a string for the integral value. if (resultValue != 0) { return(null); } // We know what strings to concatenate. Do so. Debug.Assert(foundItemsCount > 0); const int SeparatorStringLength = 2; // ", " string result = string.FastAllocateString(checked (resultLength + (SeparatorStringLength * (foundItemsCount - 1)))); Span <char> resultSpan = MemoryMarshal.CreateSpan(ref result.GetRawStringData(), result.Length); string name = names[foundItems[--foundItemsCount]]; name.AsSpan().CopyTo(resultSpan); resultSpan = resultSpan.Slice(name.Length); while (--foundItemsCount >= 0) { resultSpan[0] = ','; resultSpan[1] = ' '; resultSpan = resultSpan.Slice(2); name = names[foundItems[foundItemsCount]]; name.AsSpan().CopyTo(resultSpan); resultSpan = resultSpan.Slice(name.Length); } Debug.Assert(resultSpan.IsEmpty); return(result); }