Skip to content

Implement Shuffle, GetString, and GetItems on Random{NumberGenerator} #78598

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Jan 12, 2023

Conversation

vcsjones
Copy link
Member

Implements:

  • Random.GetItems
  • Random.Shuffle
  • RandomNumberGenerator.GetString
  • RandomNumberGenerator.GetHexString
  • RandomNumberGenerator.Shuffle

This does both RandomNumberGenerator and Random in one go. If folks prefer I can split the PR.

Closes #73864.

@ghost
Copy link

ghost commented Nov 19, 2022

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, to please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

@ghost
Copy link

ghost commented Nov 19, 2022

Tagging subscribers to this area: @dotnet/area-system-security, @vcsjones
See info in area-owners.md if you want to be subscribed.

Issue Details

Implements:

  • Random.GetItems
  • Random.Shuffle
  • RandomNumberGenerator.GetString
  • RandomNumberGenerator.GetHexString
  • RandomNumberGenerator.Shuffle

This does both RandomNumberGenerator and Random in one go. If folks prefer I can split the PR.

Closes #73864.

Author: vcsjones
Assignees: vcsjones
Labels:

area-System.Security

Milestone: -

@stephentoub
Copy link
Member

Can you search around the repo and use these where it makes sense, e.g.


public static void Shuffle<T>(this Random random, T[] array)

etc?

@stephentoub
Copy link
Member

Use where possible

A quick search suggests other opportunities, e.g.

d:\repos\runtime\src\libraries\System.Net.Sockets\tests\FunctionalTests\UnixDomainSocketTest.cs(545):result = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + new string('A', Random.Shared.Next(1, 32)));
d:\repos\runtime\src\libraries\System.Net.WebClient\tests\WebClientTest.cs(664):new string(Enumerable.Range(0, 512 * 1024).Select(_ => (char)('a' + Random.Shared.Next(0, 26))).ToArray());
d:\repos\runtime\src\libraries\Common\tests\System\Net\Http\PostScenarioTest.cs(151):var rand = new Random(42);
d:\repos\runtime\src\libraries\Common\tests\Tests\System\StringTests.cs(3776):Random rand = new Random(42);
d:\repos\runtime\src\libraries\Common\tests\Tests\System\StringTests.cs(3810):Random rand = new Random(42);
d:\repos\runtime\src\libraries\Common\tests\Tests\System\StringTests.cs(3832):Random rand = new Random(42);
d:\repos\runtime\src\libraries\Common\tests\TestUtilities\System\IO\FileCleanupTestBase.cs(178):Random random = new Random();
d:\repos\runtime\src\libraries\Common\tests\TestUtilities\System\IO\PathGenerator.cs(11):private static Random _rand = new Random();
d:\repos\runtime\src\libraries\Common\tests\TestUtilities\RandomTestCaseOrderer.cs(59):static List<T> Randomize(List<T> tests)
d:\repos\runtime\src\libraries\Common\tests\System\Net\Http\PostScenarioTest.cs(155):sb.Append((char)(rand.Next(0, 26) + 'a'));
d:\repos\runtime\src\libraries\Common\tests\TestUtilities\System\IO\FileCleanupTestBase.cs(181):sb.Append((char)('a' + random.Next(0, 26)));
d:\repos\runtime\src\libraries\System.IO\tests\StreamReader\StreamReaderTests.cs(407):data[i] = (char)('a' + r.Next(0, 26));
d:\repos\runtime\src\libraries\System.IO\tests\StreamReader\StreamReaderTests.cs(438):data[i] = (char)('a' + r.Next(0, 26));
d:\repos\runtime\src\libraries\System.IO\tests\StreamReader\StreamReaderTests.cs(469):data[i] = (char)('a' + r.Next(0, 26));
d:\repos\runtime\src\libraries\System.IO\tests\StreamReader\StreamReaderTests.cs(500):data[i] = (char)('a' + r.Next(0, 26));
d:\repos\runtime\src\libraries\System.IO\tests\StreamWriter\StreamWriter.WriteTests.cs(257):data[i] = (char)(rand.Next(0, 26) + 'a');
d:\repos\runtime\src\libraries\System.IO\tests\StreamWriter\StreamWriter.WriteTests.cs(290):data[i] = (char)(rand.Next(0, 26) + 'a');
d:\repos\runtime\src\libraries\System.IO\tests\StreamWriter\StreamWriter.WriteTests.cs(323):data[i] = (char)(rand.Next(0, 26) + 'a');
d:\repos\runtime\src\libraries\System.IO\tests\StreamWriter\StreamWriter.WriteTests.cs(356):data[i] = (char)(rand.Next(0, 26) + 'a');
d:\repos\runtime\src\libraries\System.Net.HttpListener\tests\HttpRequestStreamTests.cs(234):.Select(_ => (byte)('a' + rand.Next(0, 26)))
d:\repos\runtime\src\libraries\System.Net.HttpListener\tests\HttpRequestStreamTests.cs(274):.Select(_ => (byte)('a' + rand.Next(0, 26)))
d:\repos\runtime\src\libraries\System.Net.WebClient\tests\WebClientTest.cs(664):new string(Enumerable.Range(0, 512 * 1024).Select(_ => (char)('a' + Random.Shared.Next(0, 26))).ToArray());

@vcsjones
Copy link
Member Author

vcsjones commented Nov 20, 2022

A quick search suggests other opportunities, e.g.

I excluded searching in /tests/, but if we want to, that's fine. I'm generally risk-adverse touching tests purely for refactoring / using new APIs, but this seems simple enough.

@vcsjones
Copy link
Member Author

A quick search suggests other opportunities, e.g.

I checked in a few more. Several of them I left unchanged because they have configurations that build for NetCoreAppMinimum or NetFramework. It didn't make sense to #ifdef in tests to me.

Co-authored-by: Benjamin Moir <[email protected]>
Co-authored-by: Theodore Tsirpanis <[email protected]>
Co-authored-by: Michał Petryka <[email protected]>
@vcsjones
Copy link
Member Author

Sorry for the force push. I goofed on Co-authored-by and did not want to leave Michał out.

@stephentoub
Copy link
Member

@vcsjones, when you get a chance, could you take a look at the pending comments/questions? Then we can get this merged. Thanks.

for (int i = 0; i < destination.Length; i++)
{
destination[i] = choices[GetInt32(choices.Length)];
}
Copy link
Member

@bartonjs bartonjs Jan 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the N virtual calls concern in System.Random, it might be useful to change this to do something like

Span<byte> randomBytes = stackalloc byte[128];
Span<int> randomIntStorage = stackalloc int[randomBytes.Length / sizeof(int)];
int randomInts = 0;

for (int i = destination.Length - 1; i >= 0; i--)
{
    while (randomInts <= 0)
    {
        if (i < randomIntStorage.Length)
        {
            randomBytes = randomBytes.Slice(0, i * sizeof(int));
        }

        Fill(randomBytes);
        randomInts = FilterInt32(randomBytes, randomIntStorage);
    }

    randomInts--;
    destination[i] = choices[randomInts[randomInts]];
}

to reduce P/Invokes by a factor of randomIntStorage.Length.

Though that requires writing FilterInt32, and the complexity of above, so it should be checked to see if the perf justifies it.

(This is an idea for later, not a "right now")

…/Cryptography/RandomNumberGenerator.cs

Co-authored-by: Jeremy Barton <[email protected]>
@davidfowl
Copy link
Member

Very nice APIs! I wonder if we should have T PickRandom(this IReadOnlyList<T> source)

@stephentoub
Copy link
Member

stephentoub commented Jan 13, 2023

Very nice APIs! I wonder if we should have T PickRandom(this IReadOnlyList source)

Concept seems reasonable, though to nit pick, to match the other APIs we'd probably want it to be called GetItem rather than PickRandom. Can you open a separate issue? Are there places it would be used in ASP.NET? I assume this is essentially shorthand for: source[RandomNumberGenerator.GetInt32(source.Count)]?

@davidfowl
Copy link
Member

davidfowl commented Jan 13, 2023

Can you open a separate issue?

I can.

Are there places it would be used in ASP.NET?

Not sure, I was building something with YARP when this came up.

Also would be used here

I assume this is essentially shorthand for: source[RandomNumberGenerator.GetInt32(source.Count)]?

Yep!

@davidfowl
Copy link
Member

Filed an issue here

@ghost ghost locked as resolved and limited conversation to collaborators Feb 13, 2023
@bartonjs bartonjs added cryptographic-docs-impact Issues impacting cryptographic docs. Cleared and reused after documentation is updated each release. tracking This issue is tracking the completion of other related issues. labels Jul 29, 2024
@bartonjs bartonjs removed tracking This issue is tracking the completion of other related issues. cryptographic-docs-impact Issues impacting cryptographic docs. Cleared and reused after documentation is updated each release. labels Aug 15, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[API Proposal]: RandomDataGenerator
10 participants