From 9178814c3d3e36ec61e9421df1e321183ad19872 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 3 Oct 2026 18:51:17 +0000 Subject: [PATCH] Add BigInteger.ToByteArray/GetByteCount/TryWriteBytes polyfills (NetCore21) Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PolyShim.Tests/NetCore21/BigIntegerTests.cs | 148 ++++++++++++++++++++ PolyShim/NetCore21/BigInteger.cs | 71 ++++++++++ Signatures.md | 8 +- 3 files changed, 225 insertions(+), 2 deletions(-) create mode 100644 PolyShim.Tests/NetCore21/BigIntegerTests.cs create mode 100644 PolyShim/NetCore21/BigInteger.cs diff --git a/PolyShim.Tests/NetCore21/BigIntegerTests.cs b/PolyShim.Tests/NetCore21/BigIntegerTests.cs new file mode 100644 index 0000000..18b189b --- /dev/null +++ b/PolyShim.Tests/NetCore21/BigIntegerTests.cs @@ -0,0 +1,148 @@ +using System; +using System.Numerics; +using FluentAssertions; +using Xunit; + +namespace PolyShim.Tests.NetCore21; + +public class BigIntegerTests +{ + [Fact] + public void ToByteArray_SignedLittleEndian_Test() + { + // Arrange + var value = new BigInteger(33022); + + // Act + var bytes = value.ToByteArray(isUnsigned: false, isBigEndian: false); + + // Assert + bytes.Should().Equal(0xFE, 0x80, 0x00); + } + + [Fact] + public void ToByteArray_SignedBigEndian_Test() + { + // Arrange + var value = new BigInteger(33022); + + // Act + var bytes = value.ToByteArray(isUnsigned: false, isBigEndian: true); + + // Assert + bytes.Should().Equal(0x00, 0x80, 0xFE); + } + + [Fact] + public void ToByteArray_UnsignedLittleEndian_Test() + { + // Arrange + var value = new BigInteger(33022); + + // Act + var bytes = value.ToByteArray(isUnsigned: true, isBigEndian: false); + + // Assert + bytes.Should().Equal(0xFE, 0x80); + } + + [Fact] + public void ToByteArray_UnsignedBigEndian_Test() + { + // Arrange + var value = new BigInteger(33022); + + // Act + var bytes = value.ToByteArray(isUnsigned: true, isBigEndian: true); + + // Assert + bytes.Should().Equal(0x80, 0xFE); + } + + [Fact] + public void ToByteArray_Zero_Test() + { + // Arrange + var value = BigInteger.Zero; + + // Act + var bytes = value.ToByteArray(isUnsigned: true, isBigEndian: true); + + // Assert + bytes.Should().Equal(0x00); + } + + [Fact] + public void ToByteArray_NegativeUnsigned_Test() + { + // Arrange + var value = new BigInteger(-1); + + // Act & assert + var act = () => value.ToByteArray(isUnsigned: true); + act.Should().Throw(); + } + + [Fact] + public void GetByteCount_Test() + { + // Arrange + var value = new BigInteger(33022); + + // Act & assert + value.GetByteCount().Should().Be(3); + value.GetByteCount(isUnsigned: true).Should().Be(2); + } + + [Fact] + public void TryWriteBytes_Success_Test() + { + // Arrange + var value = new BigInteger(33022); + var destination = new byte[3]; + + // Act + var result = value.TryWriteBytes(destination, out var bytesWritten); + + // Assert + result.Should().BeTrue(); + bytesWritten.Should().Be(3); + destination.Should().Equal(0xFE, 0x80, 0x00); + } + + [Fact] + public void TryWriteBytes_TooSmallDestination_Test() + { + // Arrange + var value = new BigInteger(33022); + var destination = new byte[1]; + + // Act + var result = value.TryWriteBytes(destination, out var bytesWritten); + + // Assert + result.Should().BeFalse(); + bytesWritten.Should().Be(0); + } + + [Fact] + public void TryWriteBytes_UnsignedBigEndian_Test() + { + // Arrange + var value = new BigInteger(33022); + var destination = new byte[2]; + + // Act + var result = value.TryWriteBytes( + destination, + out var bytesWritten, + isUnsigned: true, + isBigEndian: true + ); + + // Assert + result.Should().BeTrue(); + bytesWritten.Should().Be(2); + destination.Should().Equal(0x80, 0xFE); + } +} diff --git a/PolyShim/NetCore21/BigInteger.cs b/PolyShim/NetCore21/BigInteger.cs new file mode 100644 index 0000000..0fc922c --- /dev/null +++ b/PolyShim/NetCore21/BigInteger.cs @@ -0,0 +1,71 @@ +#if (NETCOREAPP && !NETCOREAPP2_1_OR_GREATER) || (NETFRAMEWORK) || (NETSTANDARD && !NETSTANDARD2_1_OR_GREATER) +// BigInteger is only available on .NET Framework 4.0+ and .NET Standard 1.1+ +#if (!NETFRAMEWORK || NET40_OR_GREATER) && (!NETSTANDARD || NETSTANDARD1_1_OR_GREATER) +#nullable enable +#pragma warning disable CS0436 + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Numerics; + +#if !POLYSHIM_INCLUDE_COVERAGE +[ExcludeFromCodeCoverage] +#endif +internal static class MemberPolyfills_NetCore21_BigInteger +{ + extension(BigInteger value) + { + // https://learn.microsoft.com/dotnet/api/system.numerics.biginteger.getbytecount + public int GetByteCount(bool isUnsigned = false) => + value.ToByteArray(isUnsigned, isBigEndian: false).Length; + + // https://learn.microsoft.com/dotnet/api/system.numerics.biginteger.tobytearray#system-numerics-biginteger-tobytearray(system-boolean-system-boolean) + public byte[] ToByteArray(bool isUnsigned = false, bool isBigEndian = false) + { + if (isUnsigned && value.Sign < 0) + throw new OverflowException( + "Negative values do not have an unsigned representation." + ); + + var bytes = value.ToByteArray(); + + // The signed little-endian representation may contain an extra zero byte in the + // most significant position, used solely to disambiguate the sign. That byte is + // redundant for an unsigned representation, so it can be safely dropped. + if (isUnsigned && bytes.Length > 1 && bytes[bytes.Length - 1] == 0) + { + var trimmedBytes = new byte[bytes.Length - 1]; + Array.Copy(bytes, trimmedBytes, trimmedBytes.Length); + bytes = trimmedBytes; + } + + if (isBigEndian) + Array.Reverse(bytes); + + return bytes; + } + + // https://learn.microsoft.com/dotnet/api/system.numerics.biginteger.trywritebytes + public bool TryWriteBytes( + Span destination, + out int bytesWritten, + bool isUnsigned = false, + bool isBigEndian = false + ) + { + var bytes = value.ToByteArray(isUnsigned, isBigEndian); + if (bytes.Length > destination.Length) + { + bytesWritten = 0; + return false; + } + + bytes.CopyTo(destination); + bytesWritten = bytes.Length; + + return true; + } + } +} +#endif +#endif diff --git a/Signatures.md b/Signatures.md index 792ea19..20c2de7 100644 --- a/Signatures.md +++ b/Signatures.md @@ -1,8 +1,8 @@ # Signatures -- **Total:** 611 +- **Total:** 614 - **Types:** 120 -- **Members:** 491 +- **Members:** 494 ___ @@ -45,6 +45,10 @@ ___ - [**[struct]**](https://learn.microsoft.com/dotnet/api/system.runtime.compilerservices.asyncvaluetaskmethodbuilder) .NET Core 2.1 - `AsyncValueTaskMethodBuilder` - [**[struct]**](https://learn.microsoft.com/dotnet/api/system.runtime.compilerservices.asyncvaluetaskmethodbuilder-1) .NET Core 2.1 +- `BigInteger` + - [`bool TryWriteBytes(Span, out int, bool, bool)`](https://learn.microsoft.com/dotnet/api/system.numerics.biginteger.trywritebytes) .NET Core 2.1 + - [`byte[] ToByteArray(bool, bool)`](https://learn.microsoft.com/dotnet/api/system.numerics.biginteger.tobytearray#system-numerics-biginteger-tobytearray(system-boolean-system-boolean)) .NET Core 2.1 + - [`int GetByteCount(bool)`](https://learn.microsoft.com/dotnet/api/system.numerics.biginteger.getbytecount) .NET Core 2.1 - `BinaryPrimitives` - [**[class]**](https://learn.microsoft.com/dotnet/api/system.buffers.binary.binaryprimitives) .NET Core 2.1 - [`static bool TryReadDoubleBigEndian(ReadOnlySpan, out double)`](https://learn.microsoft.com/dotnet/api/system.buffers.binary.binaryprimitives.tryreaddoublebigendian) .NET 5.0