Skip to content

Commit

Permalink
Added GetUniqueNonCombinatoryFlags extension for Enums
Browse files Browse the repository at this point in the history
  • Loading branch information
Measurity committed Feb 28, 2024
1 parent cdd5c87 commit 7bd816f
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 19 deletions.
37 changes: 31 additions & 6 deletions Nitrox.Test/Model/ExtensionsTests.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
using System;
using System.Collections.Generic;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace NitroxModel;
namespace NitroxModel;

[TestClass]
public class ExtensionsTest
Expand Down Expand Up @@ -71,4 +66,34 @@ public void RemoveAllFast_CanRemoveItemsWithExtraParameterInPredicate()
list.RemoveAllFast(3, static (item, length) => item.Length == length);
list.Should().BeEquivalentTo("three", "four");
}

[TestMethod]
public void GetUniqueNonCombinatoryFlags_ShouldReturnUniqueNonCombinatoryFlags()
{
TestEnumFlags.ALL.GetUniqueNonCombinatoryFlags().Should().BeEquivalentTo([TestEnumFlags.A, TestEnumFlags.B, TestEnumFlags.C, TestEnumFlags.D, TestEnumFlags.E, TestEnumFlags.F]);
TestEnumFlags.CDEF.GetUniqueNonCombinatoryFlags().Should().BeEquivalentTo([TestEnumFlags.C, TestEnumFlags.D, TestEnumFlags.E, TestEnumFlags.F]);
TestEnumFlags.E.GetUniqueNonCombinatoryFlags().Should().BeEquivalentTo([TestEnumFlags.E]);
}

[TestMethod]
public void GetUniqueNonCombinatorFlags_ShouldReturnAllUniquesWhenAllBitsSet()
{
((TestEnumFlags)int.MaxValue).GetUniqueNonCombinatoryFlags().Should().BeEquivalentTo([TestEnumFlags.A, TestEnumFlags.B, TestEnumFlags.C, TestEnumFlags.D, TestEnumFlags.E, TestEnumFlags.F]);
}

[Flags]
private enum TestEnumFlags
{
NONE = 0,
F = 1 << 5,
A = 1 << 0,
B = 1 << 1,
C = 1 << 2,
D = 1 << 3,
E = 1 << 4,
AB = A | B,
CD = C | D,
CDEF = CD | E | F,
ALL = AB | CDEF
}
}
4 changes: 4 additions & 0 deletions Nitrox.Test/Server/Serialization/WorldPersistenceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,10 @@ public class DynamicWorldDataAfterAttribute : Attribute, ITestDataSource
{
public IEnumerable<object[]> GetData(MethodInfo methodInfo)
{
if (WorldPersistenceTest.WorldsDataAfter == null)
{
return Enumerable.Empty<object[]>();
}
return WorldPersistenceTest.WorldsDataAfter.Select((t, i) => new object[] { t, WorldPersistenceTest.ServerSerializers[i].GetType().Name });
}

Expand Down
4 changes: 2 additions & 2 deletions NitroxModel/Discovery/GameInstallationFinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ public sealed class GameInstallationFinder
/// <returns>Positive and negative results from the search</returns>
public IEnumerable<GameFinderResult> FindGame(GameInfo gameInfo, GameLibraries gameLibraries = GameLibraries.ALL)
{
if (gameInfo is null || !Enum.IsDefined(typeof(GameLibraries), gameLibraries))
if (gameInfo is null || !gameLibraries.IsDefined())
{
yield break;
}

foreach (GameLibraries wantedFinder in gameLibraries.GetFlags<GameLibraries>())
foreach (GameLibraries wantedFinder in gameLibraries.GetUniqueNonCombinatoryFlags())
{
if (!finders.TryGetValue(wantedFinder, out IGameFinder finder))
{
Expand Down
47 changes: 36 additions & 11 deletions NitroxModel/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,36 @@ public static TAttribute GetAttribute<TAttribute>(this Enum value) where TAttrib
.SingleOrDefault();
}

public static TEnum[] GetFlags<TEnum>(this Enum value) where TEnum : Enum
/// <summary>
/// Gets only the unique flags of the given enum value that aren't part of a different flag in the same enum type, excluding the 0 flag.
/// </summary>
public static IEnumerable<T> GetUniqueNonCombinatoryFlags<T>(this T flags) where T : Enum
{
Type type = value.GetType();
return Enum.GetValues(type)
.Cast<TEnum>()
.Where(e => value.HasFlag(e))
.ToArray();
ulong flagCursor = 1;
foreach (T value in Enum.GetValues(typeof(T)))
{
if (!flags.HasFlag(value))
{
continue;
}

ulong definedFlagBits = Convert.ToUInt64(value);
while (flagCursor < definedFlagBits)
{
flagCursor <<= 1;
}

if (flagCursor == definedFlagBits && value.HasFlag(value))
{
yield return value;
}
}
}

/// <inheritdoc cref="Enum.IsDefined" />
public static bool IsDefined<TEnum>(this TEnum value)
{
return Enum.IsDefined(typeof(TEnum), value);
}

/// <summary>
Expand Down Expand Up @@ -68,13 +91,15 @@ public static string AsByteUnitText(this uint byteSize)
_ => exception.Message
};


/// <returns>
/// <inheritdoc cref="Enumerable.SequenceEqual{TSource}(IEnumerable{TSource}, IEnumerable{TSource})"/><br />
/// <see langword="true" /> if both IEnumerables are null.
/// <inheritdoc cref="Enumerable.SequenceEqual{TSource}(IEnumerable{TSource}, IEnumerable{TSource})" /><br />
/// <see langword="true" /> if both IEnumerables are null.
/// </returns>
/// <remarks><see cref="ArgumentNullException"/> can't be thrown because of <paramref name="first"/> or <paramref name="second"/> being null.</remarks>
/// <inheritdoc cref="Enumerable.SequenceEqual{TSource}(IEnumerable{TSource}, IEnumerable{TSource})"/>
/// <remarks>
/// <see cref="ArgumentNullException" /> can't be thrown because of <paramref name="first" /> or
/// <paramref name="second" /> being null.
/// </remarks>
/// <inheritdoc cref="Enumerable.SequenceEqual{TSource}(IEnumerable{TSource}, IEnumerable{TSource})" />
public static bool SequenceEqualOrBothNull<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second)
{
if (first != null && second != null)
Expand Down

0 comments on commit 7bd816f

Please sign in to comment.