diff --git a/nuget.config b/nuget.config
deleted file mode 100644
index 41b398ea..00000000
--- a/nuget.config
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
diff --git a/src/LibHac/FsSrv/Impl/DeviceOperator.cs b/src/LibHac/FsSrv/Impl/DeviceOperator.cs
index 4c79e93e..ba4466a6 100644
--- a/src/LibHac/FsSrv/Impl/DeviceOperator.cs
+++ b/src/LibHac/FsSrv/Impl/DeviceOperator.cs
@@ -39,14 +39,14 @@ public class DeviceOperator : IDeviceOperator
private static Span GetSpan(OutBuffer buffer, long size)
{
- Assert.True(IntUtil.IsIntValueRepresentableAsInt(size));
+ Assert.True(IntUtil.IsIntValueRepresentable(size));
return buffer.Buffer.Slice(0, (int)size);
}
private static ReadOnlySpan GetSpan(InBuffer buffer, long size)
{
- Assert.True(IntUtil.IsIntValueRepresentableAsInt(size));
+ Assert.True(IntUtil.IsIntValueRepresentable(size));
return buffer.Buffer.Slice(0, (int)size);
}
diff --git a/src/LibHac/Result.cs b/src/LibHac/Result.cs
index cfd6fc79..066441c9 100644
--- a/src/LibHac/Result.cs
+++ b/src/LibHac/Result.cs
@@ -386,7 +386,7 @@ public readonly struct Result : IEquatable
[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);
}
///
diff --git a/src/LibHac/Util/IntUtil.cs b/src/LibHac/Util/IntUtil.cs
index 52524e31..5e29b4f0 100644
--- a/src/LibHac/Util/IntUtil.cs
+++ b/src/LibHac/Util/IntUtil.cs
@@ -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() where T : INumber => 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(TFrom value)
+ where TTo : IMinMaxValue, INumber
+ where TFrom : IMinMaxValue, INumber
{
- return value >= 0;
- }
-
- public static bool IsIntValueRepresentableAsInt(long value)
- {
- return value >= int.MinValue && value <= int.MaxValue;
- }
-
- public static bool IsIntValueRepresentableAsUInt(long value)
- {
- return value >= uint.MinValue && value <= uint.MaxValue;
- }
-
- public static bool CanAddWithoutOverflow(long x, long y)
- {
- if (y >= 0)
+ if (IsSignedType())
{
- return x <= long.MaxValue - y;
+ if (IsSignedType())
+ {
+ return IsIntValueRepresentableImplSToS(value);
+ }
+ else
+ {
+ return IsIntValueRepresentableImplSToU(value);
+ }
}
else
{
- return x >= unchecked(long.MinValue - y);
+ if (IsSignedType())
+ {
+ return IsIntValueRepresentableImplUToS(value);
+ }
+ else
+ {
+ return IsIntValueRepresentableImplUToU(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(TFrom value)
+ where TTo : IMinMaxValue, INumber
+ where TFrom : IMinMaxValue, INumber
{
- 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(TFrom value)
+ where TTo : IMinMaxValue, INumber
+ where TFrom : IMinMaxValue, INumber
+ {
+ 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(TFrom value)
+ where TTo : IMinMaxValue, INumber
+ where TFrom : IMinMaxValue, INumber, IComparisonOperators
+ {
+ if (value < TFrom.Zero)
+ {
+ return false;
+ }
+
+ return IsIntValueRepresentableImplUToU(value);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static bool IsIntValueRepresentableImplUToS(TFrom value)
+ where TTo : IMinMaxValue, INumber
+ where TFrom : IMinMaxValue, INumber
+ {
+ return ulong.CreateTruncating(value) <= ulong.CreateTruncating(TTo.MaxValue);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool CanAddWithoutOverflow(T x, T y) where T : IMinMaxValue, INumber
+ {
+ if (y >= T.Zero)
+ {
+ return x <= T.MaxValue - y;
+ }
+ else
+ {
+ return x >= T.MinValue - y;
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool CanSubtractWithoutOverflow(T x, T y) where T : IMinMaxValue, INumber
+ {
+ if (y >= T.Zero)
+ {
+ return x >= T.MinValue + y;
+ }
+ else
+ {
+ return x <= T.MaxValue + y;
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool CanMultiplyWithoutOverflow(T x, T y) where T : IMinMaxValue, INumber
+ {
+ 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(out T value, T x, T y) where T : IMinMaxValue, INumber
+ {
+ if (CanAddWithoutOverflow(x, y))
+ {
+ value = x + y;
+ return true;
+ }
+ else
+ {
+ value = default;
+ return false;
+ }
+ }
+
+ public static bool TrySubtractWithoutOverflow(out T value, T x, T y) where T : IMinMaxValue, INumber
+ {
+ if (CanSubtractWithoutOverflow(x, y))
+ {
+ value = x - y;
+ return true;
+ }
+ else
+ {
+ value = default;
+ return false;
+ }
+ }
+
+ public static bool TryMultiplyWithoutOverflow(out T value, T x, T y) where T : IMinMaxValue, INumber
+ {
+ if (CanMultiplyWithoutOverflow(x, y))
+ {
+ value = x * y;
+ return true;
+ }
+ else
+ {
+ value = default;
+ return false;
+ }
}
}
\ No newline at end of file
diff --git a/tests/LibHac.Tests/Kvdb/FlatMapKeyValueStoreTests.cs b/tests/LibHac.Tests/Kvdb/FlatMapKeyValueStoreTests.cs
index 7b36f616..78a4abb5 100644
--- a/tests/LibHac.Tests/Kvdb/FlatMapKeyValueStoreTests.cs
+++ b/tests/LibHac.Tests/Kvdb/FlatMapKeyValueStoreTests.cs
@@ -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;
}
diff --git a/tests/LibHac.Tests/Random.cs b/tests/LibHac.Tests/Random.cs
index d776d39f..abd14ad6 100644
--- a/tests/LibHac.Tests/Random.cs
+++ b/tests/LibHac.Tests/Random.cs
@@ -11,23 +11,26 @@ public struct Random
public Random(ulong seed)
{
- ulong x = seed;
- ulong z = x + 0x9e3779b97f4a7c15;
- z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
- z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
- x = z ^ (z >> 31);
- z = (x += 0x9e3779b97f4a7c15);
- z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
- z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
- _state1 = z ^ (z >> 31);
- _state2 = x;
+ unchecked
+ {
+ ulong x = seed;
+ ulong z = x + 0x9e3779b97f4a7c15;
+ z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
+ z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
+ x = z ^ (z >> 31);
+ z = (x += 0x9e3779b97f4a7c15);
+ z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
+ z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
+ _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,8 +46,11 @@ public struct Random
throw new ArgumentOutOfRangeException(nameof(minValue));
}
- long range = (long)maxValue - minValue;
- return (int)((uint)Next() * (1.0 / uint.MaxValue) * range) + minValue;
+ unchecked
+ {
+ long range = (long)maxValue - minValue;
+ return (int)((uint)Next() * (1.0 / uint.MaxValue) * range) + minValue;
+ }
}
public void NextBytes(Span 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());
}
}
}
\ No newline at end of file
diff --git a/tests/LibHac.Tests/Util/IntUtilTests.cs b/tests/LibHac.Tests/Util/IntUtilTests.cs
new file mode 100644
index 00000000..b89c6e56
--- /dev/null
+++ b/tests/LibHac.Tests/Util/IntUtilTests.cs
@@ -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(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(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(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(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(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(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(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(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(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 (value);
+ Assert.Equal(expectedResult, actualResult);
+ }
+}
\ No newline at end of file