mirror of
https://github.com/Ellpeck/MLEM.git
synced 2024-11-22 12:58:33 +01:00
Added DynamicEnum IsDefined and EnumHelper and DynamicEnum GetUniqueFlags
This commit is contained in:
parent
7ab76d239d
commit
8745a3237e
5 changed files with 151 additions and 38 deletions
|
@ -14,7 +14,7 @@ Jump to version:
|
||||||
### MLEM
|
### MLEM
|
||||||
Additions
|
Additions
|
||||||
- Added TokenizedString.Realign
|
- Added TokenizedString.Realign
|
||||||
- Added EnumHelper.GetFlags
|
- Added GetFlags and GetUniqueFlags to EnumHelper
|
||||||
- **Added the ability to find paths to one of multiple goals using AStar**
|
- **Added the ability to find paths to one of multiple goals using AStar**
|
||||||
|
|
||||||
Improvements
|
Improvements
|
||||||
|
@ -60,7 +60,7 @@ Fixes
|
||||||
Additions
|
Additions
|
||||||
- Added data, from, and copy instructions to DataTextureAtlas
|
- Added data, from, and copy instructions to DataTextureAtlas
|
||||||
- Added the ability to add additional regions to a RuntimeTexturePacker after packing
|
- Added the ability to add additional regions to a RuntimeTexturePacker after packing
|
||||||
- Added DynamicEnum.GetFlags
|
- Added GetFlags, GetUniqueFlags and IsDefined to DynamicEnum
|
||||||
|
|
||||||
Improvements
|
Improvements
|
||||||
- Allow data texture atlas pivots and offsets to be negative
|
- Allow data texture atlas pivots and offsets to be negative
|
||||||
|
|
|
@ -134,7 +134,7 @@ namespace MLEM.Data {
|
||||||
/// <returns>The newly created enum value</returns>
|
/// <returns>The newly created enum value</returns>
|
||||||
public static T AddValue<T>(string name) where T : DynamicEnum {
|
public static T AddValue<T>(string name) where T : DynamicEnum {
|
||||||
BigInteger value = 0;
|
BigInteger value = 0;
|
||||||
while (DynamicEnum.GetStorage(typeof(T)).Values.ContainsKey(value))
|
while (DynamicEnum.IsDefined(typeof(T), value))
|
||||||
value++;
|
value++;
|
||||||
return DynamicEnum.Add<T>(name, value);
|
return DynamicEnum.Add<T>(name, value);
|
||||||
}
|
}
|
||||||
|
@ -149,7 +149,7 @@ namespace MLEM.Data {
|
||||||
/// <returns>The newly created enum value</returns>
|
/// <returns>The newly created enum value</returns>
|
||||||
public static T AddFlag<T>(string name) where T : DynamicEnum {
|
public static T AddFlag<T>(string name) where T : DynamicEnum {
|
||||||
BigInteger value = 1;
|
BigInteger value = 1;
|
||||||
while (DynamicEnum.GetStorage(typeof(T)).Values.ContainsKey(value))
|
while (DynamicEnum.IsDefined(typeof(T), value))
|
||||||
value <<= 1;
|
value <<= 1;
|
||||||
return DynamicEnum.Add<T>(name, value);
|
return DynamicEnum.Add<T>(name, value);
|
||||||
}
|
}
|
||||||
|
@ -189,6 +189,27 @@ namespace MLEM.Data {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns all of the defined unique flags from the given dynamic enum type <typeparamref name="T"/> which are contained in <paramref name="combinedFlag"/>.
|
||||||
|
/// Any combined flags (flags that aren't powers of two) which are defined in <typeparamref name="T"/> will not be returned.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="combinedFlag">The combined flags whose individual flags to return.</param>
|
||||||
|
/// <typeparam name="T">The type of enum.</typeparam>
|
||||||
|
/// <returns>All of the unique flags that make up <paramref name="combinedFlag"/>.</returns>
|
||||||
|
public static IEnumerable<T> GetUniqueFlags<T>(T combinedFlag) where T : DynamicEnum {
|
||||||
|
// we can't use the same method here as EnumHelper.GetUniqueFlags since DynamicEnum doesn't guarantee sorted values
|
||||||
|
var max = DynamicEnum.GetValues<T>().Max(DynamicEnum.GetValue);
|
||||||
|
var uniqueFlag = BigInteger.One;
|
||||||
|
while (uniqueFlag <= max) {
|
||||||
|
if (DynamicEnum.IsDefined(typeof(T), uniqueFlag)) {
|
||||||
|
var uniqueFlagValue = DynamicEnum.GetEnumValue<T>(uniqueFlag);
|
||||||
|
if (combinedFlag.HasFlag(uniqueFlagValue))
|
||||||
|
yield return uniqueFlagValue;
|
||||||
|
}
|
||||||
|
uniqueFlag <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the bitwise OR (|) combination of the two dynamic enum values
|
/// Returns the bitwise OR (|) combination of the two dynamic enum values
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -307,7 +328,8 @@ namespace MLEM.Data {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parses the given <see cref="string"/> into a dynamic enum value and returns the result.
|
/// Parses the given <see cref="string"/> into a dynamic enum value and returns the result.
|
||||||
/// This method supports defined enum values as well as values combined using the pipe (|) character and any number of spaces.
|
/// This method supports defined enum values as well as values combined using the pipe (|) character and any number of spaces.
|
||||||
/// If no enum value can be parsed, null is returned. /// </summary>
|
/// If no enum value can be parsed, null is returned.
|
||||||
|
/// </summary>
|
||||||
/// <param name="type">The type of the dynamic enum value to parse</param>
|
/// <param name="type">The type of the dynamic enum value to parse</param>
|
||||||
/// <param name="strg">The string to parse into a dynamic enum value</param>
|
/// <param name="strg">The string to parse into a dynamic enum value</param>
|
||||||
/// <returns>The parsed enum value, or null if parsing fails</returns>
|
/// <returns>The parsed enum value, or null if parsing fails</returns>
|
||||||
|
@ -330,6 +352,27 @@ namespace MLEM.Data {
|
||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns whether the given <paramref name="value"/> is defined in the given dynamic enum <paramref name="type"/>.
|
||||||
|
/// A value counts as explicitly defined if it has been added using <see cref="Add{T}"/>, <see cref="AddValue{T}"/> or <see cref="AddFlag{T}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">The dynamic enum type to query.</param>
|
||||||
|
/// <param name="value">The value to query.</param>
|
||||||
|
/// <returns>Whether the <paramref name="value"/> is defined.</returns>
|
||||||
|
public static bool IsDefined(Type type, BigInteger value) {
|
||||||
|
return DynamicEnum.GetStorage(type).Values.ContainsKey(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns whether the given <paramref name="value"/> is defined in its dynamic enum type.
|
||||||
|
/// A value counts as explicitly defined if it has been added using <see cref="Add{T}"/>, <see cref="AddValue{T}"/> or <see cref="AddFlag{T}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The value to query.</param>
|
||||||
|
/// <returns>Whether the <paramref name="value"/> is defined.</returns>
|
||||||
|
public static bool IsDefined(DynamicEnum value) {
|
||||||
|
return value != null && DynamicEnum.IsDefined(value.GetType(), DynamicEnum.GetValue(value));
|
||||||
|
}
|
||||||
|
|
||||||
private static Storage GetStorage(Type type) {
|
private static Storage GetStorage(Type type) {
|
||||||
if (!DynamicEnum.Storages.TryGetValue(type, out var storage)) {
|
if (!DynamicEnum.Storages.TryGetValue(type, out var storage)) {
|
||||||
storage = new Storage();
|
storage = new Storage();
|
||||||
|
|
|
@ -46,5 +46,24 @@ namespace MLEM.Misc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns all of the defined unique flags from the given enum type <typeparamref name="T"/> which are contained in <paramref name="combinedFlag"/>.
|
||||||
|
/// Any combined flags (flags that aren't powers of two) which are defined in <typeparamref name="T"/> will not be returned.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="combinedFlag">The combined flags whose individual flags to return.</param>
|
||||||
|
/// <typeparam name="T">The type of enum.</typeparam>
|
||||||
|
/// <returns>All of the unique flags that make up <paramref name="combinedFlag"/>.</returns>
|
||||||
|
public static IEnumerable<T> GetUniqueFlags<T>(T combinedFlag) where T : struct, Enum {
|
||||||
|
var uniqueFlag = 1;
|
||||||
|
foreach (var flag in EnumHelper.GetValues<T>()) {
|
||||||
|
var flagValue = Convert.ToInt64(flag);
|
||||||
|
// GetValues is always ordered by binary value, so we can be sure that the next flag is bigger than the last
|
||||||
|
while (uniqueFlag < flagValue)
|
||||||
|
uniqueFlag <<= 1;
|
||||||
|
if (flagValue == uniqueFlag && combinedFlag.HasFlag(flag))
|
||||||
|
yield return flag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,33 +36,6 @@ namespace Tests {
|
||||||
Assert.AreEqual(this.testObject, read);
|
Assert.AreEqual(this.testObject, read);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestDynamicEnum() {
|
|
||||||
var flags = new TestEnum[100];
|
|
||||||
for (var i = 0; i < flags.Length; i++)
|
|
||||||
flags[i] = DynamicEnum.AddFlag<TestEnum>("Flag" + i);
|
|
||||||
|
|
||||||
Assert.AreEqual(DynamicEnum.GetValue(flags[7]), BigInteger.One << 7);
|
|
||||||
Assert.AreEqual(DynamicEnum.GetEnumValue<TestEnum>(BigInteger.One << 75), flags[75]);
|
|
||||||
|
|
||||||
Assert.AreEqual(DynamicEnum.GetValue(DynamicEnum.Or(flags[2], flags[17])), BigInteger.One << 2 | BigInteger.One << 17);
|
|
||||||
Assert.AreEqual(DynamicEnum.GetValue(DynamicEnum.And(flags[2], flags[3])), BigInteger.Zero);
|
|
||||||
Assert.AreEqual(DynamicEnum.And(DynamicEnum.Or(flags[24], flags[52]), DynamicEnum.Or(flags[52], flags[75])), flags[52]);
|
|
||||||
Assert.AreEqual(DynamicEnum.Xor(DynamicEnum.Or(flags[85], flags[73]), flags[73]), flags[85]);
|
|
||||||
Assert.AreEqual(DynamicEnum.Xor(DynamicEnum.Or(flags[85], DynamicEnum.Or(flags[73], flags[12])), flags[73]), DynamicEnum.Or(flags[85], flags[12]));
|
|
||||||
Assert.AreEqual(DynamicEnum.GetValue(DynamicEnum.Neg(flags[74])), ~(BigInteger.One << 74));
|
|
||||||
|
|
||||||
Assert.AreEqual(DynamicEnum.Or(flags[24], flags[52]).HasFlag(flags[24]), true);
|
|
||||||
Assert.AreEqual(DynamicEnum.Or(flags[24], flags[52]).HasAnyFlag(flags[24]), true);
|
|
||||||
Assert.AreEqual(DynamicEnum.Or(flags[24], flags[52]).HasFlag(DynamicEnum.Or(flags[24], flags[26])), false);
|
|
||||||
Assert.AreEqual(DynamicEnum.Or(flags[24], flags[52]).HasAnyFlag(DynamicEnum.Or(flags[24], flags[26])), true);
|
|
||||||
|
|
||||||
Assert.AreEqual(DynamicEnum.Parse<TestEnum>("Flag24"), flags[24]);
|
|
||||||
Assert.AreEqual(DynamicEnum.Parse<TestEnum>("Flag24 | Flag43"), DynamicEnum.Or(flags[24], flags[43]));
|
|
||||||
Assert.AreEqual(flags[24].ToString(), "Flag24");
|
|
||||||
Assert.AreEqual(DynamicEnum.Or(flags[24], flags[43]).ToString(), "Flag24 | Flag43");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestJsonTypeSafety() {
|
public void TestJsonTypeSafety() {
|
||||||
var serializer = new JsonSerializer {TypeNameHandling = TypeNameHandling.Auto};
|
var serializer = new JsonSerializer {TypeNameHandling = TypeNameHandling.Auto};
|
||||||
|
@ -109,11 +82,5 @@ namespace Tests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestEnum : DynamicEnum {
|
|
||||||
|
|
||||||
public TestEnum(string name, BigInteger value) : base(name, value) {}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
84
Tests/EnumTests.cs
Normal file
84
Tests/EnumTests.cs
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
using MLEM.Data;
|
||||||
|
using MLEM.Misc;
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace Tests;
|
||||||
|
|
||||||
|
public class EnumTests {
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRegularEnums() {
|
||||||
|
Assert.AreEqual(
|
||||||
|
new[] {TestEnum.One, TestEnum.Two, TestEnum.Eight, TestEnum.Sixteen, TestEnum.EightSixteen},
|
||||||
|
EnumHelper.GetFlags(TestEnum.One | TestEnum.Sixteen | TestEnum.Eight | TestEnum.Two));
|
||||||
|
|
||||||
|
Assert.AreEqual(
|
||||||
|
new[] {TestEnum.One, TestEnum.Two, TestEnum.Eight, TestEnum.Sixteen},
|
||||||
|
EnumHelper.GetUniqueFlags(TestEnum.One | TestEnum.Sixteen | TestEnum.Eight | TestEnum.Two));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDynamicEnums() {
|
||||||
|
var flags = new TestDynamicEnum[100];
|
||||||
|
for (var i = 0; i < flags.Length; i++)
|
||||||
|
flags[i] = DynamicEnum.AddFlag<TestDynamicEnum>("Flag" + i);
|
||||||
|
var combined = DynamicEnum.Add<TestDynamicEnum>("Combined", DynamicEnum.GetValue(DynamicEnum.Or(flags[7], flags[13])));
|
||||||
|
|
||||||
|
Assert.AreEqual(DynamicEnum.GetValue(flags[7]), BigInteger.One << 7);
|
||||||
|
Assert.AreEqual(DynamicEnum.GetEnumValue<TestDynamicEnum>(BigInteger.One << 75), flags[75]);
|
||||||
|
|
||||||
|
Assert.AreEqual(DynamicEnum.GetValue(DynamicEnum.Or(flags[2], flags[17])), BigInteger.One << 2 | BigInteger.One << 17);
|
||||||
|
Assert.AreEqual(DynamicEnum.GetValue(DynamicEnum.And(flags[2], flags[3])), BigInteger.Zero);
|
||||||
|
Assert.AreEqual(DynamicEnum.And(DynamicEnum.Or(flags[24], flags[52]), DynamicEnum.Or(flags[52], flags[75])), flags[52]);
|
||||||
|
Assert.AreEqual(DynamicEnum.Xor(DynamicEnum.Or(flags[85], flags[73]), flags[73]), flags[85]);
|
||||||
|
Assert.AreEqual(DynamicEnum.Xor(DynamicEnum.Or(flags[85], DynamicEnum.Or(flags[73], flags[12])), flags[73]), DynamicEnum.Or(flags[85], flags[12]));
|
||||||
|
Assert.AreEqual(DynamicEnum.GetValue(DynamicEnum.Neg(flags[74])), ~(BigInteger.One << 74));
|
||||||
|
|
||||||
|
Assert.AreEqual(DynamicEnum.Or(flags[24], flags[52]).HasFlag(flags[24]), true);
|
||||||
|
Assert.AreEqual(DynamicEnum.Or(flags[24], flags[52]).HasAnyFlag(flags[24]), true);
|
||||||
|
Assert.AreEqual(DynamicEnum.Or(flags[24], flags[52]).HasFlag(DynamicEnum.Or(flags[24], flags[26])), false);
|
||||||
|
Assert.AreEqual(DynamicEnum.Or(flags[24], flags[52]).HasAnyFlag(DynamicEnum.Or(flags[24], flags[26])), true);
|
||||||
|
|
||||||
|
Assert.AreEqual(DynamicEnum.Parse<TestDynamicEnum>("Flag24"), flags[24]);
|
||||||
|
Assert.AreEqual(DynamicEnum.Parse<TestDynamicEnum>("Flag24 | Flag43"), DynamicEnum.Or(flags[24], flags[43]));
|
||||||
|
Assert.AreEqual(flags[24].ToString(), "Flag24");
|
||||||
|
Assert.AreEqual(DynamicEnum.Or(flags[24], flags[43]).ToString(), "Flag24 | Flag43");
|
||||||
|
|
||||||
|
Assert.True(DynamicEnum.IsDefined(flags[27]));
|
||||||
|
Assert.True(DynamicEnum.IsDefined(combined));
|
||||||
|
Assert.False(DynamicEnum.IsDefined(DynamicEnum.Or(flags[17], flags[49])));
|
||||||
|
Assert.False(DynamicEnum.IsDefined(DynamicEnum.Or(combined, flags[49])));
|
||||||
|
|
||||||
|
Assert.AreEqual(
|
||||||
|
new[] {flags[0], flags[7], flags[13], combined},
|
||||||
|
DynamicEnum.GetFlags(DynamicEnum.Or(DynamicEnum.Or(flags[0], flags[13]), flags[7])));
|
||||||
|
|
||||||
|
Assert.AreEqual(
|
||||||
|
new[] {flags[0], flags[7], flags[13]},
|
||||||
|
DynamicEnum.GetUniqueFlags(DynamicEnum.Or(DynamicEnum.Or(flags[0], flags[13]), flags[7])));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
private enum TestEnum {
|
||||||
|
|
||||||
|
One = 1,
|
||||||
|
Two = 2,
|
||||||
|
Eight = 8,
|
||||||
|
Sixteen = 16,
|
||||||
|
EightSixteen = TestEnum.Eight | TestEnum.Sixteen,
|
||||||
|
ThirtyTwo = 32,
|
||||||
|
OneTwentyEight = 128,
|
||||||
|
OneTwentyEightTwoOne = TestEnum.OneTwentyEight | TestEnum.Two | TestEnum.One
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestDynamicEnum : DynamicEnum {
|
||||||
|
|
||||||
|
public TestDynamicEnum(string name, BigInteger value) : base(name, value) {}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue