diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj b/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj index f3a2c373d1872..fa0c354fdbcbc 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj @@ -32,8 +32,7 @@ - + @@ -45,6 +44,7 @@ + diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs index a1c6830a77e4c..346c5e56796c3 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs @@ -1657,15 +1657,20 @@ public void CopyTo(Span destination) } /// - /// Sets the bytes in 'outBytes' to be random values. outBytes.Length must be less than or equal to 16 + /// Sets the bytes in 'outBytes' to be random values. outBytes.Length must be either 8 or 16 bytes. /// /// internal static unsafe void SetToRandomBytes(Span outBytes) { - Debug.Assert(outBytes.Length <= sizeof(Guid)); // Guid is 16 bytes, and so is TraceId - Guid guid = Guid.NewGuid(); - ReadOnlySpan guidBytes = new ReadOnlySpan(&guid, sizeof(Guid)); - guidBytes.Slice(0, outBytes.Length).CopyTo(outBytes); + Debug.Assert(outBytes.Length == 16 || outBytes.Length == 8); + RandomNumberGenerator r = RandomNumberGenerator.Current; + + Unsafe.WriteUnaligned(ref outBytes[0], r.Next()); + + if (outBytes.Length == 16) + { + Unsafe.WriteUnaligned(ref outBytes[8], r.Next()); + } } /// diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/RandomNumberGenerator.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/RandomNumberGenerator.cs new file mode 100755 index 0000000000000..dd151339b5cf4 --- /dev/null +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/RandomNumberGenerator.cs @@ -0,0 +1,72 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Diagnostics +{ + /// + /// RandomNumberGenerator implementation is the 64-bit random number generator based on the Xoshiro256StarStar algorithm (known as shift-register generators). + /// + internal class RandomNumberGenerator + { + [ThreadStatic] private static RandomNumberGenerator? t_random; + + private ulong _s0, _s1, _s2, _s3; + + public static RandomNumberGenerator Current + { + get + { + if (t_random == null) + { + t_random = new RandomNumberGenerator(); + } + return t_random; + } + } + +#if ALLOW_PARTIALLY_TRUSTED_CALLERS + [System.Security.SecuritySafeCriticalAttribute] +#endif + public unsafe RandomNumberGenerator() + { + do + { + Guid g1 = Guid.NewGuid(); + Guid g2 = Guid.NewGuid(); + ulong* g1p = (ulong*)&g1; + ulong* g2p = (ulong*)&g2; + _s0 = *g1p; + _s1 = *(g1p + 1); + _s2 = *g2p; + _s3 = *(g2p + 1); + + // Guid uses the 4 most significant bits of the first long as the version which would be fixed and not randomized. + // and uses 2 other bits in the second long for variants which would be fixed and not randomized too. + // let's overwrite the fixed bits in each long part by the other long. + _s0 = (_s0 & 0x0FFFFFFFFFFFFFFF) | (_s1 & 0xF000000000000000); + _s2 = (_s2 & 0x0FFFFFFFFFFFFFFF) | (_s3 & 0xF000000000000000); + _s1 = (_s1 & 0xFFFFFFFFFFFFFF3F) | (_s0 & 0x00000000000000C0); + _s3 = (_s3 & 0xFFFFFFFFFFFFFF3F) | (_s2 & 0x00000000000000C0); + } + while ((_s0 | _s1 | _s2 | _s3) == 0); + } + + private ulong Rol64(ulong x, int k) => (x << k) | (x >> (64 - k)); + + public long Next() + { + ulong result = Rol64(_s1 * 5, 7) * 9; + ulong t = _s1 << 17; + + _s2 ^= _s0; + _s3 ^= _s1; + _s1 ^= _s2; + _s0 ^= _s3; + + _s2 ^= t; + _s3 = Rol64(_s3, 45); + + return (long)result; + } + } +}