aboutsummaryrefslogtreecommitdiff
path: root/src/ProtocolBuffers/CodedInputStream.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/ProtocolBuffers/CodedInputStream.cs')
-rw-r--r--src/ProtocolBuffers/CodedInputStream.cs84
1 files changed, 81 insertions, 3 deletions
diff --git a/src/ProtocolBuffers/CodedInputStream.cs b/src/ProtocolBuffers/CodedInputStream.cs
index 293b735a..5e7adf57 100644
--- a/src/ProtocolBuffers/CodedInputStream.cs
+++ b/src/ProtocolBuffers/CodedInputStream.cs
@@ -474,7 +474,7 @@ namespace Google.ProtocolBuffers
/// <summary>
/// Reads an enum field value from the stream. If the enum is valid for type T,
- /// then the ref value is set and it returns true. Otherwise the unkown output
+ /// then the ref value is set and it returns true. Otherwise the unknown output
/// value is set and this method returns false.
/// </summary>
[CLSCompliant(false)]
@@ -482,10 +482,9 @@ namespace Google.ProtocolBuffers
where T : struct, IComparable, IFormattable
{
int number = (int) ReadRawVarint32();
- if (Enum.IsDefined(typeof(T), number))
+ if (EnumHelper<T>.TryConvert(number, ref value))
{
unknown = null;
- value = (T) (object) number;
return true;
}
unknown = number;
@@ -1861,5 +1860,84 @@ namespace Google.ProtocolBuffers
}
#endregion
+
+ /// <summary>
+ /// Helper class to make parsing enums faster.
+ /// </summary>
+ private static class EnumHelper<T> where T : struct
+ {
+ /// <summary>
+ /// We use the array form if all values are in the range [0, LimitForArray),
+ /// otherwise we build a dictionary.
+ /// </summary>
+ private const int LimitForArray = 32;
+ // Only one of these will be populated.
+ private static readonly Dictionary<int, T> 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<int, T>();
+ foreach (int number in array)
+ {
+ dictionary[number] = (T)(object)number;
+ }
+ }
+ }
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ 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