mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2025-02-09 13:14:46 +01:00
Use generic math in Util.IntUtil
This commit is contained in:
parent
d47e0ebedc
commit
b7625af148
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key="dotnet-core" value="https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json" />
|
||||
</packageSources>
|
||||
</configuration>
|
@ -39,14 +39,14 @@ public class DeviceOperator : IDeviceOperator
|
||||
|
||||
private static Span<byte> GetSpan(OutBuffer buffer, long size)
|
||||
{
|
||||
Assert.True(IntUtil.IsIntValueRepresentableAsInt(size));
|
||||
Assert.True(IntUtil.IsIntValueRepresentable<int, long>(size));
|
||||
|
||||
return buffer.Buffer.Slice(0, (int)size);
|
||||
}
|
||||
|
||||
private static ReadOnlySpan<byte> GetSpan(InBuffer buffer, long size)
|
||||
{
|
||||
Assert.True(IntUtil.IsIntValueRepresentableAsInt(size));
|
||||
Assert.True(IntUtil.IsIntValueRepresentable<int, long>(size));
|
||||
|
||||
return buffer.Buffer.Slice(0, (int)size);
|
||||
}
|
||||
|
@ -386,7 +386,7 @@ public readonly struct Result : IEquatable<Result>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static ulong SetBitsValueLong(int value, int bitsOffset, int bitsCount)
|
||||
{
|
||||
return ((uint)value & ~(~default(ulong) << bitsCount)) << bitsOffset;
|
||||
return unchecked(((uint)value & ~(~default(ulong) << bitsCount)) << bitsOffset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -1,42 +1,189 @@
|
||||
namespace LibHac.Util;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace LibHac.Util;
|
||||
|
||||
public static class IntUtil
|
||||
{
|
||||
// Todo: Use generic math once C# 11 is out
|
||||
public static bool IsIntValueRepresentableAsLong(ulong value)
|
||||
{
|
||||
return value <= long.MaxValue;
|
||||
}
|
||||
private static bool IsSignedType<T>() where T : INumber<T> => unchecked(T.IsNegative(T.Zero - T.One));
|
||||
|
||||
public static bool IsIntValueRepresentableAsULong(long value)
|
||||
// Todo .NET 8: Remove NoInlining. .NET 7's JIT doesn't inline everything properly
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static bool IsIntValueRepresentable<TTo, TFrom>(TFrom value)
|
||||
where TTo : IMinMaxValue<TTo>, INumber<TTo>
|
||||
where TFrom : IMinMaxValue<TFrom>, INumber<TFrom>
|
||||
{
|
||||
return value >= 0;
|
||||
}
|
||||
|
||||
public static bool IsIntValueRepresentableAsInt(long value)
|
||||
if (IsSignedType<TFrom>())
|
||||
{
|
||||
return value >= int.MinValue && value <= int.MaxValue;
|
||||
}
|
||||
|
||||
public static bool IsIntValueRepresentableAsUInt(long value)
|
||||
if (IsSignedType<TTo>())
|
||||
{
|
||||
return value >= uint.MinValue && value <= uint.MaxValue;
|
||||
}
|
||||
|
||||
public static bool CanAddWithoutOverflow(long x, long y)
|
||||
{
|
||||
if (y >= 0)
|
||||
{
|
||||
return x <= long.MaxValue - y;
|
||||
return IsIntValueRepresentableImplSToS<TTo, TFrom>(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return x >= unchecked(long.MinValue - y);
|
||||
return IsIntValueRepresentableImplSToU<TTo, TFrom>(value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsSignedType<TTo>())
|
||||
{
|
||||
return IsIntValueRepresentableImplUToS<TTo, TFrom>(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return IsIntValueRepresentableImplUToU<TTo, TFrom>(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool CanAddWithoutOverflow(ulong x, ulong y)
|
||||
// Methods for the 4 signed/unsigned TTo/TFrom permutations.
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool IsIntValueRepresentableImplSToS<TTo, TFrom>(TFrom value)
|
||||
where TTo : IMinMaxValue<TTo>, INumber<TTo>
|
||||
where TFrom : IMinMaxValue<TFrom>, INumber<TFrom>
|
||||
{
|
||||
return x <= ulong.MaxValue - y;
|
||||
if (long.CreateTruncating(TTo.MinValue) <= long.CreateTruncating(TFrom.MinValue) &&
|
||||
long.CreateTruncating(TFrom.MaxValue) <= long.CreateTruncating(TTo.MaxValue))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return long.CreateTruncating(TTo.MinValue) <= long.CreateTruncating(value) &&
|
||||
long.CreateTruncating(value) <= long.CreateTruncating(TTo.MaxValue);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool IsIntValueRepresentableImplUToU<TTo, TFrom>(TFrom value)
|
||||
where TTo : IMinMaxValue<TTo>, INumber<TTo>
|
||||
where TFrom : IMinMaxValue<TFrom>, INumber<TFrom>
|
||||
{
|
||||
if (ulong.CreateTruncating(TTo.MinValue) <= ulong.CreateTruncating(TFrom.MinValue) &&
|
||||
ulong.CreateTruncating(TFrom.MaxValue) <= ulong.CreateTruncating(TTo.MaxValue))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return ulong.CreateTruncating(TTo.MinValue) <= ulong.CreateTruncating(value) &&
|
||||
ulong.CreateTruncating(value) <= ulong.CreateTruncating(TTo.MaxValue);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool IsIntValueRepresentableImplSToU<TTo, TFrom>(TFrom value)
|
||||
where TTo : IMinMaxValue<TTo>, INumber<TTo>
|
||||
where TFrom : IMinMaxValue<TFrom>, INumber<TFrom>, IComparisonOperators<TFrom, TFrom, bool>
|
||||
{
|
||||
if (value < TFrom.Zero)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return IsIntValueRepresentableImplUToU<TTo, TFrom>(value);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool IsIntValueRepresentableImplUToS<TTo, TFrom>(TFrom value)
|
||||
where TTo : IMinMaxValue<TTo>, INumber<TTo>
|
||||
where TFrom : IMinMaxValue<TFrom>, INumber<TFrom>
|
||||
{
|
||||
return ulong.CreateTruncating(value) <= ulong.CreateTruncating(TTo.MaxValue);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool CanAddWithoutOverflow<T>(T x, T y) where T : IMinMaxValue<T>, INumber<T>
|
||||
{
|
||||
if (y >= T.Zero)
|
||||
{
|
||||
return x <= T.MaxValue - y;
|
||||
}
|
||||
else
|
||||
{
|
||||
return x >= T.MinValue - y;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool CanSubtractWithoutOverflow<T>(T x, T y) where T : IMinMaxValue<T>, INumber<T>
|
||||
{
|
||||
if (y >= T.Zero)
|
||||
{
|
||||
return x >= T.MinValue + y;
|
||||
}
|
||||
else
|
||||
{
|
||||
return x <= T.MaxValue + y;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool CanMultiplyWithoutOverflow<T>(T x, T y) where T : IMinMaxValue<T>, INumber<T>
|
||||
{
|
||||
if (x == T.Zero || y == T.Zero)
|
||||
return true;
|
||||
|
||||
if (x > T.Zero)
|
||||
{
|
||||
if (y > T.Zero)
|
||||
{
|
||||
return y <= T.MaxValue / x;
|
||||
}
|
||||
else
|
||||
{
|
||||
return y >= T.MinValue / x;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (y > T.Zero)
|
||||
{
|
||||
return x >= T.MinValue / y;
|
||||
}
|
||||
else
|
||||
{
|
||||
return y >= T.MaxValue / x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryAddWithoutOverflow<T>(out T value, T x, T y) where T : IMinMaxValue<T>, INumber<T>
|
||||
{
|
||||
if (CanAddWithoutOverflow(x, y))
|
||||
{
|
||||
value = x + y;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TrySubtractWithoutOverflow<T>(out T value, T x, T y) where T : IMinMaxValue<T>, INumber<T>
|
||||
{
|
||||
if (CanSubtractWithoutOverflow(x, y))
|
||||
{
|
||||
value = x - y;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryMultiplyWithoutOverflow<T>(out T value, T x, T y) where T : IMinMaxValue<T>, INumber<T>
|
||||
{
|
||||
if (CanMultiplyWithoutOverflow(x, y))
|
||||
{
|
||||
value = x * y;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -47,7 +47,7 @@ public class FlatMapKeyValueStoreTests
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
byte[] value = new byte[startingSize + i];
|
||||
value.AsSpan().Fill((byte)count);
|
||||
value.AsSpan().Fill(unchecked((byte)count));
|
||||
values[i] = value;
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,8 @@ public struct Random
|
||||
private ulong _state2;
|
||||
|
||||
public Random(ulong seed)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
ulong x = seed;
|
||||
ulong z = x + 0x9e3779b97f4a7c15;
|
||||
@ -22,12 +24,13 @@ public struct Random
|
||||
_state1 = z ^ (z >> 31);
|
||||
_state2 = x;
|
||||
}
|
||||
}
|
||||
|
||||
ulong Next()
|
||||
{
|
||||
ulong s0 = _state1;
|
||||
ulong s1 = _state2;
|
||||
ulong result = BitOperations.RotateLeft(s0 + s1, 17) + s0;
|
||||
ulong result = unchecked(BitOperations.RotateLeft(s0 + s1, 17) + s0);
|
||||
|
||||
s1 ^= s0;
|
||||
_state1 = BitOperations.RotateLeft(s0, 49) ^ s1 ^ (s1 << 21);
|
||||
@ -43,9 +46,12 @@ public struct Random
|
||||
throw new ArgumentOutOfRangeException(nameof(minValue));
|
||||
}
|
||||
|
||||
unchecked
|
||||
{
|
||||
long range = (long)maxValue - minValue;
|
||||
return (int)((uint)Next() * (1.0 / uint.MaxValue) * range) + minValue;
|
||||
}
|
||||
}
|
||||
|
||||
public void NextBytes(Span<byte> buffer)
|
||||
{
|
||||
@ -58,7 +64,7 @@ public struct Random
|
||||
|
||||
for (int i = bufferUlong.Length * sizeof(ulong); i < buffer.Length; i++)
|
||||
{
|
||||
buffer[i] = (byte)Next();
|
||||
buffer[i] = unchecked((byte)Next());
|
||||
}
|
||||
}
|
||||
}
|
200
tests/LibHac.Tests/Util/IntUtilTests.cs
Normal file
200
tests/LibHac.Tests/Util/IntUtilTests.cs
Normal file
@ -0,0 +1,200 @@
|
||||
using LibHac.Util;
|
||||
using Xunit;
|
||||
|
||||
namespace LibHac.Tests.Util;
|
||||
|
||||
public class IntUtilTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(0x80, 0x7F, true)]
|
||||
[InlineData(0x80, 0x80, false)]
|
||||
public void CanAddWithoutOverflow_Byte(byte x, byte y, bool expectedResult)
|
||||
{
|
||||
Assert.Equal(expectedResult, IntUtil.CanAddWithoutOverflow(x, y));
|
||||
Assert.Equal(expectedResult, IntUtil.CanAddWithoutOverflow(y, x));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(-0x8000, -1, false)]
|
||||
[InlineData(-0x8000, 0, true)]
|
||||
[InlineData(0x4000, 0x3FFF, true)]
|
||||
[InlineData(0x4000, 0x4000, false)]
|
||||
[InlineData(-0x4000, -0x4000, true)]
|
||||
public void CanAddWithoutOverflow_Short(short x, short y, bool expectedResult)
|
||||
{
|
||||
Assert.Equal(expectedResult, IntUtil.CanAddWithoutOverflow(x, y));
|
||||
Assert.Equal(expectedResult, IntUtil.CanAddWithoutOverflow(y, x));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0x80, 0x80, true)]
|
||||
[InlineData(0x80, 0x81, false)]
|
||||
public void CanSubtractWithoutOverflow_Byte(byte x, byte y, bool expectedResult)
|
||||
{
|
||||
Assert.Equal(expectedResult, IntUtil.CanSubtractWithoutOverflow(x, y));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(-0x8000, 1, false)]
|
||||
[InlineData(-0x8000, 0, true)]
|
||||
[InlineData(-0x4000, 0x4000, true)]
|
||||
[InlineData(-0x4000, 0x4001, false)]
|
||||
[InlineData(0x4000, -0x4000, false)]
|
||||
public void CanSubtractWithoutOverflow_Short(short x, short y, bool expectedResult)
|
||||
{
|
||||
Assert.Equal(expectedResult, IntUtil.CanSubtractWithoutOverflow(x, y));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0xFF, 0, true)]
|
||||
[InlineData(0x55, 3, true)]
|
||||
[InlineData(0x40, 4, false)]
|
||||
public void CanMultiplyWithoutOverflow_Byte(byte x, byte y, bool expectedResult)
|
||||
{
|
||||
Assert.Equal(expectedResult, IntUtil.CanMultiplyWithoutOverflow(x, y));
|
||||
Assert.Equal(expectedResult, IntUtil.CanMultiplyWithoutOverflow(y, x));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0x7FFF, 0, true)]
|
||||
[InlineData(+0x1249, +7, true)]
|
||||
[InlineData(-0x1249, +7, true)]
|
||||
[InlineData(-0x1249, -7, true)]
|
||||
[InlineData(+0x1249, -7, true)]
|
||||
[InlineData(+0x2000, +4, false)]
|
||||
[InlineData(-0x2000, +4, true)]
|
||||
[InlineData(-0x2000, -4, false)]
|
||||
[InlineData(+0x2000, -4, true)]
|
||||
[InlineData(-0x2001, +4, false)]
|
||||
[InlineData(+0x2001, -4, false)]
|
||||
[InlineData(-0x8000, -1, false)]
|
||||
[InlineData(-0x7FFF, -1, true)]
|
||||
public void CanMultiplyWithoutOverflow_Short(short x, short y, bool expectedResult)
|
||||
{
|
||||
Assert.Equal(expectedResult, IntUtil.CanMultiplyWithoutOverflow(x, y));
|
||||
Assert.Equal(expectedResult, IntUtil.CanMultiplyWithoutOverflow(y, x));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0x5555555555555555, 3, true)]
|
||||
[InlineData(0x4000000000000000, 4, false)]
|
||||
public void CanMultiplyWithoutOverflow_Ulong(ulong x, ulong y, bool expectedResult)
|
||||
{
|
||||
Assert.Equal(expectedResult, IntUtil.CanMultiplyWithoutOverflow(x, y));
|
||||
Assert.Equal(expectedResult, IntUtil.CanMultiplyWithoutOverflow(y, x));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, true)]
|
||||
[InlineData(-1, false)]
|
||||
[InlineData(unchecked((long)0x8111111100000000), false)]
|
||||
[InlineData(long.MinValue, false)]
|
||||
[InlineData(long.MaxValue, true)]
|
||||
public void IsIntValueRepresentable_LongToUlong(long value, bool expectedResult)
|
||||
{
|
||||
bool actualResult = IntUtil.IsIntValueRepresentable<ulong, long>(value);
|
||||
Assert.Equal(expectedResult, actualResult);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, true)]
|
||||
[InlineData(5, true)]
|
||||
[InlineData(long.MaxValue, true)]
|
||||
[InlineData(long.MaxValue + 1LU, false)]
|
||||
[InlineData(ulong.MaxValue, false)]
|
||||
public void IsIntValueRepresentable_ULongToLong(ulong value, bool expectedResult)
|
||||
{
|
||||
bool actualResult = IntUtil.IsIntValueRepresentable<long, ulong>(value);
|
||||
Assert.Equal(expectedResult, actualResult);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, true)]
|
||||
[InlineData(-1, true)]
|
||||
[InlineData(int.MinValue, true)]
|
||||
[InlineData(int.MaxValue, true)]
|
||||
[InlineData(int.MinValue - 1L, false)]
|
||||
[InlineData(int.MaxValue + 1L, false)]
|
||||
public void IsIntValueRepresentable_LongToInt(long value, bool expectedResult)
|
||||
{
|
||||
bool actualResult = IntUtil.IsIntValueRepresentable<int, long>(value);
|
||||
Assert.Equal(expectedResult, actualResult);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, true)]
|
||||
[InlineData(-1, true)]
|
||||
[InlineData(short.MinValue, true)]
|
||||
[InlineData(short.MaxValue, true)]
|
||||
[InlineData(short.MinValue - 1L, false)]
|
||||
[InlineData(short.MaxValue + 1L, false)]
|
||||
public void IsIntValueRepresentable_LongToShort(long value, bool expectedResult)
|
||||
{
|
||||
bool actualResult = IntUtil.IsIntValueRepresentable<short, long>(value);
|
||||
Assert.Equal(expectedResult, actualResult);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, true)]
|
||||
[InlineData(-1, false)]
|
||||
[InlineData(uint.MaxValue, true)]
|
||||
[InlineData(uint.MaxValue + 1L, false)]
|
||||
public void IsIntValueRepresentable_LongToUint(long value, bool expectedResult)
|
||||
{
|
||||
bool actualResult = IntUtil.IsIntValueRepresentable<uint, long>(value);
|
||||
Assert.Equal(expectedResult, actualResult);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, true)]
|
||||
[InlineData(-1, false)]
|
||||
[InlineData(ushort.MaxValue, true)]
|
||||
[InlineData(ushort.MaxValue + 1L, false)]
|
||||
public void IsIntValueRepresentable_LongToUshort(long value, bool expectedResult)
|
||||
{
|
||||
bool actualResult = IntUtil.IsIntValueRepresentable<ushort, long>(value);
|
||||
Assert.Equal(expectedResult, actualResult);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, true)]
|
||||
[InlineData(-1, false)]
|
||||
[InlineData(short.MinValue, false)]
|
||||
[InlineData(short.MaxValue, true)]
|
||||
public void IsIntValueRepresentable_ShortToUshort(short value, bool expectedResult)
|
||||
{
|
||||
bool actualResult = IntUtil.IsIntValueRepresentable<ushort, short>(value);
|
||||
Assert.Equal(expectedResult, actualResult);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, true)]
|
||||
[InlineData(0xFFFFF000, false)]
|
||||
[InlineData(0xFFFF7FFF, false)]
|
||||
[InlineData(short.MaxValue, true)]
|
||||
[InlineData(short.MaxValue + 1, false)]
|
||||
public void IsIntValueRepresentable_UintToShort(uint value, bool expectedResult)
|
||||
{
|
||||
bool actualResult = IntUtil.IsIntValueRepresentable<short, uint>(value);
|
||||
Assert.Equal(expectedResult, actualResult);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, true)]
|
||||
[InlineData(uint.MaxValue, true)]
|
||||
[InlineData((ulong)uint.MaxValue + 1, false)]
|
||||
public void IsIntValueRepresentable_UlongToUint(ulong value, bool expectedResult)
|
||||
{
|
||||
bool actualResult = IntUtil.IsIntValueRepresentable<uint, ulong>(value);
|
||||
Assert.Equal(expectedResult, actualResult);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0, true)]
|
||||
[InlineData(uint.MaxValue, true)]
|
||||
public void IsIntValueRepresentable_UintToUlong(uint value, bool expectedResult)
|
||||
{
|
||||
bool actualResult = IntUtil.IsIntValueRepresentable<ulong, uint> (value);
|
||||
Assert.Equal(expectedResult, actualResult);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user