Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 26 additions & 11 deletions PolyShim/Net100/Random.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// ReSharper disable PartialTypeWithSinglePart

using System;
using System.Text;
using System.Buffers;
using System.Diagnostics.CodeAnalysis;

[ExcludeFromCodeCoverage]
Expand All @@ -17,25 +17,40 @@ internal static class MemberPolyfills_Net100_Random
// https://learn.microsoft.com/dotnet/api/system.random.gethexstring#system-random-gethexstring(system-int32-system-boolean)
public string GetHexString(int stringLength, bool lowercase = false)
{
var bytes = new byte[(stringLength + 1) / 2];
random.NextBytes(bytes);
var byteCount = (stringLength + 1) / 2;
var bytes = ArrayPool<byte>.Shared.Rent(byteCount);

var hex = lowercase ? Convert.ToHexStringLower(bytes) : Convert.ToHexString(bytes);
return hex.Substring(0, stringLength);
try
{
random.NextBytes(bytes.AsSpan(0, byteCount));

var hex = lowercase
? Convert.ToHexStringLower(bytes, 0, byteCount)
: Convert.ToHexString(bytes, 0, byteCount);

return hex.Substring(0, stringLength);
}
finally
{
ArrayPool<byte>.Shared.Return(bytes);
}
}

// https://learn.microsoft.com/dotnet/api/system.random.getstring
public string GetString(ReadOnlySpan<char> choices, int length)
{
var buffer = new StringBuilder(length);
var chars = ArrayPool<char>.Shared.Rent(length);
try
{
for (var i = 0; i < length; i++)
chars[i] = choices[random.Next(choices.Length)];

for (var i = 0; i < length; i++)
return new string(chars, 0, length);
}
finally
{
var index = random.Next(choices.Length);
buffer.Append(choices[index]);
ArrayPool<char>.Shared.Return(chars);
}

return buffer.ToString();
}
}
}
Expand Down
38 changes: 26 additions & 12 deletions PolyShim/Net60/Random.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// ReSharper disable PartialTypeWithSinglePart

using System;
using System.Buffers;
using System.Diagnostics.CodeAnalysis;

file static class RandomEx
Expand All @@ -32,15 +33,22 @@ public long NextInt64(long minValue, long maxValue)

var range = (ulong)(maxValue - minValue);

ulong ulongRand;
do
var buffer = ArrayPool<byte>.Shared.Rent(8);
try
{
var buffer = new byte[8];
random.NextBytes(buffer);
ulongRand = BitConverter.ToUInt64(buffer, 0);
} while (ulongRand > ulong.MaxValue - (ulong.MaxValue % range + 1) % range);
ulong ulongRand;
do
{
random.NextBytes(buffer.AsSpan(0, 8));
ulongRand = BitConverter.ToUInt64(buffer, 0);
} while (ulongRand > ulong.MaxValue - (ulong.MaxValue % range + 1) % range);

return (long)(ulongRand % range) + minValue;
return (long)(ulongRand % range) + minValue;
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
}

// https://learn.microsoft.com/dotnet/api/system.random.nextint64#system-random-nextint64(system-int64)
Expand All @@ -52,11 +60,17 @@ public long NextInt64(long minValue, long maxValue)
// https://learn.microsoft.com/dotnet/api/system.random.nextsingle
public float NextSingle()
{
var buffer = new byte[4];
random.NextBytes(buffer);
var uintValue = BitConverter.ToUInt32(buffer, 0);

return (uintValue >> 8) * (1.0f / (1u << 24));
var buffer = ArrayPool<byte>.Shared.Rent(4);
try
{
random.NextBytes(buffer.AsSpan(0, 4));
var uintValue = BitConverter.ToUInt32(buffer, 0);
return (uintValue >> 8) * (1.0f / (1u << 24));
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
}

// https://learn.microsoft.com/dotnet/api/system.random.shared
Expand Down
17 changes: 10 additions & 7 deletions PolyShim/Net80/RandomNumberGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

using System;
using System.Buffers;
using System.Text;
using System.Diagnostics.CodeAnalysis;
using System.Security.Cryptography;

Expand Down Expand Up @@ -56,6 +55,7 @@ public static string GetHexString(int stringLength, bool lowercase = false)
var hex = lowercase
? Convert.ToHexStringLower(bytes, 0, byteCount)
: Convert.ToHexString(bytes, 0, byteCount);

return hex.Substring(0, stringLength);
}
finally
Expand All @@ -67,15 +67,18 @@ public static string GetHexString(int stringLength, bool lowercase = false)
// https://learn.microsoft.com/dotnet/api/system.security.cryptography.randomnumbergenerator.getstring
public static string GetString(ReadOnlySpan<char> choices, int length)
{
var buffer = new StringBuilder(length);
var chars = ArrayPool<char>.Shared.Rent(length);
try
{
for (var i = 0; i < length; i++)
chars[i] = choices[RandomNumberGenerator.GetInt32(choices.Length)];

for (var i = 0; i < length; i++)
return new string(chars, 0, length);
}
finally
{
var index = RandomNumberGenerator.GetInt32(choices.Length);
buffer.Append(choices[index]);
ArrayPool<char>.Shared.Return(chars, clearArray: true);
}

return buffer.ToString();
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion PolyShim/NetCore21/Random.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ internal static class MemberPolyfills_NetCore21_Random
// https://learn.microsoft.com/dotnet/api/system.random.nextbytes#system-random-nextbytes(system-span((system-byte)))
public void NextBytes(Span<byte> buffer)
{
var bufferArray = buffer.ToArray();
var bufferArray = new byte[buffer.Length];
random.NextBytes(bufferArray);
bufferArray.CopyTo(buffer);
}
Expand Down
31 changes: 17 additions & 14 deletions PolyShim/NetCore21/RandomNumberGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,15 @@
// ReSharper disable PartialTypeWithSinglePart

using System;
using System.Buffers;
using System.Diagnostics.CodeAnalysis;
using System.Security.Cryptography;

file static class RandomNumberGeneratorEx
{
public static RandomNumberGenerator Instance { get; } = RandomNumberGenerator.Create();
}

[ExcludeFromCodeCoverage]
internal static class MemberPolyfills_NetCore21_RandomNumberGenerator
{
Expand All @@ -20,9 +26,16 @@ public void GetBytes(Span<byte> data)
if (data.Length == 0)
return;

var buffer = new byte[data.Length];
rng.GetBytes(buffer);
buffer.CopyTo(data);
var buffer = ArrayPool<byte>.Shared.Rent(data.Length);
try
{
rng.GetBytes(buffer, 0, data.Length);
buffer.AsSpan(0, data.Length).CopyTo(data);
}
finally
{
ArrayPool<byte>.Shared.Return(buffer, clearArray: true);
}
}

// https://learn.microsoft.com/dotnet/api/system.security.cryptography.randomnumbergenerator.getnonzerobytes#system-security-cryptography-randomnumbergenerator-getnonzerobytes(system-span((system-byte)))
Expand All @@ -43,17 +56,7 @@ public static void Fill(Span<byte> data)
if (data.Length == 0)
return;

var rng = RandomNumberGenerator.Create();
try
{
rng.GetBytes(data);
}
finally
{
// Explicit cast needed for .NET Framework 3.5 where RandomNumberGenerator
// doesn't properly expose IDisposable for using statements.
((IDisposable)rng).Dispose();
}
RandomNumberGeneratorEx.Instance.GetBytes(data);
}
}
}
Expand Down
24 changes: 15 additions & 9 deletions PolyShim/NetCore30/RandomNumberGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,15 @@
// ReSharper disable PartialTypeWithSinglePart

using System;
using System.Buffers;
using System.Diagnostics.CodeAnalysis;
using System.Security.Cryptography;

file static class RandomNumberGeneratorEx
{
public static RandomNumberGenerator Instance { get; } = RandomNumberGenerator.Create();
}

[ExcludeFromCodeCoverage]
internal static class MemberPolyfills_NetCore30_RandomNumberGenerator
{
Expand All @@ -18,27 +24,27 @@ internal static class MemberPolyfills_NetCore30_RandomNumberGenerator
public static int GetInt32(int fromInclusive, int toExclusive)
{
var range = (uint)(toExclusive - fromInclusive);
var rng = RandomNumberGenerator.Create();

// Reject values that would cause bias in the distribution.
// This ensures uniform distribution by rejecting values in the
// incomplete final bucket.
var rejectionThreshold = uint.MaxValue - (uint.MaxValue % range + 1) % range;

var buffer = ArrayPool<byte>.Shared.Rent(4);
try
{
// Reject values that would cause bias in the distribution.
// This ensures uniform distribution by rejecting values in the
// incomplete final bucket.
var rejectionThreshold = uint.MaxValue - (uint.MaxValue % range + 1) % range;

uint result;
do
{
var buffer = new byte[4];
rng.GetBytes(buffer);
RandomNumberGeneratorEx.Instance.GetBytes(buffer, 0, 4);
result = BitConverter.ToUInt32(buffer, 0);
} while (result > rejectionThreshold);

return (int)(result % range) + fromInclusive;
}
finally
{
((IDisposable)rng).Dispose();
ArrayPool<byte>.Shared.Return(buffer, clearArray: true);
}
}

Expand Down
Loading