File: System\Security\Cryptography\RandomNumberGeneratorImplementation.cs
Web Access
Project: src\src\libraries\System.Security.Cryptography\src\System.Security.Cryptography.csproj (System.Security.Cryptography)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
namespace System.Security.Cryptography
{
    internal sealed partial class RandomNumberGeneratorImplementation : RandomNumberGenerator
    {
        // a singleton which always calls into a thread-safe implementation
        // and whose Dispose method no-ops
        internal static readonly RandomNumberGeneratorImplementation s_singleton = new RandomNumberGeneratorImplementation();
 
        // private ctor used only by singleton
        private RandomNumberGeneratorImplementation()
        {
        }
 
        // As long as each implementation can provide a static GetBytes(ref byte buf, int length)
        // they can share this one implementation of FillSpan.
        internal static unsafe void FillSpan(Span<byte> data)
        {
            if (data.Length > 0)
            {
                fixed (byte* ptr = data) GetBytes(ptr, data.Length);
            }
        }
 
        public override void GetBytes(byte[] data)
        {
            ArgumentNullException.ThrowIfNull(data);
 
            GetBytes(new Span<byte>(data));
        }
 
        public override void GetBytes(byte[] data, int offset, int count)
        {
            VerifyGetBytes(data, offset, count);
            GetBytes(new Span<byte>(data, offset, count));
        }
 
        public override unsafe void GetBytes(Span<byte> data)
        {
            if (data.Length > 0)
            {
                fixed (byte* ptr = data) GetBytes(ptr, data.Length);
            }
        }
 
        public override void GetNonZeroBytes(byte[] data)
        {
            ArgumentNullException.ThrowIfNull(data);
 
            FillNonZeroBytes(data);
        }
 
        public override void GetNonZeroBytes(Span<byte> data)
        {
            FillNonZeroBytes(data);
        }
 
        internal static void FillNonZeroBytes(Span<byte> data)
        {
            while (data.Length > 0)
            {
                // Fill the remaining portion of the span with random bytes.
                FillSpan(data);
 
                // Find the first zero in the remaining portion. If there isn't any, we're all done.
                int first0Byte = data.IndexOf((byte)0);
                if (first0Byte < 0)
                {
                    return;
                }
 
                // There's at least one zero.  Shift down all non-zeros.
                int zerosFound = 1;
                Span<byte> remainder = data.Slice(first0Byte + 1);
                while (true)
                {
                    // Find the next zero.
                    int next0Byte = remainder.IndexOf((byte)0);
                    if (next0Byte < 0)
                    {
                        // There weren't any more zeros. Copy the remaining valid data down.
                        remainder.CopyTo(data.Slice(first0Byte));
                        break;
                    }
 
                    // Copy down until the next zero, then reset to continue copying from there.
                    remainder.Slice(0, next0Byte).CopyTo(data.Slice(first0Byte));
                    zerosFound++;
                    first0Byte += next0Byte;
                    remainder = remainder.Slice(next0Byte + 1);
                }
 
                // Slice to any remaining space that needs to be filled. This is equal to the
                // number of zeros found.
                data = data.Slice(data.Length - zerosFound);
            }
        }
    }
}