From 1e29e701a73d3fa1466d6999b765aaddf12f1b59 Mon Sep 17 00:00:00 2001 From: csharptest Date: Sat, 7 Feb 2015 15:33:24 -0600 Subject: Fix build error for missing method Enum.GetValues() on some platforms --- .../AbstractReader.cs | 28 ++--- src/ProtocolBuffers/CodedInputStream.cs | 81 +------------ src/ProtocolBuffers/EnumLite.cs | 126 ++++++++++++++++++++- 3 files changed, 128 insertions(+), 107 deletions(-) diff --git a/src/ProtocolBuffers.Serialization/AbstractReader.cs b/src/ProtocolBuffers.Serialization/AbstractReader.cs index c8dfd2b4..e198d0b0 100644 --- a/src/ProtocolBuffers.Serialization/AbstractReader.cs +++ b/src/ProtocolBuffers.Serialization/AbstractReader.cs @@ -469,23 +469,12 @@ namespace Google.ProtocolBuffers.Serialization rawValue = null; if (ReadEnum(ref rawValue)) { - if (Enum.IsDefined(typeof(T), rawValue)) + if (!EnumParser.TryConvert(rawValue, ref value)) { - if (rawValue is int) - { - value = (T) rawValue; - } - else if (rawValue is string) - { - value = (T) Enum.Parse(typeof(T), (string) rawValue, false); - } - else - { - value = default(T); - return false; - } - return true; + value = default(T); + return false; } + return true; } return false; } @@ -560,13 +549,10 @@ namespace Google.ProtocolBuffers.Serialization { foreach (object rawValue in array) { - if (rawValue is int) - { - list.Add((T) rawValue); - } - else if (rawValue is string) + T val = default(T); + if (EnumParser.TryConvert(rawValue, ref val)) { - list.Add((T) Enum.Parse(typeof(T), (string) rawValue, false)); + list.Add(val); } else { diff --git a/src/ProtocolBuffers/CodedInputStream.cs b/src/ProtocolBuffers/CodedInputStream.cs index 4f3dfb2d..773e8c18 100644 --- a/src/ProtocolBuffers/CodedInputStream.cs +++ b/src/ProtocolBuffers/CodedInputStream.cs @@ -482,7 +482,7 @@ namespace Google.ProtocolBuffers where T : struct, IComparable, IFormattable { int number = (int) ReadRawVarint32(); - if (EnumHelper.TryConvert(number, ref value)) + if (EnumParser.TryConvert(number, ref value)) { unknown = null; return true; @@ -1860,84 +1860,5 @@ namespace Google.ProtocolBuffers } #endregion - - /// - /// Helper class to make parsing enums faster. - /// - private static class EnumHelper where T : struct - { - /// - /// We use the array form if all values are in the range [0, LimitForArray), - /// otherwise we build a dictionary. - /// - private const int LimitForArray = 32; - // Only one of these will be populated. - private static readonly Dictionary dictionary; - private static readonly T?[] values; - - static EnumHelper() - { - // It will actually be a T[], but the CLR will let us convert. - int[] array = (int[]) Enum.GetValues(typeof (T)); - if (array.Length == 0) - { - // Empty enum; model with an empty values array. - values = new T?[0]; - return; - } - int min = int.MaxValue; - int max = int.MinValue; - foreach (int number in array) - { - min = Math.Min(number, min); - max = Math.Max(number, max); - } - if (min >= 0 && max < LimitForArray) - { - values = new T?[max + 1]; - foreach (int number in array) - { - values[number] = (T)(object)number; - } - } - else - { - dictionary = new Dictionary(); - foreach (int number in array) - { - dictionary[number] = (T)(object)number; - } - } - } - - /// - /// Tries to convert an integer to its enum representation. This would take an out parameter, - /// but the caller uses ref, so this approach is simpler. - /// - internal static bool TryConvert(int number, ref T value) - { - if (values != null) - { - if (number < 0 || number >= values.Length) - { - return false; - } - T? maybeValue = values[number]; - if (maybeValue != null) - { - value = maybeValue.Value; - return true; - } - return false; - } - T converted; - if (dictionary.TryGetValue(number, out converted)) - { - value = converted; - return true; - } - return false; - } - } } } \ No newline at end of file diff --git a/src/ProtocolBuffers/EnumLite.cs b/src/ProtocolBuffers/EnumLite.cs index 978f4e51..1301ec2a 100644 --- a/src/ProtocolBuffers/EnumLite.cs +++ b/src/ProtocolBuffers/EnumLite.cs @@ -95,26 +95,140 @@ namespace Google.ProtocolBuffers public IEnumLite FindValueByNumber(int number) { - if (Enum.IsDefined(typeof(TEnum), number)) + TEnum val = default(TEnum); + if (EnumParser.TryConvert(number, ref val)) { - return new EnumValue((TEnum)(object)number); + return new EnumValue(val); } return null; } public IEnumLite FindValueByName(string name) { - if (Enum.IsDefined(typeof(TEnum), name)) + TEnum val = default(TEnum); + if (EnumParser.TryConvert(name, ref val)) { - object evalue = Enum.Parse(typeof(TEnum), name, false); - return new EnumValue((TEnum)evalue); + return new EnumValue(val); } return null; } public bool IsValidValue(IEnumLite value) { - return Enum.IsDefined(typeof(TEnum), value.Number); + TEnum val = default(TEnum); + return EnumParser.TryConvert(value.Number, ref val); + } + } + + public static class EnumParser where T : struct, IComparable, IFormattable + { + private static readonly Dictionary _byNumber; + private static Dictionary _byName; + + static EnumParser() + { + int[] array; + try + { +#if CLIENTPROFILE + // It will actually be a T[], but the CLR will let us convert. + array = (int[])Enum.GetValues(typeof(T)); +#else + var temp = new List(); + foreach (var fld in typeof (T).GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static)) + { + if (fld.IsLiteral && fld.FieldType == typeof(T)) + { + temp.Add((T)fld.GetValue(null)); + } + } + array = (int[])(object)temp.ToArray(); +#endif + } + catch + { + _byNumber = null; + return; + } + + _byNumber = new Dictionary(array.Length); + foreach (int i in array) + { + _byNumber[i] = (T)(object)i; + } + } + + public static bool TryConvert(object input, ref T value) + { + if (input is int || input is T) + { + return TryConvert((int)input, ref value); + } + if (input is string) + { + return TryConvert((string)input, ref value); + } + return false; + } + + /// + /// Tries to convert an integer to its enum representation. This would take an out parameter, + /// but the caller uses ref, so this approach is simpler. + /// + public static bool TryConvert(int number, ref T value) + { + // null indicates an exception at construction, use native IsDefined. + if (_byNumber == null) + { + return Enum.IsDefined(typeof(T), number); + } + T converted; + if (_byNumber != null && _byNumber.TryGetValue(number, out converted)) + { + value = converted; + return true; + } + + return false; + } + + /// + /// Tries to convert a string to its enum representation. This would take an out parameter, + /// but the caller uses ref, so this approach is simpler. + /// + public static bool TryConvert(string name, ref T value) + { + // null indicates an exception at construction, use native IsDefined/Parse. + if (_byNumber == null) + { + if (Enum.IsDefined(typeof(T), name)) + { + value = (T)Enum.Parse(typeof(T), name, false); + return true; + } + return false; + } + + // known race, possible multiple threads each build their own copy; however, last writer will win + var map = _byName; + if (map == null) + { + map = new Dictionary(StringComparer.Ordinal); + foreach (var possible in _byNumber.Values) + { + map[possible.ToString()] = possible; + } + _byName = map; + } + + T converted; + if (map.TryGetValue(name, out converted)) + { + value = converted; + return true; + } + + return false; } } } \ No newline at end of file -- cgit v1.2.3