17793bf5a99681d5e19bf6a3833f34e896b16353
[platform/upstream/coreclr.git] / src / System.Private.CoreLib / src / System / Enum.cs
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5 using System.Reflection;
6 using System.Text;
7 using System.Collections;
8 using System.Globalization;
9 using System.Runtime.CompilerServices;
10 using System.Runtime.InteropServices;
11 using System.Runtime.Versioning;
12 using System.Diagnostics;
13
14 // The code below includes partial support for float/double and
15 // pointer sized enums.
16 //
17 // The type loader does not prohibit such enums, and older versions of
18 // the ECMA spec include them as possible enum types.
19 //
20 // However there are many things broken throughout the stack for
21 // float/double/intptr/uintptr enums. There was a conscious decision
22 // made to not fix the whole stack to work well for them because of
23 // the right behavior is often unclear, and it is hard to test and
24 // very low value because of such enums cannot be expressed in C#.
25
26 namespace System
27 {
28     [Serializable]
29     [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
30     public abstract class Enum : ValueType, IComparable, IFormattable, IConvertible
31     {
32         #region Private Constants
33         private const char enumSeparatorChar = ',';
34         #endregion
35
36         #region Private Static Methods
37         private static TypeValuesAndNames GetCachedValuesAndNames(RuntimeType enumType, bool getNames)
38         {
39             TypeValuesAndNames entry = enumType.GenericCache as TypeValuesAndNames;
40
41             if (entry == null || (getNames && entry.Names == null))
42             {
43                 ulong[] values = null;
44                 string[] names = null;
45                 bool isFlags = enumType.IsDefined(typeof(System.FlagsAttribute), false);
46
47                 GetEnumValuesAndNames(
48                     enumType.GetTypeHandleInternal(),
49                     JitHelpers.GetObjectHandleOnStack(ref values),
50                     JitHelpers.GetObjectHandleOnStack(ref names),
51                     getNames);
52
53                 entry = new TypeValuesAndNames(isFlags, values, names);
54                 enumType.GenericCache = entry;
55             }
56
57             return entry;
58         }
59
60         private unsafe string InternalFormattedHexString()
61         {
62             fixed (void* pValue = &JitHelpers.GetPinningHelper(this).m_data)
63             {
64                 switch (InternalGetCorElementType())
65                 {
66                     case CorElementType.I1:
67                     case CorElementType.U1:
68                         return (*(byte*)pValue).ToString("X2", null);
69                     case CorElementType.Boolean:
70                         // direct cast from bool to byte is not allowed
71                         return Convert.ToByte(*(bool*)pValue).ToString("X2", null);
72                     case CorElementType.I2:
73                     case CorElementType.U2:
74                     case CorElementType.Char:
75                         return (*(ushort*)pValue).ToString("X4", null);
76                     case CorElementType.I4:
77                     case CorElementType.U4:
78                         return (*(uint*)pValue).ToString("X8", null);
79                     case CorElementType.I8:
80                     case CorElementType.U8:
81                         return (*(ulong*)pValue).ToString("X16", null);
82                     default:
83                         throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType);
84                 }
85             }
86         }
87
88         private static string InternalFormattedHexString(object value)
89         {
90             TypeCode typeCode = Convert.GetTypeCode(value);
91
92             switch (typeCode)
93             {
94                 case TypeCode.SByte:
95                     return ((byte)(sbyte)value).ToString("X2", null);
96                 case TypeCode.Byte:
97                     return ((byte)value).ToString("X2", null);
98                 case TypeCode.Boolean:
99                     // direct cast from bool to byte is not allowed
100                     return Convert.ToByte((bool)value).ToString("X2", null);
101                 case TypeCode.Int16:
102                     return ((ushort)(short)value).ToString("X4", null);
103                 case TypeCode.UInt16:
104                     return ((ushort)value).ToString("X4", null);
105                 case TypeCode.Char:
106                     return ((ushort)(char)value).ToString("X4", null);
107                 case TypeCode.UInt32:
108                     return ((uint)value).ToString("X8", null);
109                 case TypeCode.Int32:
110                     return ((uint)(int)value).ToString("X8", null);
111                 case TypeCode.UInt64:
112                     return ((ulong)value).ToString("X16", null);
113                 case TypeCode.Int64:
114                     return ((ulong)(long)value).ToString("X16", null);
115                 // All unsigned types will be directly cast
116                 default:
117                     throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType);
118             }
119         }
120
121         internal static string GetEnumName(RuntimeType eT, ulong ulValue)
122         {
123             Debug.Assert(eT != null);
124             ulong[] ulValues = Enum.InternalGetValues(eT);
125             int index = Array.BinarySearch(ulValues, ulValue);
126
127             if (index >= 0)
128             {
129                 string[] names = Enum.InternalGetNames(eT);
130                 return names[index];
131             }
132
133             return null; // return null so the caller knows to .ToString() the input
134         }
135
136         private static string InternalFormat(RuntimeType eT, ulong value)
137         {
138             Debug.Assert(eT != null);
139
140             // These values are sorted by value. Don't change this
141             TypeValuesAndNames entry = GetCachedValuesAndNames(eT, true);
142
143             if (!entry.IsFlag) // Not marked with Flags attribute
144             {
145                 return Enum.GetEnumName(eT, value);
146             }
147             else // These are flags OR'ed together (We treat everything as unsigned types)
148             {
149                 return InternalFlagsFormat(eT, entry, value);
150             }
151         }
152
153         private static string InternalFlagsFormat(RuntimeType eT, ulong result)
154         {
155             // These values are sorted by value. Don't change this
156             TypeValuesAndNames entry = GetCachedValuesAndNames(eT, true);
157
158             return InternalFlagsFormat(eT, entry, result);
159         }
160
161         private static string InternalFlagsFormat(RuntimeType eT, TypeValuesAndNames entry, ulong resultValue)
162         {
163             Debug.Assert(eT != null);
164
165             string[] names = entry.Names;
166             ulong[] values = entry.Values;
167             Debug.Assert(names.Length == values.Length);
168
169             // Values are sorted, so if the incoming value is 0, we can check to see whether
170             // the first entry matches it, in which case we can return its name; otherwise,
171             // we can just return "0".
172             if (resultValue == 0)
173             {
174                 return values.Length > 0 && values[0] == 0 ?
175                     names[0] :
176                     "0";
177             }
178
179             // With a ulong result value, regardless of the enum's base type, the maximum
180             // possible number of consistent name/values we could have is 64, since every
181             // value is made up of one or more bits, and when we see values and incorporate
182             // their names, we effectively switch off those bits.
183             Span<int> foundItems = stackalloc int[64];
184
185             // Walk from largest to smallest. It's common to have a flags enum with a single
186             // value that matches a single entry, in which case we can just return the existing
187             // name string.
188             int index = values.Length - 1;
189             while (index >= 0)
190             {
191                 if (values[index] == resultValue)
192                 {
193                     return names[index];
194                 }
195
196                 if (values[index] < resultValue)
197                 {
198                     break;
199                 }
200
201                 index--;
202             }
203
204             // Now look for multiple matches, storing the indices of the values
205             // into our span.
206             int resultLength = 0, foundItemsCount = 0;
207             while (index >= 0)
208             {
209                 ulong currentValue = values[index];
210                 if (index == 0 && currentValue == 0)
211                 {
212                     break;
213                 }
214
215                 if ((resultValue & currentValue) == currentValue)
216                 {
217                     resultValue -= currentValue;
218                     foundItems[foundItemsCount++] = index;
219                     resultLength = checked(resultLength + names[index].Length);
220                 }
221
222                 index--;
223             }
224
225             // If we exhausted looking through all the values and we still have
226             // a non-zero result, we couldn't match the result to only named values.
227             // In that case, we return null and let the call site just generate
228             // a string for the integral value.
229             if (resultValue != 0)
230             {
231                 return null;
232             }
233
234             // We know what strings to concatenate.  Do so.
235
236             Debug.Assert(foundItemsCount > 0);
237             const int SeparatorStringLength = 2; // ", "
238             string result = string.FastAllocateString(checked(resultLength + (SeparatorStringLength * (foundItemsCount - 1))));
239
240             Span<char> resultSpan = MemoryMarshal.CreateSpan(ref result.GetRawStringData(), result.Length);
241             string name = names[foundItems[--foundItemsCount]];
242             name.AsSpan().CopyTo(resultSpan);
243             resultSpan = resultSpan.Slice(name.Length);
244             while (--foundItemsCount >= 0)
245             {
246                 resultSpan[0] = ',';
247                 resultSpan[1] = ' ';
248                 resultSpan = resultSpan.Slice(2);
249
250                 name = names[foundItems[foundItemsCount]];
251                 name.AsSpan().CopyTo(resultSpan);
252                 resultSpan = resultSpan.Slice(name.Length);
253             }
254             Debug.Assert(resultSpan.IsEmpty);
255
256             return result;
257         }
258
259         internal static ulong ToUInt64(object value)
260         {
261             // Helper function to silently convert the value to UInt64 from the other base types for enum without throwing an exception.
262             // This is need since the Convert functions do overflow checks.
263             TypeCode typeCode = Convert.GetTypeCode(value);
264
265             ulong result;
266             switch (typeCode)
267             {
268                 case TypeCode.SByte:
269                     result = (ulong)(sbyte)value;
270                     break;
271                 case TypeCode.Byte:
272                     result = (byte)value;
273                     break;
274                 case TypeCode.Boolean:
275                     // direct cast from bool to byte is not allowed
276                     result = Convert.ToByte((bool)value);
277                     break;
278                 case TypeCode.Int16:
279                     result = (ulong)(short)value;
280                     break;
281                 case TypeCode.UInt16:
282                     result = (ushort)value;
283                     break;
284                 case TypeCode.Char:
285                     result = (ushort)(char)value;
286                     break;
287                 case TypeCode.UInt32:
288                     result = (uint)value;
289                     break;
290                 case TypeCode.Int32:
291                     result = (ulong)(int)value;
292                     break;
293                 case TypeCode.UInt64:
294                     result = (ulong)value;
295                     break;
296                 case TypeCode.Int64:
297                     result = (ulong)(long)value;
298                     break;
299                 // All unsigned types will be directly cast
300                 default:
301                     throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType);
302             }
303
304             return result;
305         }
306
307         [MethodImplAttribute(MethodImplOptions.InternalCall)]
308         private static extern int InternalCompareTo(object o1, object o2);
309
310         [MethodImplAttribute(MethodImplOptions.InternalCall)]
311         internal static extern RuntimeType InternalGetUnderlyingType(RuntimeType enumType);
312
313         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
314         private static extern void GetEnumValuesAndNames(RuntimeTypeHandle enumType, ObjectHandleOnStack values, ObjectHandleOnStack names, bool getNames);
315
316         [MethodImplAttribute(MethodImplOptions.InternalCall)]
317         private static extern object InternalBoxEnum(RuntimeType enumType, long value);
318         #endregion
319
320         #region Public Static Methods
321         private enum ParseFailureKind
322         {
323             None = 0,
324             Argument = 1,
325             ArgumentNull = 2,
326             ArgumentWithParameter = 3,
327             UnhandledException = 4
328         }
329
330         // This will store the result of the parsing.
331         private struct EnumResult
332         {
333             internal object parsedEnum;
334             internal bool canThrow;
335             internal ParseFailureKind m_failure;
336             internal string m_failureMessageID;
337             internal string m_failureParameter;
338             internal object m_failureMessageFormatArgument;
339             internal Exception m_innerException;
340
341             internal void SetFailure(Exception unhandledException)
342             {
343                 m_failure = ParseFailureKind.UnhandledException;
344                 m_innerException = unhandledException;
345             }
346             internal void SetFailure(ParseFailureKind failure, string failureParameter)
347             {
348                 m_failure = failure;
349                 m_failureParameter = failureParameter;
350                 if (canThrow)
351                     throw GetEnumParseException();
352             }
353             internal void SetFailure(ParseFailureKind failure, string failureMessageID, object failureMessageFormatArgument)
354             {
355                 m_failure = failure;
356                 m_failureMessageID = failureMessageID;
357                 m_failureMessageFormatArgument = failureMessageFormatArgument;
358                 if (canThrow)
359                     throw GetEnumParseException();
360             }
361             internal Exception GetEnumParseException()
362             {
363                 switch (m_failure)
364                 {
365                     case ParseFailureKind.Argument:
366                         return new ArgumentException(SR.GetResourceString(m_failureMessageID));
367
368                     case ParseFailureKind.ArgumentNull:
369                         return new ArgumentNullException(m_failureParameter);
370
371                     case ParseFailureKind.ArgumentWithParameter:
372                         return new ArgumentException(SR.Format(SR.GetResourceString(m_failureMessageID), m_failureMessageFormatArgument));
373
374                     case ParseFailureKind.UnhandledException:
375                         return m_innerException;
376
377                     default:
378                         Debug.Fail("Unknown EnumParseFailure: " + m_failure);
379                         return new ArgumentException(SR.Arg_EnumValueNotFound);
380                 }
381             }
382         }
383
384         public static bool TryParse(Type enumType, string value, out object result)
385         {
386             return TryParse(enumType, value, false, out result);
387         }
388
389         public static bool TryParse(Type enumType, string value, bool ignoreCase, out object result)
390         {
391             result = null;
392             EnumResult parseResult = new EnumResult();
393             bool retValue;
394
395             if (retValue = TryParseEnum(enumType, value, ignoreCase, ref parseResult))
396                 result = parseResult.parsedEnum;
397             return retValue;
398         }
399
400         public static bool TryParse<TEnum>(string value, out TEnum result) where TEnum : struct
401         {
402             return TryParse(value, false, out result);
403         }
404
405         public static bool TryParse<TEnum>(string value, bool ignoreCase, out TEnum result) where TEnum : struct
406         {
407             result = default;
408             EnumResult parseResult = new EnumResult();
409             bool retValue;
410
411             if (retValue = TryParseEnum(typeof(TEnum), value, ignoreCase, ref parseResult))
412                 result = (TEnum)parseResult.parsedEnum;
413             return retValue;
414         }
415
416         public static object Parse(Type enumType, string value)
417         {
418             return Parse(enumType, value, false);
419         }
420
421         public static object Parse(Type enumType, string value, bool ignoreCase)
422         {
423             EnumResult parseResult = new EnumResult() { canThrow = true };
424             if (TryParseEnum(enumType, value, ignoreCase, ref parseResult))
425                 return parseResult.parsedEnum;
426             else
427                 throw parseResult.GetEnumParseException();
428         }
429
430         public static TEnum Parse<TEnum>(string value) where TEnum : struct
431         {
432             return Parse<TEnum>(value, false);
433         }
434
435         public static TEnum Parse<TEnum>(string value, bool ignoreCase) where TEnum : struct
436         {
437             EnumResult parseResult = new EnumResult() { canThrow = true };
438             if (TryParseEnum(typeof(TEnum), value, ignoreCase, ref parseResult))
439                 return (TEnum)parseResult.parsedEnum;
440             else
441                 throw parseResult.GetEnumParseException();
442         }
443
444         private static bool TryParseEnum(Type enumType, string value, bool ignoreCase, ref EnumResult parseResult)
445         {
446             if (enumType == null)
447                 throw new ArgumentNullException(nameof(enumType));
448
449             RuntimeType rtType = enumType as RuntimeType;
450             if (rtType == null)
451                 throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType));
452
453             if (!enumType.IsEnum)
454                 throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType));
455
456             if (value == null)
457             {
458                 parseResult.SetFailure(ParseFailureKind.ArgumentNull, nameof(value));
459                 return false;
460             }
461
462             int firstNonWhitespaceIndex = -1;
463             for (int i = 0; i < value.Length; i++)
464             {
465                 if (!char.IsWhiteSpace(value[i]))
466                 {
467                     firstNonWhitespaceIndex = i;
468                     break;
469                 }
470             }
471             if (firstNonWhitespaceIndex == -1)
472             {
473                 parseResult.SetFailure(ParseFailureKind.Argument, nameof(SR.Arg_MustContainEnumInfo), null);
474                 return false;
475             }
476
477             // We have 2 code paths here. One if they are values else if they are Strings.
478             // values will have the first character as as number or a sign.
479             ulong result = 0;
480
481             char firstNonWhitespaceChar = value[firstNonWhitespaceIndex];
482             if (char.IsDigit(firstNonWhitespaceChar) || firstNonWhitespaceChar == '-' || firstNonWhitespaceChar == '+')
483             {
484                 Type underlyingType = GetUnderlyingType(enumType);
485                 object temp;
486
487                 try
488                 {
489                     value = value.Trim();
490                     temp = Convert.ChangeType(value, underlyingType, CultureInfo.InvariantCulture);
491                     parseResult.parsedEnum = ToObject(enumType, temp);
492                     return true;
493                 }
494                 catch (FormatException)
495                 { // We need to Parse this as a String instead. There are cases
496                   // when you tlbimp enums that can have values of the form "3D".
497                   // Don't fix this code.
498                 }
499                 catch (Exception ex)
500                 {
501                     if (parseResult.canThrow)
502                         throw;
503                     else
504                     {
505                         parseResult.SetFailure(ex);
506                         return false;
507                     }
508                 }
509             }
510
511             // Find the field. Let's assume that these are always static classes 
512             // because the class is an enum.
513             TypeValuesAndNames entry = GetCachedValuesAndNames(rtType, true);
514             string[] enumNames = entry.Names;
515             ulong[] enumValues = entry.Values;
516
517             StringComparison comparison = ignoreCase ?
518                 StringComparison.OrdinalIgnoreCase :
519                 StringComparison.Ordinal;
520
521             int valueIndex = firstNonWhitespaceIndex;
522             while (valueIndex <= value.Length) // '=' is to handle invalid case of an ending comma
523             {
524                 // Find the next separator, if there is one, otherwise the end of the string.
525                 int endIndex = value.IndexOf(enumSeparatorChar, valueIndex);
526                 if (endIndex == -1)
527                 {
528                     endIndex = value.Length;
529                 }
530
531                 // Shift the starting and ending indices to eliminate whitespace
532                 int endIndexNoWhitespace = endIndex;
533                 while (valueIndex < endIndex && char.IsWhiteSpace(value[valueIndex])) valueIndex++;
534                 while (endIndexNoWhitespace > valueIndex && char.IsWhiteSpace(value[endIndexNoWhitespace - 1])) endIndexNoWhitespace--;
535                 int valueSubstringLength = endIndexNoWhitespace - valueIndex;
536
537                 // Try to match this substring against each enum name
538                 bool success = false;
539                 for (int i = 0; i < enumNames.Length; i++)
540                 {
541                     if (enumNames[i].Length == valueSubstringLength &&
542                         string.Compare(enumNames[i], 0, value, valueIndex, valueSubstringLength, comparison) == 0)
543                     {
544                         result |= enumValues[i];
545                         success = true;
546                         break;
547                     }
548                 }
549
550                 // If we couldn't find a match, throw an argument exception.
551                 if (!success)
552                 {
553                     // Not found, throw an argument exception.
554                     parseResult.SetFailure(ParseFailureKind.ArgumentWithParameter, nameof(SR.Arg_EnumValueNotFound), value);
555                     return false;
556                 }
557
558                 // Move our pointer to the ending index to go again.
559                 valueIndex = endIndex + 1;
560             }
561
562             try
563             {
564                 parseResult.parsedEnum = ToObject(enumType, result);
565                 return true;
566             }
567             catch (Exception ex)
568             {
569                 if (parseResult.canThrow)
570                     throw;
571                 else
572                 {
573                     parseResult.SetFailure(ex);
574                     return false;
575                 }
576             }
577         }
578
579         public static Type GetUnderlyingType(Type enumType)
580         {
581             if (enumType == null)
582                 throw new ArgumentNullException(nameof(enumType));
583
584             return enumType.GetEnumUnderlyingType();
585         }
586
587         public static Array GetValues(Type enumType)
588         {
589             if (enumType == null)
590                 throw new ArgumentNullException(nameof(enumType));
591
592             return enumType.GetEnumValues();
593         }
594
595         internal static ulong[] InternalGetValues(RuntimeType enumType)
596         {
597             // Get all of the values
598             return GetCachedValuesAndNames(enumType, false).Values;
599         }
600
601         public static string GetName(Type enumType, object value)
602         {
603             if (enumType == null)
604                 throw new ArgumentNullException(nameof(enumType));
605
606             return enumType.GetEnumName(value);
607         }
608
609         public static string[] GetNames(Type enumType)
610         {
611             if (enumType == null)
612                 throw new ArgumentNullException(nameof(enumType));
613
614             return enumType.GetEnumNames();
615         }
616
617         internal static string[] InternalGetNames(RuntimeType enumType)
618         {
619             // Get all of the names
620             return GetCachedValuesAndNames(enumType, true).Names;
621         }
622
623         public static object ToObject(Type enumType, object value)
624         {
625             if (value == null)
626                 throw new ArgumentNullException(nameof(value));
627
628             // Delegate rest of error checking to the other functions
629             TypeCode typeCode = Convert.GetTypeCode(value);
630
631             switch (typeCode)
632             {
633                 case TypeCode.Int32:
634                     return ToObject(enumType, (int)value);
635
636                 case TypeCode.SByte:
637                     return ToObject(enumType, (sbyte)value);
638
639                 case TypeCode.Int16:
640                     return ToObject(enumType, (short)value);
641
642                 case TypeCode.Int64:
643                     return ToObject(enumType, (long)value);
644
645                 case TypeCode.UInt32:
646                     return ToObject(enumType, (uint)value);
647
648                 case TypeCode.Byte:
649                     return ToObject(enumType, (byte)value);
650
651                 case TypeCode.UInt16:
652                     return ToObject(enumType, (ushort)value);
653
654                 case TypeCode.UInt64:
655                     return ToObject(enumType, (ulong)value);
656
657                 case TypeCode.Char:
658                     return ToObject(enumType, (char)value);
659
660                 case TypeCode.Boolean:
661                     return ToObject(enumType, (bool)value);
662
663                 default:
664                     // All unsigned types will be directly cast
665                     throw new ArgumentException(SR.Arg_MustBeEnumBaseTypeOrEnum, nameof(value));
666             }
667         }
668
669         public static bool IsDefined(Type enumType, object value)
670         {
671             if (enumType == null)
672                 throw new ArgumentNullException(nameof(enumType));
673
674             return enumType.IsEnumDefined(value);
675         }
676
677         public static string Format(Type enumType, object value, string format)
678         {
679             if (enumType == null)
680                 throw new ArgumentNullException(nameof(enumType));
681
682             if (!enumType.IsEnum)
683                 throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType));
684
685             if (value == null)
686                 throw new ArgumentNullException(nameof(value));
687
688             if (format == null)
689                 throw new ArgumentNullException(nameof(format));
690
691             RuntimeType rtType = enumType as RuntimeType;
692             if (rtType == null)
693                 throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType));
694
695             // Check if both of them are of the same type
696             Type valueType = value.GetType();
697
698             Type underlyingType = GetUnderlyingType(enumType);
699
700             // If the value is an Enum then we need to extract the underlying value from it
701             if (valueType.IsEnum)
702             {
703                 if (!valueType.IsEquivalentTo(enumType))
704                     throw new ArgumentException(SR.Format(SR.Arg_EnumAndObjectMustBeSameType, valueType.ToString(), enumType.ToString()));
705
706                 if (format.Length != 1)
707                 {
708                     // all acceptable format string are of length 1
709                     throw new FormatException(SR.Format_InvalidEnumFormatSpecification);
710                 }
711                 return ((Enum)value).ToString(format);
712             }
713             // The value must be of the same type as the Underlying type of the Enum
714             else if (valueType != underlyingType)
715             {
716                 throw new ArgumentException(SR.Format(SR.Arg_EnumFormatUnderlyingTypeAndObjectMustBeSameType, valueType.ToString(), underlyingType.ToString()));
717             }
718             if (format.Length != 1)
719             {
720                 // all acceptable format string are of length 1
721                 throw new FormatException(SR.Format_InvalidEnumFormatSpecification);
722             }
723
724             char formatCh = format[0];
725             if (formatCh == 'G' || formatCh == 'g')
726                 return GetEnumName(rtType, ToUInt64(value)) ?? value.ToString();
727
728             if (formatCh == 'D' || formatCh == 'd')
729                 return value.ToString();
730
731             if (formatCh == 'X' || formatCh == 'x')
732                 return InternalFormattedHexString(value);
733
734             if (formatCh == 'F' || formatCh == 'f')
735                 return Enum.InternalFlagsFormat(rtType, ToUInt64(value)) ?? value.ToString();
736
737             throw new FormatException(SR.Format_InvalidEnumFormatSpecification);
738         }
739
740         #endregion
741
742         #region Definitions
743         private class TypeValuesAndNames
744         {
745             // Each entry contains a list of sorted pair of enum field names and values, sorted by values
746             public TypeValuesAndNames(bool isFlag, ulong[] values, string[] names)
747             {
748                 this.IsFlag = isFlag;
749                 this.Values = values;
750                 this.Names = names;
751             }
752
753             public bool IsFlag;
754             public ulong[] Values;
755             public string[] Names;
756         }
757         #endregion
758
759         #region Private Methods
760         internal unsafe object GetValue()
761         {
762             fixed (void* pValue = &JitHelpers.GetPinningHelper(this).m_data)
763             {
764                 switch (InternalGetCorElementType())
765                 {
766                     case CorElementType.I1:
767                         return *(sbyte*)pValue;
768                     case CorElementType.U1:
769                         return *(byte*)pValue;
770                     case CorElementType.Boolean:
771                         return *(bool*)pValue;
772                     case CorElementType.I2:
773                         return *(short*)pValue;
774                     case CorElementType.U2:
775                         return *(ushort*)pValue;
776                     case CorElementType.Char:
777                         return *(char*)pValue;
778                     case CorElementType.I4:
779                         return *(int*)pValue;
780                     case CorElementType.U4:
781                         return *(uint*)pValue;
782                     case CorElementType.R4:
783                         return *(float*)pValue;
784                     case CorElementType.I8:
785                         return *(long*)pValue;
786                     case CorElementType.U8:
787                         return *(ulong*)pValue;
788                     case CorElementType.R8:
789                         return *(double*)pValue;
790                     case CorElementType.I:
791                         return *(IntPtr*)pValue;
792                     case CorElementType.U:
793                         return *(UIntPtr*)pValue;
794                     default:
795                         Debug.Fail("Invalid primitive type");
796                         return null;
797                 }
798             }
799         }
800
801         private unsafe ulong ToUInt64()
802         {
803             fixed (void* pValue = &JitHelpers.GetPinningHelper(this).m_data)
804             {
805                 switch (InternalGetCorElementType())
806                 {
807                     case CorElementType.I1:
808                         return (ulong)*(sbyte*)pValue;
809                     case CorElementType.U1:
810                         return *(byte*)pValue;
811                     case CorElementType.Boolean:
812                         return Convert.ToUInt64(*(bool*)pValue, CultureInfo.InvariantCulture);
813                     case CorElementType.I2:
814                         return (ulong)*(short*)pValue;
815                     case CorElementType.U2:
816                     case CorElementType.Char:
817                         return *(ushort*)pValue;
818                     case CorElementType.I4:
819                         return (ulong)*(int*)pValue;
820                     case CorElementType.U4:
821                     case CorElementType.R4:
822                         return *(uint*)pValue;
823                     case CorElementType.I8:
824                         return (ulong)*(long*)pValue;
825                     case CorElementType.U8:
826                     case CorElementType.R8:
827                         return *(ulong*)pValue;
828                     case CorElementType.I:
829                         if (IntPtr.Size == 8)
830                         {
831                             return *(ulong*)pValue;
832                         }
833                         else
834                         {
835                             return (ulong)*(int*)pValue;
836                         }
837                     case CorElementType.U:
838                         if (IntPtr.Size == 8)
839                         {
840                             return *(ulong*)pValue;
841                         }
842                         else
843                         {
844                             return *(uint*)pValue;
845                         }
846                     default:
847                         Debug.Fail("Invalid primitive type");
848                         return 0;
849                 }
850             }
851         }
852
853         [MethodImplAttribute(MethodImplOptions.InternalCall)]
854         private extern bool InternalHasFlag(Enum flags);
855
856         [MethodImplAttribute(MethodImplOptions.InternalCall)]
857         private extern CorElementType InternalGetCorElementType();
858
859         #endregion
860
861         #region Object Overrides
862         [MethodImplAttribute(MethodImplOptions.InternalCall)]
863         public extern override bool Equals(object obj);
864
865         public override unsafe int GetHashCode()
866         {
867             // CONTRACT with the runtime: GetHashCode of enum types is implemented as GetHashCode of the underlying type.
868             // The runtime can bypass calls to Enum::GetHashCode and call the underlying type's GetHashCode directly
869             // to avoid boxing the enum.
870
871             fixed (void* pValue = &JitHelpers.GetPinningHelper(this).m_data)
872             {
873                 switch (InternalGetCorElementType())
874                 {
875                     case CorElementType.I1:
876                         return (*(sbyte*)pValue).GetHashCode();
877                     case CorElementType.U1:
878                         return (*(byte*)pValue).GetHashCode();
879                     case CorElementType.Boolean:
880                         return (*(bool*)pValue).GetHashCode();
881                     case CorElementType.I2:
882                         return (*(short*)pValue).GetHashCode();
883                     case CorElementType.U2:
884                         return (*(ushort*)pValue).GetHashCode();
885                     case CorElementType.Char:
886                         return (*(char*)pValue).GetHashCode();
887                     case CorElementType.I4:
888                         return (*(int*)pValue).GetHashCode();
889                     case CorElementType.U4:
890                         return (*(uint*)pValue).GetHashCode();
891                     case CorElementType.R4:
892                         return (*(float*)pValue).GetHashCode();
893                     case CorElementType.I8:
894                         return (*(long*)pValue).GetHashCode();
895                     case CorElementType.U8:
896                         return (*(ulong*)pValue).GetHashCode();
897                     case CorElementType.R8:
898                         return (*(double*)pValue).GetHashCode();
899                     case CorElementType.I:
900                         return (*(IntPtr*)pValue).GetHashCode();
901                     case CorElementType.U:
902                         return (*(UIntPtr*)pValue).GetHashCode();
903                     default:
904                         Debug.Fail("Invalid primitive type");
905                         return 0;
906                 }
907             }
908         }
909
910         public override string ToString()
911         {
912             // Returns the value in a human readable format.  For PASCAL style enums who's value maps directly the name of the field is returned.
913             // For PASCAL style enums who's values do not map directly the decimal value of the field is returned.
914             // For BitFlags (indicated by the Flags custom attribute): If for each bit that is set in the value there is a corresponding constant
915             //(a pure power of 2), then the  OR string (ie "Red | Yellow") is returned. Otherwise, if the value is zero or if you can't create a string that consists of
916             // pure powers of 2 OR-ed together, you return a hex value
917
918
919             // Try to see if its one of the enum values, then we return a String back else the value
920             return Enum.InternalFormat((RuntimeType)GetType(), ToUInt64()) ?? GetValue().ToString();
921         }
922         #endregion
923
924         #region IFormattable
925         [Obsolete("The provider argument is not used. Please use ToString(String).")]
926         public string ToString(string format, IFormatProvider provider)
927         {
928             return ToString(format);
929         }
930         #endregion
931
932         #region IComparable
933         public int CompareTo(object target)
934         {
935             const int retIncompatibleMethodTables = 2;  // indicates that the method tables did not match
936             const int retInvalidEnumType = 3; // indicates that the enum was of an unknown/unsupported underlying type
937
938             if (this == null)
939                 throw new NullReferenceException();
940
941             int ret = InternalCompareTo(this, target);
942
943             if (ret < retIncompatibleMethodTables)
944             {
945                 // -1, 0 and 1 are the normal return codes
946                 return ret;
947             }
948             else if (ret == retIncompatibleMethodTables)
949             {
950                 Type thisType = this.GetType();
951                 Type targetType = target.GetType();
952
953                 throw new ArgumentException(SR.Format(SR.Arg_EnumAndObjectMustBeSameType, targetType.ToString(), thisType.ToString()));
954             }
955             else
956             {
957                 // assert valid return code (3)
958                 Debug.Assert(ret == retInvalidEnumType, "Enum.InternalCompareTo return code was invalid");
959
960                 throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType);
961             }
962         }
963         #endregion
964
965         #region Public Methods
966         public string ToString(string format)
967         {
968             char formatCh;
969             if (format == null || format.Length == 0)
970                 formatCh = 'G';
971             else if (format.Length != 1)
972                 throw new FormatException(SR.Format_InvalidEnumFormatSpecification);
973             else
974                 formatCh = format[0];
975
976             if (formatCh == 'G' || formatCh == 'g')
977                 return ToString();
978
979             if (formatCh == 'D' || formatCh == 'd')
980                 return GetValue().ToString();
981
982             if (formatCh == 'X' || formatCh == 'x')
983                 return InternalFormattedHexString();
984
985             if (formatCh == 'F' || formatCh == 'f')
986                 return InternalFlagsFormat((RuntimeType)GetType(), ToUInt64()) ?? GetValue().ToString();
987
988             throw new FormatException(SR.Format_InvalidEnumFormatSpecification);
989         }
990
991         [Obsolete("The provider argument is not used. Please use ToString().")]
992         public string ToString(IFormatProvider provider)
993         {
994             return ToString();
995         }
996
997         [Intrinsic]
998         public bool HasFlag(Enum flag)
999         {
1000             if (flag == null)
1001                 throw new ArgumentNullException(nameof(flag));
1002
1003             if (!this.GetType().IsEquivalentTo(flag.GetType()))
1004             {
1005                 throw new ArgumentException(SR.Format(SR.Argument_EnumTypeDoesNotMatch, flag.GetType(), this.GetType()));
1006             }
1007
1008             return InternalHasFlag(flag);
1009         }
1010
1011         #endregion
1012
1013         #region IConvertable
1014         public TypeCode GetTypeCode()
1015         {
1016             switch (InternalGetCorElementType())
1017             {
1018                 case CorElementType.I1:
1019                     return TypeCode.SByte;
1020                 case CorElementType.U1:
1021                     return TypeCode.Byte;
1022                 case CorElementType.Boolean:
1023                     return TypeCode.Boolean;
1024                 case CorElementType.I2:
1025                     return TypeCode.Int16;
1026                 case CorElementType.U2:
1027                     return TypeCode.UInt16;
1028                 case CorElementType.Char:
1029                     return TypeCode.Char;
1030                 case CorElementType.I4:
1031                     return TypeCode.Int32;
1032                 case CorElementType.U4:
1033                     return TypeCode.UInt32;
1034                 case CorElementType.I8:
1035                     return TypeCode.Int64;
1036                 case CorElementType.U8:
1037                     return TypeCode.UInt64;
1038                 default:
1039                     throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType);
1040             }
1041         }
1042
1043         bool IConvertible.ToBoolean(IFormatProvider provider)
1044         {
1045             return Convert.ToBoolean(GetValue(), CultureInfo.CurrentCulture);
1046         }
1047
1048         char IConvertible.ToChar(IFormatProvider provider)
1049         {
1050             return Convert.ToChar(GetValue(), CultureInfo.CurrentCulture);
1051         }
1052
1053         sbyte IConvertible.ToSByte(IFormatProvider provider)
1054         {
1055             return Convert.ToSByte(GetValue(), CultureInfo.CurrentCulture);
1056         }
1057
1058         byte IConvertible.ToByte(IFormatProvider provider)
1059         {
1060             return Convert.ToByte(GetValue(), CultureInfo.CurrentCulture);
1061         }
1062
1063         short IConvertible.ToInt16(IFormatProvider provider)
1064         {
1065             return Convert.ToInt16(GetValue(), CultureInfo.CurrentCulture);
1066         }
1067
1068         ushort IConvertible.ToUInt16(IFormatProvider provider)
1069         {
1070             return Convert.ToUInt16(GetValue(), CultureInfo.CurrentCulture);
1071         }
1072
1073         int IConvertible.ToInt32(IFormatProvider provider)
1074         {
1075             return Convert.ToInt32(GetValue(), CultureInfo.CurrentCulture);
1076         }
1077
1078         uint IConvertible.ToUInt32(IFormatProvider provider)
1079         {
1080             return Convert.ToUInt32(GetValue(), CultureInfo.CurrentCulture);
1081         }
1082
1083         long IConvertible.ToInt64(IFormatProvider provider)
1084         {
1085             return Convert.ToInt64(GetValue(), CultureInfo.CurrentCulture);
1086         }
1087
1088         ulong IConvertible.ToUInt64(IFormatProvider provider)
1089         {
1090             return Convert.ToUInt64(GetValue(), CultureInfo.CurrentCulture);
1091         }
1092
1093         float IConvertible.ToSingle(IFormatProvider provider)
1094         {
1095             return Convert.ToSingle(GetValue(), CultureInfo.CurrentCulture);
1096         }
1097
1098         double IConvertible.ToDouble(IFormatProvider provider)
1099         {
1100             return Convert.ToDouble(GetValue(), CultureInfo.CurrentCulture);
1101         }
1102
1103         decimal IConvertible.ToDecimal(IFormatProvider provider)
1104         {
1105             return Convert.ToDecimal(GetValue(), CultureInfo.CurrentCulture);
1106         }
1107
1108         DateTime IConvertible.ToDateTime(IFormatProvider provider)
1109         {
1110             throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Enum", "DateTime"));
1111         }
1112
1113         object IConvertible.ToType(Type type, IFormatProvider provider)
1114         {
1115             return Convert.DefaultToType((IConvertible)this, type, provider);
1116         }
1117         #endregion
1118
1119         #region ToObject
1120         [CLSCompliant(false)]
1121         public static object ToObject(Type enumType, sbyte value)
1122         {
1123             if (enumType == null)
1124                 throw new ArgumentNullException(nameof(enumType));
1125             if (!enumType.IsEnum)
1126                 throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType));
1127             RuntimeType rtType = enumType as RuntimeType;
1128             if (rtType == null)
1129                 throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType));
1130             return InternalBoxEnum(rtType, value);
1131         }
1132
1133         public static object ToObject(Type enumType, short value)
1134         {
1135             if (enumType == null)
1136                 throw new ArgumentNullException(nameof(enumType));
1137             if (!enumType.IsEnum)
1138                 throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType));
1139             RuntimeType rtType = enumType as RuntimeType;
1140             if (rtType == null)
1141                 throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType));
1142             return InternalBoxEnum(rtType, value);
1143         }
1144
1145         public static object ToObject(Type enumType, int value)
1146         {
1147             if (enumType == null)
1148                 throw new ArgumentNullException(nameof(enumType));
1149             if (!enumType.IsEnum)
1150                 throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType));
1151             RuntimeType rtType = enumType as RuntimeType;
1152             if (rtType == null)
1153                 throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType));
1154             return InternalBoxEnum(rtType, value);
1155         }
1156
1157         public static object ToObject(Type enumType, byte value)
1158         {
1159             if (enumType == null)
1160                 throw new ArgumentNullException(nameof(enumType));
1161             if (!enumType.IsEnum)
1162                 throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType));
1163             RuntimeType rtType = enumType as RuntimeType;
1164             if (rtType == null)
1165                 throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType));
1166             return InternalBoxEnum(rtType, value);
1167         }
1168
1169         [CLSCompliant(false)]
1170         public static object ToObject(Type enumType, ushort value)
1171         {
1172             if (enumType == null)
1173                 throw new ArgumentNullException(nameof(enumType));
1174             if (!enumType.IsEnum)
1175                 throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType));
1176             RuntimeType rtType = enumType as RuntimeType;
1177             if (rtType == null)
1178                 throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType));
1179             return InternalBoxEnum(rtType, value);
1180         }
1181
1182         [CLSCompliant(false)]
1183         public static object ToObject(Type enumType, uint value)
1184         {
1185             if (enumType == null)
1186                 throw new ArgumentNullException(nameof(enumType));
1187             if (!enumType.IsEnum)
1188                 throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType));
1189             RuntimeType rtType = enumType as RuntimeType;
1190             if (rtType == null)
1191                 throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType));
1192             return InternalBoxEnum(rtType, value);
1193         }
1194
1195         public static object ToObject(Type enumType, long value)
1196         {
1197             if (enumType == null)
1198                 throw new ArgumentNullException(nameof(enumType));
1199             if (!enumType.IsEnum)
1200                 throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType));
1201             RuntimeType rtType = enumType as RuntimeType;
1202             if (rtType == null)
1203                 throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType));
1204             return InternalBoxEnum(rtType, value);
1205         }
1206
1207         [CLSCompliant(false)]
1208         public static object ToObject(Type enumType, ulong value)
1209         {
1210             if (enumType == null)
1211                 throw new ArgumentNullException(nameof(enumType));
1212             if (!enumType.IsEnum)
1213                 throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType));
1214             RuntimeType rtType = enumType as RuntimeType;
1215             if (rtType == null)
1216                 throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType));
1217             return InternalBoxEnum(rtType, unchecked((long)value));
1218         }
1219
1220         private static object ToObject(Type enumType, char value)
1221         {
1222             if (enumType == null)
1223                 throw new ArgumentNullException(nameof(enumType));
1224             if (!enumType.IsEnum)
1225                 throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType));
1226             RuntimeType rtType = enumType as RuntimeType;
1227             if (rtType == null)
1228                 throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType));
1229             return InternalBoxEnum(rtType, value);
1230         }
1231
1232         private static object ToObject(Type enumType, bool value)
1233         {
1234             if (enumType == null)
1235                 throw new ArgumentNullException(nameof(enumType));
1236             if (!enumType.IsEnum)
1237                 throw new ArgumentException(SR.Arg_MustBeEnum, nameof(enumType));
1238             RuntimeType rtType = enumType as RuntimeType;
1239             if (rtType == null)
1240                 throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType));
1241             return InternalBoxEnum(rtType, value ? 1 : 0);
1242         }
1243         #endregion
1244     }
1245 }