From 7fce26e8996a0f9400e26a21472c4ecb878eb7cf Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Tue, 13 Dec 2022 18:09:00 -0700 Subject: [PATCH] Use generic math in Util.BitUtil --- src/LibHac/Util/BitUtil.cs | 41 ++++-------- tests/LibHac.Tests/Util/BitUtilTests.cs | 87 +++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 30 deletions(-) create mode 100644 tests/LibHac.Tests/Util/BitUtilTests.cs diff --git a/src/LibHac/Util/BitUtil.cs b/src/LibHac/Util/BitUtil.cs index 473b40fe..2d0cdc82 100644 --- a/src/LibHac/Util/BitUtil.cs +++ b/src/LibHac/Util/BitUtil.cs @@ -5,47 +5,28 @@ namespace LibHac.Util; public static class BitUtil { - public static bool IsPowerOfTwo(int value) + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsPowerOfTwo(T value) where T : IBitwiseOperators, INumberBase { - return value > 0 && ResetLeastSignificantOneBit(value) == 0; + return !T.IsNegative(value) && !T.IsZero(value) && T.IsZero(ResetLeastSignificantOneBit(value)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsPowerOfTwo(long value) + public static T ResetLeastSignificantOneBit(T value) where T : IBitwiseOperators, INumberBase { - return value > 0 && ResetLeastSignificantOneBit(value) == 0; + return value & unchecked(value - T.One); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsPowerOfTwo(ulong value) + public static int CountLeadingZeros(T value) where T : IBinaryInteger { - return value > 0 && ResetLeastSignificantOneBit(value) == 0; + return int.CreateTruncating(T.LeadingZeroCount(value)); } - private static int ResetLeastSignificantOneBit(int value) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T DivideUp(T value, T divisor) where T : INumberBase { - return value & (value - 1); + return unchecked(value + divisor - T.One) / divisor; } - - private static long ResetLeastSignificantOneBit(long value) - { - return value & (value - 1); - } - - private static ulong ResetLeastSignificantOneBit(ulong value) - { - return value & (value - 1); - } - - public static int CountLeadingZeros(uint value) - { - return BitOperations.LeadingZeroCount(value); - } - - // DivideUp comes from a C++ template that always casts to unsigned types - public static uint DivideUp(uint value, uint divisor) => (value + divisor - 1) / divisor; - public static ulong DivideUp(ulong value, ulong divisor) => (value + divisor - 1) / divisor; - - public static int DivideUp(int value, int divisor) => (int)DivideUp((uint)value, (uint)divisor); - public static long DivideUp(long value, long divisor) => (long)DivideUp((ulong)value, (ulong)divisor); } \ No newline at end of file diff --git a/tests/LibHac.Tests/Util/BitUtilTests.cs b/tests/LibHac.Tests/Util/BitUtilTests.cs new file mode 100644 index 00000000..ec87e0e9 --- /dev/null +++ b/tests/LibHac.Tests/Util/BitUtilTests.cs @@ -0,0 +1,87 @@ +using LibHac.Util; +using Xunit; + +namespace LibHac.Tests.Util; + +public class BitUtilTests +{ + [Theory] + [InlineData(0x80, 0)] + [InlineData(0x00, 8)] + public void CountLeadingZeros_Byte(byte value, int expectedValue) + { + Assert.Equal(expectedValue, BitUtil.CountLeadingZeros(value)); + } + + [Theory] + [InlineData(0x0080, 8)] + [InlineData(0x0000, 16)] + public void CountLeadingZeros_Short(short value, int expectedValue) + { + Assert.Equal(expectedValue, BitUtil.CountLeadingZeros(value)); + } + + [Theory] + [InlineData(0b0000_1000_0110_0000, 0b0000_1000_0100_0000)] + [InlineData(0b0000_1000_0110_1111, 0b0000_1000_0110_1110)] + [InlineData(unchecked((short)0b1000_0000_0000_0000), 0b0000_0000_0000_0000)] + [InlineData(0, 0)] + public void ResetLeastSignificantOneBit_Short(short value, short expectedValue) + { + Assert.Equal(expectedValue, BitUtil.ResetLeastSignificantOneBit(value)); + } + + [Theory] + [InlineData(0b0101_0000_0000_0000_0000, 0b0100_0000_0000_0000_0000)] + [InlineData(0b1111_0000_1000_0110_1111, 0b1111_0000_1000_0110_1110)] + [InlineData(0b0100_1000_0000_0000_0000, 0b0100_0000_0000_0000_0000)] + [InlineData(0, 0)] + public void ResetLeastSignificantOneBit_Uint(uint value, uint expectedValue) + { + Assert.Equal(expectedValue, BitUtil.ResetLeastSignificantOneBit(value)); + } + + [Theory] + [InlineData(0x80, true)] + [InlineData(0x81, false)] + [InlineData(0, false)] + [InlineData(short.MinValue, false)] + public void IsPowerOfTwo_Short(short value, bool expectedValue) + { + Assert.Equal(expectedValue, BitUtil.IsPowerOfTwo(value)); + } + + [Theory] + [InlineData(0x0000100000000000, true)] + [InlineData(0x0000100000004000, false)] + [InlineData(0, false)] + public void IsPowerOfTwo_Ulong(ulong value, bool expectedValue) + { + Assert.Equal(expectedValue, BitUtil.IsPowerOfTwo(value)); + } + + [Theory] + [InlineData(-55, -2, 29)] + [InlineData(-55, 2, -27)] + [InlineData(0, 26, 0)] + [InlineData(-55, -26, 3)] + [InlineData(55, -26, -1)] + [InlineData(int.MinValue, -26, -82595523)] + public void DivideUp_Int(int value, int divisor, int expectedValue) + { + Assert.Equal(expectedValue, BitUtil.DivideUp(value, divisor)); + } + + [Theory] + [InlineData(55, 2, 28)] + [InlineData(0, 26, 0)] + [InlineData(1127, 24, 47)] + [InlineData(1128, 24, 47)] + [InlineData(1129, 24, 48)] + [InlineData(567, 987, 1)] + [InlineData(int.MaxValue, 26, 82595525)] + public void DivideUp_Uint(uint value, uint divisor, uint expectedValue) + { + Assert.Equal(expectedValue, BitUtil.DivideUp(value, divisor)); + } +} \ No newline at end of file