From dc0f73a33a4f3960e117e0a8e556753f492f96ad Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 14:38:28 +0000 Subject: [PATCH 01/12] Initial plan From b26a09a06de879caf83c47542a945f058b3aa97e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 14:42:57 +0000 Subject: [PATCH 02/12] Add RandomNumberGenerator.Fill polyfill with review feedback addressed Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- .../NetCore21/RandomNumberGeneratorTests.cs | 25 ++++++++++++++ PolyShim/NetCore21/RandomNumberGenerator.cs | 33 +++++++++++++++++++ PolyShim/Signatures.md | 6 ++-- 3 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 PolyShim.Tests/NetCore21/RandomNumberGeneratorTests.cs create mode 100644 PolyShim/NetCore21/RandomNumberGenerator.cs diff --git a/PolyShim.Tests/NetCore21/RandomNumberGeneratorTests.cs b/PolyShim.Tests/NetCore21/RandomNumberGeneratorTests.cs new file mode 100644 index 00000000..3ba7dca8 --- /dev/null +++ b/PolyShim.Tests/NetCore21/RandomNumberGeneratorTests.cs @@ -0,0 +1,25 @@ +using System; +using System.Security.Cryptography; +using FluentAssertions; +using Xunit; + +namespace PolyShim.Tests.NetCore21; + +public class RandomNumberGeneratorTests +{ + [Fact] + public void Fill_Span_Test() + { + // Arrange + var buffer = new byte[16]; + var span = new Span(buffer); + + // Act + RandomNumberGenerator.Fill(span); + + // Assert + // Since cryptographic random data is non-deterministic, + // we can only verify that the buffer contains some non-zero bytes + buffer.Should().Contain(b => b != 0); + } +} diff --git a/PolyShim/NetCore21/RandomNumberGenerator.cs b/PolyShim/NetCore21/RandomNumberGenerator.cs new file mode 100644 index 00000000..3595abec --- /dev/null +++ b/PolyShim/NetCore21/RandomNumberGenerator.cs @@ -0,0 +1,33 @@ +#if (NETCOREAPP && !NETCOREAPP2_1_OR_GREATER) || (NETFRAMEWORK && NET40_OR_GREATER) || (NETSTANDARD && NETSTANDARD1_3_OR_GREATER && !NETSTANDARD2_1_OR_GREATER) +#nullable enable +// ReSharper disable RedundantUsingDirective +// ReSharper disable CheckNamespace +// ReSharper disable InconsistentNaming +// ReSharper disable PartialTypeWithSinglePart + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Security.Cryptography; + +[ExcludeFromCodeCoverage] +internal static class MemberPolyfills_NetCore21_RandomNumberGenerator +{ + extension(RandomNumberGenerator) + { + // https://learn.microsoft.com/dotnet/api/system.security.cryptography.randomnumbergenerator.fill + public static void Fill(Span data) + { + // Fast-path for empty spans to avoid unnecessary allocations + if (data.Length == 0) + return; + + var buffer = new byte[data.Length]; + using (var rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(buffer); + } + buffer.CopyTo(data); + } + } +} +#endif diff --git a/PolyShim/Signatures.md b/PolyShim/Signatures.md index b08d43b5..272b74ad 100644 --- a/PolyShim/Signatures.md +++ b/PolyShim/Signatures.md @@ -1,8 +1,8 @@ # Signatures -- **Total:** 340 +- **Total:** 341 - **Types:** 74 -- **Members:** 266 +- **Members:** 267 ___ @@ -275,6 +275,8 @@ ___ - [`T[] GetItems(T[], int)`](https://learn.microsoft.com/dotnet/api/system.random.getitems#system-random-getitems-1(-0()-system-int32)) .NET 8.0 - [`void NextBytes(Span)`](https://learn.microsoft.com/dotnet/api/system.random.nextbytes#system-random-nextbytes(system-span((system-byte)))) .NET Core 2.1 - [`void Shuffle(T[])`](https://learn.microsoft.com/dotnet/api/system.random.shuffle#system-random-shuffle-1(-0())) .NET 8.0 +- `RandomNumberGenerator` + - [`void Fill(Span)`](https://learn.microsoft.com/dotnet/api/system.security.cryptography.randomnumbergenerator.fill) .NET Core 2.1 - `Range` - [**[struct]**](https://learn.microsoft.com/dotnet/api/system.range) .NET Core 3.0 - `ReadOnlyMemory` From bce162bc20d6e82571d15209ec9160353fed9e51 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 14:44:24 +0000 Subject: [PATCH 03/12] Address code review feedback: move CopyTo inside using block and add empty span test Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- .../NetCore21/RandomNumberGeneratorTests.cs | 12 ++++++++++++ PolyShim/NetCore21/RandomNumberGenerator.cs | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/PolyShim.Tests/NetCore21/RandomNumberGeneratorTests.cs b/PolyShim.Tests/NetCore21/RandomNumberGeneratorTests.cs index 3ba7dca8..97dc61e3 100644 --- a/PolyShim.Tests/NetCore21/RandomNumberGeneratorTests.cs +++ b/PolyShim.Tests/NetCore21/RandomNumberGeneratorTests.cs @@ -22,4 +22,16 @@ public void Fill_Span_Test() // we can only verify that the buffer contains some non-zero bytes buffer.Should().Contain(b => b != 0); } + + [Fact] + public void Fill_EmptySpan_Test() + { + // Arrange + var buffer = new byte[0]; + var span = new Span(buffer); + + // Act & Assert + // Should not throw and should return immediately + RandomNumberGenerator.Fill(span); + } } diff --git a/PolyShim/NetCore21/RandomNumberGenerator.cs b/PolyShim/NetCore21/RandomNumberGenerator.cs index 3595abec..d5b9414a 100644 --- a/PolyShim/NetCore21/RandomNumberGenerator.cs +++ b/PolyShim/NetCore21/RandomNumberGenerator.cs @@ -25,8 +25,8 @@ public static void Fill(Span data) using (var rng = RandomNumberGenerator.Create()) { rng.GetBytes(buffer); + buffer.CopyTo(data); } - buffer.CopyTo(data); } } } From df32acb1a6acd705ec409650bbb05ff9a31d9b7b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 14:47:51 +0000 Subject: [PATCH 04/12] Add null check for RandomNumberGenerator.Create and improve test assertion Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PolyShim.Tests/NetCore21/RandomNumberGeneratorTests.cs | 4 ++-- PolyShim/NetCore21/RandomNumberGenerator.cs | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/PolyShim.Tests/NetCore21/RandomNumberGeneratorTests.cs b/PolyShim.Tests/NetCore21/RandomNumberGeneratorTests.cs index 97dc61e3..98361ca4 100644 --- a/PolyShim.Tests/NetCore21/RandomNumberGeneratorTests.cs +++ b/PolyShim.Tests/NetCore21/RandomNumberGeneratorTests.cs @@ -19,8 +19,8 @@ public void Fill_Span_Test() // Assert // Since cryptographic random data is non-deterministic, - // we can only verify that the buffer contains some non-zero bytes - buffer.Should().Contain(b => b != 0); + // we verify that not all bytes are zero (virtually impossible for crypto RNG) + buffer.Should().NotBeEquivalentTo(new byte[16]); } [Fact] diff --git a/PolyShim/NetCore21/RandomNumberGenerator.cs b/PolyShim/NetCore21/RandomNumberGenerator.cs index d5b9414a..16c44be4 100644 --- a/PolyShim/NetCore21/RandomNumberGenerator.cs +++ b/PolyShim/NetCore21/RandomNumberGenerator.cs @@ -24,6 +24,11 @@ public static void Fill(Span data) var buffer = new byte[data.Length]; using (var rng = RandomNumberGenerator.Create()) { + if (rng == null) + throw new InvalidOperationException( + "Failed to create RandomNumberGenerator instance" + ); + rng.GetBytes(buffer); buffer.CopyTo(data); } From d44533dc48c339315eb2ce7525a79dd46987e3a6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 14:49:09 +0000 Subject: [PATCH 05/12] Remove unnecessary null check and clarify test assertion comment Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PolyShim.Tests/NetCore21/RandomNumberGeneratorTests.cs | 5 +++-- PolyShim/NetCore21/RandomNumberGenerator.cs | 5 ----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/PolyShim.Tests/NetCore21/RandomNumberGeneratorTests.cs b/PolyShim.Tests/NetCore21/RandomNumberGeneratorTests.cs index 98361ca4..f31192ea 100644 --- a/PolyShim.Tests/NetCore21/RandomNumberGeneratorTests.cs +++ b/PolyShim.Tests/NetCore21/RandomNumberGeneratorTests.cs @@ -18,8 +18,9 @@ public void Fill_Span_Test() RandomNumberGenerator.Fill(span); // Assert - // Since cryptographic random data is non-deterministic, - // we verify that not all bytes are zero (virtually impossible for crypto RNG) + // Verify that not all bytes are zero (cryptographic RNG should produce non-zero data) + // Note: Theoretically, a crypto RNG could produce all zeros (probability: 2^-128), + // but this is astronomically unlikely in practice buffer.Should().NotBeEquivalentTo(new byte[16]); } diff --git a/PolyShim/NetCore21/RandomNumberGenerator.cs b/PolyShim/NetCore21/RandomNumberGenerator.cs index 16c44be4..d5b9414a 100644 --- a/PolyShim/NetCore21/RandomNumberGenerator.cs +++ b/PolyShim/NetCore21/RandomNumberGenerator.cs @@ -24,11 +24,6 @@ public static void Fill(Span data) var buffer = new byte[data.Length]; using (var rng = RandomNumberGenerator.Create()) { - if (rng == null) - throw new InvalidOperationException( - "Failed to create RandomNumberGenerator instance" - ); - rng.GetBytes(buffer); buffer.CopyTo(data); } From 981b9ca664ff9439c52b72f45a28339a0448cf88 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 15:06:57 +0000 Subject: [PATCH 06/12] Address PR feedback: simplify compilation guards, improve test assertions, use try-finally for disposal Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- .../NetCore21/RandomNumberGeneratorTests.cs | 12 +++++------- PolyShim/NetCore21/RandomNumberGenerator.cs | 12 ++++++++++-- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/PolyShim.Tests/NetCore21/RandomNumberGeneratorTests.cs b/PolyShim.Tests/NetCore21/RandomNumberGeneratorTests.cs index f31192ea..f3c79aea 100644 --- a/PolyShim.Tests/NetCore21/RandomNumberGeneratorTests.cs +++ b/PolyShim.Tests/NetCore21/RandomNumberGeneratorTests.cs @@ -11,25 +11,23 @@ public class RandomNumberGeneratorTests public void Fill_Span_Test() { // Arrange - var buffer = new byte[16]; - var span = new Span(buffer); + var span = new Span(new byte[16]); // Act RandomNumberGenerator.Fill(span); // Assert - // Verify that not all bytes are zero (cryptographic RNG should produce non-zero data) + // Verify that not all bytes are zero (cryptographic RNG should produce non-zero data). // Note: Theoretically, a crypto RNG could produce all zeros (probability: 2^-128), - // but this is astronomically unlikely in practice - buffer.Should().NotBeEquivalentTo(new byte[16]); + // but this is astronomically unlikely in practice. + span.ToArray().Should().Contain(b => b != 0); } [Fact] public void Fill_EmptySpan_Test() { // Arrange - var buffer = new byte[0]; - var span = new Span(buffer); + var span = new Span(); // Act & Assert // Should not throw and should return immediately diff --git a/PolyShim/NetCore21/RandomNumberGenerator.cs b/PolyShim/NetCore21/RandomNumberGenerator.cs index d5b9414a..e78a65cf 100644 --- a/PolyShim/NetCore21/RandomNumberGenerator.cs +++ b/PolyShim/NetCore21/RandomNumberGenerator.cs @@ -1,4 +1,4 @@ -#if (NETCOREAPP && !NETCOREAPP2_1_OR_GREATER) || (NETFRAMEWORK && NET40_OR_GREATER) || (NETSTANDARD && NETSTANDARD1_3_OR_GREATER && !NETSTANDARD2_1_OR_GREATER) +#if (NETCOREAPP && !NETCOREAPP2_1_OR_GREATER) || (NETFRAMEWORK) || (NETSTANDARD && !NETSTANDARD2_1_OR_GREATER) #nullable enable // ReSharper disable RedundantUsingDirective // ReSharper disable CheckNamespace @@ -7,6 +7,7 @@ using System; using System.Diagnostics.CodeAnalysis; +#if !NETSTANDARD || NETSTANDARD1_3_OR_GREATER using System.Security.Cryptography; [ExcludeFromCodeCoverage] @@ -22,12 +23,19 @@ public static void Fill(Span data) return; var buffer = new byte[data.Length]; - using (var rng = RandomNumberGenerator.Create()) + var rng = RandomNumberGenerator.Create(); + try { rng.GetBytes(buffer); buffer.CopyTo(data); } + finally + { + // Explicit Dispose() for compatibility with older frameworks + ((IDisposable)rng).Dispose(); + } } } } #endif +#endif From 1bf1f0c6735b80feeafe257366047b66c8ba4783 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 15:22:39 +0000 Subject: [PATCH 07/12] Use scoped using declaration (using var) with conditional compilation for net35 Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PolyShim/NetCore21/RandomNumberGenerator.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/PolyShim/NetCore21/RandomNumberGenerator.cs b/PolyShim/NetCore21/RandomNumberGenerator.cs index e78a65cf..2a984638 100644 --- a/PolyShim/NetCore21/RandomNumberGenerator.cs +++ b/PolyShim/NetCore21/RandomNumberGenerator.cs @@ -23,6 +23,7 @@ public static void Fill(Span data) return; var buffer = new byte[data.Length]; +#if NET35 var rng = RandomNumberGenerator.Create(); try { @@ -31,9 +32,13 @@ public static void Fill(Span data) } finally { - // Explicit Dispose() for compatibility with older frameworks ((IDisposable)rng).Dispose(); } +#else + using var rng = RandomNumberGenerator.Create(); + rng.GetBytes(buffer); + buffer.CopyTo(data); +#endif } } } From 5c023b4ae5818f836fba1a72691a5b7cdf7b5c78 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 15:30:31 +0000 Subject: [PATCH 08/12] Remove NET35 conditional by casting to IDisposable in using statement Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PolyShim/NetCore21/RandomNumberGenerator.cs | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/PolyShim/NetCore21/RandomNumberGenerator.cs b/PolyShim/NetCore21/RandomNumberGenerator.cs index 2a984638..f23d48a2 100644 --- a/PolyShim/NetCore21/RandomNumberGenerator.cs +++ b/PolyShim/NetCore21/RandomNumberGenerator.cs @@ -23,22 +23,9 @@ public static void Fill(Span data) return; var buffer = new byte[data.Length]; -#if NET35 - var rng = RandomNumberGenerator.Create(); - try - { - rng.GetBytes(buffer); - buffer.CopyTo(data); - } - finally - { - ((IDisposable)rng).Dispose(); - } -#else - using var rng = RandomNumberGenerator.Create(); - rng.GetBytes(buffer); + using var rng = (IDisposable)RandomNumberGenerator.Create(); + ((RandomNumberGenerator)rng).GetBytes(buffer); buffer.CopyTo(data); -#endif } } } From 0d9a36a7e6baafee9822f6effa81ff7e0242f0db Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 15:41:12 +0000 Subject: [PATCH 09/12] Add RandomNumberGenerator polyfill for netstandard<1.3, use try-finally, remove comment Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PolyShim/NetCore21/RandomNumberGenerator.cs | 75 +++++++++++++++++++-- 1 file changed, 69 insertions(+), 6 deletions(-) diff --git a/PolyShim/NetCore21/RandomNumberGenerator.cs b/PolyShim/NetCore21/RandomNumberGenerator.cs index f23d48a2..be8810eb 100644 --- a/PolyShim/NetCore21/RandomNumberGenerator.cs +++ b/PolyShim/NetCore21/RandomNumberGenerator.cs @@ -7,8 +7,66 @@ using System; using System.Diagnostics.CodeAnalysis; -#if !NETSTANDARD || NETSTANDARD1_3_OR_GREATER +#if NETSTANDARD && !NETSTANDARD1_3_OR_GREATER +using System.Reflection; +#endif + +#if NETSTANDARD && !NETSTANDARD1_3_OR_GREATER +// Polyfill for RandomNumberGenerator class on older .NET Standard versions +namespace System.Security.Cryptography; + +[ExcludeFromCodeCoverage] +internal abstract class RandomNumberGenerator : IDisposable +{ + public static RandomNumberGenerator Create() => new RNGCryptoServiceProviderWrapper(); + + public abstract void GetBytes(byte[] data); + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) { } + + private sealed class RNGCryptoServiceProviderWrapper : RandomNumberGenerator + { + private readonly object _rng; + private readonly System.Reflection.MethodInfo _getBytesMethod; + + public RNGCryptoServiceProviderWrapper() + { + var rngType = Type.GetType( + "System.Security.Cryptography.RNGCryptoServiceProvider, mscorlib" + ); + if (rngType == null) + throw new PlatformNotSupportedException("RNGCryptoServiceProvider not available"); + + _rng = Activator.CreateInstance(rngType); + _getBytesMethod = rngType.GetTypeInfo().GetDeclaredMethod("GetBytes"); + if (_getBytesMethod == null) + throw new PlatformNotSupportedException("GetBytes method not found"); + } + + public override void GetBytes(byte[] data) + { + _getBytesMethod.Invoke(_rng, new object[] { data }); + } + + protected override void Dispose(bool disposing) + { + if (disposing && _rng is IDisposable disposable) + { + disposable.Dispose(); + } + base.Dispose(disposing); + } + } +} +#else using System.Security.Cryptography; +#endif [ExcludeFromCodeCoverage] internal static class MemberPolyfills_NetCore21_RandomNumberGenerator @@ -18,16 +76,21 @@ internal static class MemberPolyfills_NetCore21_RandomNumberGenerator // https://learn.microsoft.com/dotnet/api/system.security.cryptography.randomnumbergenerator.fill public static void Fill(Span data) { - // Fast-path for empty spans to avoid unnecessary allocations if (data.Length == 0) return; var buffer = new byte[data.Length]; - using var rng = (IDisposable)RandomNumberGenerator.Create(); - ((RandomNumberGenerator)rng).GetBytes(buffer); - buffer.CopyTo(data); + var rng = RandomNumberGenerator.Create(); + try + { + rng.GetBytes(buffer); + buffer.CopyTo(data); + } + finally + { + ((IDisposable)rng).Dispose(); + } } } } #endif -#endif From 1207292f476b5a317854bc7d218f32aa1716cde1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 15:48:33 +0000 Subject: [PATCH 10/12] Move RandomNumberGenerator class polyfill to NetCore10, use Random wrapper, add IDisposable cast comment Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PolyShim/NetCore10/RandomNumberGenerator.cs | 41 ++++++++++++++ PolyShim/NetCore21/RandomNumberGenerator.cs | 61 +-------------------- 2 files changed, 43 insertions(+), 59 deletions(-) create mode 100644 PolyShim/NetCore10/RandomNumberGenerator.cs diff --git a/PolyShim/NetCore10/RandomNumberGenerator.cs b/PolyShim/NetCore10/RandomNumberGenerator.cs new file mode 100644 index 00000000..579fb37d --- /dev/null +++ b/PolyShim/NetCore10/RandomNumberGenerator.cs @@ -0,0 +1,41 @@ +#if NETSTANDARD && !NETSTANDARD1_3_OR_GREATER +#nullable enable +// ReSharper disable RedundantUsingDirective +// ReSharper disable CheckNamespace +// ReSharper disable InconsistentNaming +// ReSharper disable PartialTypeWithSinglePart + +using System; +using System.Diagnostics.CodeAnalysis; + +namespace System.Security.Cryptography; + +// Polyfill for RandomNumberGenerator class on .NET Standard < 1.3 +// Note: This uses Random instead of cryptographically secure RNG as System.Security.Cryptography +// types are not available on these older platforms. +[ExcludeFromCodeCoverage] +internal abstract class RandomNumberGenerator : IDisposable +{ + public static RandomNumberGenerator Create() => new RandomWrapper(); + + public abstract void GetBytes(byte[] data); + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) { } + + private sealed class RandomWrapper : RandomNumberGenerator + { + private readonly Random _random = new(); + + public override void GetBytes(byte[] data) + { + _random.NextBytes(data); + } + } +} +#endif diff --git a/PolyShim/NetCore21/RandomNumberGenerator.cs b/PolyShim/NetCore21/RandomNumberGenerator.cs index be8810eb..e335ab6f 100644 --- a/PolyShim/NetCore21/RandomNumberGenerator.cs +++ b/PolyShim/NetCore21/RandomNumberGenerator.cs @@ -7,66 +7,7 @@ using System; using System.Diagnostics.CodeAnalysis; -#if NETSTANDARD && !NETSTANDARD1_3_OR_GREATER -using System.Reflection; -#endif - -#if NETSTANDARD && !NETSTANDARD1_3_OR_GREATER -// Polyfill for RandomNumberGenerator class on older .NET Standard versions -namespace System.Security.Cryptography; - -[ExcludeFromCodeCoverage] -internal abstract class RandomNumberGenerator : IDisposable -{ - public static RandomNumberGenerator Create() => new RNGCryptoServiceProviderWrapper(); - - public abstract void GetBytes(byte[] data); - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) { } - - private sealed class RNGCryptoServiceProviderWrapper : RandomNumberGenerator - { - private readonly object _rng; - private readonly System.Reflection.MethodInfo _getBytesMethod; - - public RNGCryptoServiceProviderWrapper() - { - var rngType = Type.GetType( - "System.Security.Cryptography.RNGCryptoServiceProvider, mscorlib" - ); - if (rngType == null) - throw new PlatformNotSupportedException("RNGCryptoServiceProvider not available"); - - _rng = Activator.CreateInstance(rngType); - _getBytesMethod = rngType.GetTypeInfo().GetDeclaredMethod("GetBytes"); - if (_getBytesMethod == null) - throw new PlatformNotSupportedException("GetBytes method not found"); - } - - public override void GetBytes(byte[] data) - { - _getBytesMethod.Invoke(_rng, new object[] { data }); - } - - protected override void Dispose(bool disposing) - { - if (disposing && _rng is IDisposable disposable) - { - disposable.Dispose(); - } - base.Dispose(disposing); - } - } -} -#else using System.Security.Cryptography; -#endif [ExcludeFromCodeCoverage] internal static class MemberPolyfills_NetCore21_RandomNumberGenerator @@ -88,6 +29,8 @@ public static void Fill(Span data) } finally { + // Explicit cast needed for .NET Framework 3.5 where RandomNumberGenerator + // doesn't properly expose IDisposable for using statements ((IDisposable)rng).Dispose(); } } From 46d115510cb79ace454233b7621def1fe2a6d4a3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 15:57:53 +0000 Subject: [PATCH 11/12] Use arrow method for GetBytes, reorder Dispose methods Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PolyShim/NetCore10/RandomNumberGenerator.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/PolyShim/NetCore10/RandomNumberGenerator.cs b/PolyShim/NetCore10/RandomNumberGenerator.cs index 579fb37d..a788faa5 100644 --- a/PolyShim/NetCore10/RandomNumberGenerator.cs +++ b/PolyShim/NetCore10/RandomNumberGenerator.cs @@ -20,22 +20,19 @@ internal abstract class RandomNumberGenerator : IDisposable public abstract void GetBytes(byte[] data); + protected virtual void Dispose(bool disposing) { } + public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } - protected virtual void Dispose(bool disposing) { } - private sealed class RandomWrapper : RandomNumberGenerator { private readonly Random _random = new(); - public override void GetBytes(byte[] data) - { - _random.NextBytes(data); - } + public override void GetBytes(byte[] data) => _random.NextBytes(data); } } #endif From a0ca6691062e0151865900e46073f2cffdbf6893 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 16:08:21 +0000 Subject: [PATCH 12/12] Move buffer allocation inside try, add period to comment, fix signature script to detect abstract classes Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> --- PolyShim/List-Signatures.ps1 | 2 +- PolyShim/NetCore21/RandomNumberGenerator.cs | 4 ++-- PolyShim/Signatures.md | 7 +++++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/PolyShim/List-Signatures.ps1 b/PolyShim/List-Signatures.ps1 index d3edd323..5388d991 100644 --- a/PolyShim/List-Signatures.ps1 +++ b/PolyShim/List-Signatures.ps1 @@ -371,7 +371,7 @@ foreach ($file in $codeFiles) { } # Extract type declarations (must be internal) - $typePattern = 'internal\s+(?:(?:readonly|partial|static|sealed|ref)\s+)*(class|enum|struct|interface|record)\s+(\w+(?:<[^>]+>)?)' + $typePattern = 'internal\s+(?:(?:readonly|partial|static|sealed|abstract|ref)\s+)*(class|enum|struct|interface|record)\s+(\w+(?:<[^>]+>)?)' $typeMatches = [regex]::Matches($content, $typePattern) foreach ($match in $typeMatches) { diff --git a/PolyShim/NetCore21/RandomNumberGenerator.cs b/PolyShim/NetCore21/RandomNumberGenerator.cs index e335ab6f..bbce1b77 100644 --- a/PolyShim/NetCore21/RandomNumberGenerator.cs +++ b/PolyShim/NetCore21/RandomNumberGenerator.cs @@ -20,17 +20,17 @@ public static void Fill(Span data) if (data.Length == 0) return; - var buffer = new byte[data.Length]; var rng = RandomNumberGenerator.Create(); try { + var buffer = new byte[data.Length]; rng.GetBytes(buffer); buffer.CopyTo(data); } finally { // Explicit cast needed for .NET Framework 3.5 where RandomNumberGenerator - // doesn't properly expose IDisposable for using statements + // doesn't properly expose IDisposable for using statements. ((IDisposable)rng).Dispose(); } } diff --git a/PolyShim/Signatures.md b/PolyShim/Signatures.md index 272b74ad..4eee34af 100644 --- a/PolyShim/Signatures.md +++ b/PolyShim/Signatures.md @@ -1,7 +1,7 @@ # Signatures -- **Total:** 341 -- **Types:** 74 +- **Total:** 343 +- **Types:** 76 - **Members:** 267 ___ @@ -236,6 +236,8 @@ ___ - `OSPlatform` - [**[struct]**](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.osplatform) .NET Core 1.0 - [`OSPlatform FreeBSD`](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.osplatform.freebsd) .NET Core 3.0 +- `OSPlatformAttribute` + - [**[class]**](https://learn.microsoft.com/dotnet/api/system.runtime.versioning.osplatformattribute) .NET 5.0 - `OverloadResolutionPriorityAttribute` - [**[class]**](https://learn.microsoft.com/dotnet/api/system.runtime.compilerservices.overloadresolutionpriorityattribute) .NET 9.0 - `Parallel` @@ -276,6 +278,7 @@ ___ - [`void NextBytes(Span)`](https://learn.microsoft.com/dotnet/api/system.random.nextbytes#system-random-nextbytes(system-span((system-byte)))) .NET Core 2.1 - [`void Shuffle(T[])`](https://learn.microsoft.com/dotnet/api/system.random.shuffle#system-random-shuffle-1(-0())) .NET 8.0 - `RandomNumberGenerator` + - **[class]** .NET Core 1.0 - [`void Fill(Span)`](https://learn.microsoft.com/dotnet/api/system.security.cryptography.randomnumbergenerator.fill) .NET Core 2.1 - `Range` - [**[struct]**](https://learn.microsoft.com/dotnet/api/system.range) .NET Core 3.0