Skip to content

Commit

Permalink
Fix #103: EnumHelper.GetValues crashes with negative enum values
Browse files Browse the repository at this point in the history
  • Loading branch information
ig-sinicyn committed Feb 10, 2020
1 parent 347d995 commit efff362
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 6 deletions.
26 changes: 21 additions & 5 deletions CodeJam.Main.Tests/EnumHelperTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ private enum Flags : byte
D = 0x8,
// ReSharper disable once InconsistentNaming
CD = C | D,
Dx = D | 0x20
Dx = D | 0x20,
}

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

public enum NameDescEnum
public enum NameDescEnum : long
{
[Display(Name = "Field 1", Description = "Field 1 Desc")]
Field1,
Field1 = long.MinValue,

[Display]
Field2,
Field2 = 0,

Field3
Field3 = long.MaxValue
}
#endregion

Expand Down Expand Up @@ -425,5 +425,21 @@ public static void TestSetOrClearFlag()
[TestCase(NameDescEnum.Field2, ExpectedResult = "Field2")]
[TestCase(NameDescEnum.Field3, ExpectedResult = "Field3")]
public string TestGetDisplay(NameDescEnum value) => EnumHelper.GetEnumValue(value).ToString();

[Test]
public void TestNegativeValues()
{
IsTrue(EnumHelper.IsDefined(NameDescEnum.Field1));
IsFalse(EnumHelper.IsFlagsEnum<NameDescEnum>());
AreEqual(
NameDescEnum.Field1,
EnumHelper.TryParse<NameDescEnum>(nameof(NameDescEnum.Field1)));
AreEqual(
NameDescEnum.Field1,
EnumHelper.TryParse<NameDescEnum>(long.MinValue.ToString()));
AreEqual(
"Field2, Field3, Field1",
EnumHelper.GetNameValues<NameDescEnum>().Select(kvp => kvp.Key).Join(", "));
}
}
}
24 changes: 23 additions & 1 deletion CodeJam.Main/EnumHelper.Holder.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
Expand Down Expand Up @@ -93,14 +94,35 @@ private static TEnum[] GetNonDefaultFlagsCore() =>
.Distinct()
.ToArray();

private static ulong ToUInt64(object value)
{
switch (Convert.GetTypeCode(value))
{
case TypeCode.Boolean:
case TypeCode.Char:
case TypeCode.Byte:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
return Convert.ToUInt64(value, CultureInfo.InvariantCulture);
case TypeCode.SByte:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
return unchecked((ulong)Convert.ToInt64(value, CultureInfo.InvariantCulture));
default:
throw CodeExceptions.UnexpectedArgumentValue(nameof(value), value);
}
}

// NB: simple implementation here
// as result of method call is cached.
private static TEnum[] GetNonDefaultUniqueFlagsCore()
{
// THANKSTO: Maciej Hehl, https://stackoverflow.com/a/2709523
static int GetNumberOfSetBits(TEnum value)
{
var i = Convert.ToUInt64(value);
var i = ToUInt64(value);
i -= (i >> 1) & 0x5555555555555555UL;
i = (i & 0x3333333333333333UL) + ((i >> 2) & 0x3333333333333333UL);
return (int)(unchecked(((i + (i >> 4)) & 0xF0F0F0F0F0F0F0FUL) * 0x101010101010101UL) >> 56);
Expand Down

0 comments on commit efff362

Please sign in to comment.