Skip to content

Commit efff362

Browse files
committed
Fix #103: EnumHelper.GetValues crashes with negative enum values
1 parent 347d995 commit efff362

File tree

2 files changed

+44
-6
lines changed

2 files changed

+44
-6
lines changed

CodeJam.Main.Tests/EnumHelperTests.cs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ private enum Flags : byte
4040
D = 0x8,
4141
// ReSharper disable once InconsistentNaming
4242
CD = C | D,
43-
Dx = D | 0x20
43+
Dx = D | 0x20,
4444
}
4545

4646
private const Flags Ab = Flags.A | Flags.B;
@@ -77,15 +77,15 @@ private enum NoFlags : byte
7777
private const NoFlags EfU = NoFlags.E | NoFlags.F | NoFlagsUndef;
7878
// ReSharper restore BitwiseOperatorOnEnumWithoutFlags
7979

80-
public enum NameDescEnum
80+
public enum NameDescEnum : long
8181
{
8282
[Display(Name = "Field 1", Description = "Field 1 Desc")]
83-
Field1,
83+
Field1 = long.MinValue,
8484

8585
[Display]
86-
Field2,
86+
Field2 = 0,
8787

88-
Field3
88+
Field3 = long.MaxValue
8989
}
9090
#endregion
9191

@@ -425,5 +425,21 @@ public static void TestSetOrClearFlag()
425425
[TestCase(NameDescEnum.Field2, ExpectedResult = "Field2")]
426426
[TestCase(NameDescEnum.Field3, ExpectedResult = "Field3")]
427427
public string TestGetDisplay(NameDescEnum value) => EnumHelper.GetEnumValue(value).ToString();
428+
429+
[Test]
430+
public void TestNegativeValues()
431+
{
432+
IsTrue(EnumHelper.IsDefined(NameDescEnum.Field1));
433+
IsFalse(EnumHelper.IsFlagsEnum<NameDescEnum>());
434+
AreEqual(
435+
NameDescEnum.Field1,
436+
EnumHelper.TryParse<NameDescEnum>(nameof(NameDescEnum.Field1)));
437+
AreEqual(
438+
NameDescEnum.Field1,
439+
EnumHelper.TryParse<NameDescEnum>(long.MinValue.ToString()));
440+
AreEqual(
441+
"Field2, Field3, Field1",
442+
EnumHelper.GetNameValues<NameDescEnum>().Select(kvp => kvp.Key).Join(", "));
443+
}
428444
}
429445
}

CodeJam.Main/EnumHelper.Holder.cs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Globalization;
34
using System.Linq;
45
using System.Linq.Expressions;
56
using System.Reflection;
@@ -93,14 +94,35 @@ private static TEnum[] GetNonDefaultFlagsCore() =>
9394
.Distinct()
9495
.ToArray();
9596

97+
private static ulong ToUInt64(object value)
98+
{
99+
switch (Convert.GetTypeCode(value))
100+
{
101+
case TypeCode.Boolean:
102+
case TypeCode.Char:
103+
case TypeCode.Byte:
104+
case TypeCode.UInt16:
105+
case TypeCode.UInt32:
106+
case TypeCode.UInt64:
107+
return Convert.ToUInt64(value, CultureInfo.InvariantCulture);
108+
case TypeCode.SByte:
109+
case TypeCode.Int16:
110+
case TypeCode.Int32:
111+
case TypeCode.Int64:
112+
return unchecked((ulong)Convert.ToInt64(value, CultureInfo.InvariantCulture));
113+
default:
114+
throw CodeExceptions.UnexpectedArgumentValue(nameof(value), value);
115+
}
116+
}
117+
96118
// NB: simple implementation here
97119
// as result of method call is cached.
98120
private static TEnum[] GetNonDefaultUniqueFlagsCore()
99121
{
100122
// THANKSTO: Maciej Hehl, https://stackoverflow.com/a/2709523
101123
static int GetNumberOfSetBits(TEnum value)
102124
{
103-
var i = Convert.ToUInt64(value);
125+
var i = ToUInt64(value);
104126
i -= (i >> 1) & 0x5555555555555555UL;
105127
i = (i & 0x3333333333333333UL) + ((i >> 2) & 0x3333333333333333UL);
106128
return (int)(unchecked(((i + (i >> 4)) & 0xF0F0F0F0F0F0F0FUL) * 0x101010101010101UL) >> 56);

0 commit comments

Comments
 (0)