@@ -16,7 +16,7 @@ namespace Fast.PRNGs;
1616/// This uses vectorization from the AVX2 instructions
1717/// </summary>
1818[ StructLayout ( LayoutKind . Sequential ) ]
19- unsafe public struct Shishua : IDisposable
19+ public readonly struct Shishua : IDisposable
2020{
2121 private static Span < ulong > Phi => new ulong [ ]
2222 {
@@ -25,39 +25,65 @@ unsafe public struct Shishua : IDisposable
2525 0xF06AD7AE9717877E , 0x85839D6EFFBD7DC6 , 0x64D325D1C5371682 , 0xCADD0CCCFDFFBBE1 ,
2626 0x626E33B8D04B4331 , 0xBBF73C790D94F79D , 0x471C4AB3ED3D82A5 , 0xFEC507705E4AE6E5 ,
2727 } ;
28+
2829 private const int BufferSize = 1 << 17 ;
2930
30- private readonly void * _state ;
31+ private readonly nuint _state ;
32+
33+ public static bool IsSupported => Avx2 . IsSupported ;
3134
32- private ref BufferedState State
35+ unsafe private ref BufferedState State
3336 {
3437 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
3538 get
3639 {
37- return ref Unsafe . AsRef < BufferedState > ( _state ) ;
40+ return ref Unsafe . AsRef < BufferedState > ( _state . ToPointer ( ) ) ;
3841 }
3942 }
4043
41- private Shishua ( Random ? seedGenerator = null )
44+ private Shishua ( ref Seed seed )
45+ {
46+ _state = AllocateState ( ref seed ) ;
47+ }
48+
49+ public static Shishua Create ( )
50+ {
51+ ThrowIfNotSupported ( ) ;
52+
53+ Seed seed = default ;
54+ var seedGenerator = Random . Shared ;
55+ seedGenerator . NextBytes ( MemoryMarshal . AsBytes ( seed . Span ) ) ;
56+ return new Shishua ( ref seed ) ;
57+ }
58+
59+ public static Shishua Create ( Random seedGenerator )
4260 {
61+ ThrowIfNotSupported ( ) ;
62+
4363 Seed seed = default ;
44- seedGenerator ??= Random . Shared ;
4564 seedGenerator . NextBytes ( MemoryMarshal . AsBytes ( seed . Span ) ) ;
65+ return new Shishua ( ref seed ) ;
66+ }
67+
68+ public static Shishua Create ( ReadOnlySpan < byte > seedBytes )
69+ {
70+ ThrowIfNotSupported ( ) ;
71+
72+ if ( seedBytes . Length != 32 )
73+ throw new ArgumentException ( "Seed bytes should be of length 32, got: " + seedBytes . Length ) ;
4674
47- _state = AllocateState ( seed ) ;
75+ Seed seed = default ;
76+ seedBytes . CopyTo ( MemoryMarshal . AsBytes ( seed . Span ) ) ;
77+ return new Shishua ( ref seed ) ;
4878 }
4979
50- static Shishua ( )
80+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
81+ private static void ThrowIfNotSupported ( )
5182 {
5283 if ( ! Avx2 . IsSupported )
53- {
54- throw new InvalidProgramException ( "Need access to AVX2 instruction to run this module" ) ;
55- }
84+ throw new InvalidOperationException ( "Need access to AVX2 instruction to run the Shishua PRNG" ) ;
5685 }
5786
58- public static Shishua Create ( Random ? seedGenerator = null ) =>
59- new Shishua ( seedGenerator ) ;
60-
6187 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
6288 private ulong NextInternal ( )
6389 {
@@ -97,17 +123,17 @@ public void Dispose()
97123 FreeState ( ) ;
98124 }
99125
100- private void * AllocateState ( Seed seed )
126+ unsafe private nuint AllocateState ( ref Seed seed )
101127 {
102- var ptr = NativeMemory . AlignedAlloc ( ( nuint ) Marshal . SizeOf < BufferedState > ( ) , 128 ) ;
128+ void * ptr = NativeMemory . AlignedAlloc ( ( nuint ) Marshal . SizeOf < BufferedState > ( ) , 128 ) ;
103129 ref BufferedState bufferedState = ref Unsafe . AsRef < BufferedState > ( ptr ) ;
104130
105- InitState ( ref bufferedState . State , seed ) ;
131+ InitState ( ref bufferedState . State , ref seed ) ;
106132 FillBuffer ( ref bufferedState ) ;
107- return ptr ;
133+ return ( nuint ) ptr ;
108134 }
109135
110- private void InitState ( ref RawState state , Seed seed )
136+ private void InitState ( ref RawState state , ref Seed seed )
111137 {
112138 state = default ;
113139
@@ -123,20 +149,22 @@ private void InitState(ref RawState state, Seed seed)
123149
124150 for ( int i = 0 ; i < rounds ; i ++ )
125151 {
126- PrngGen ( ref state , buf , 128 * steps ) ;
152+ PrngGen ( ref state , buf ) ;
127153 state . State [ 0 ] = state . Output [ 3 ] ; state . State [ 1 ] = state . Output [ 2 ] ;
128154 state . State [ 2 ] = state . Output [ 1 ] ; state . State [ 3 ] = state . Output [ 0 ] ;
129155 }
130156 }
131157
132158 private void FillBuffer ( ref BufferedState bufferedState )
133159 {
134- PrngGen ( ref bufferedState . State , bufferedState . Buffer , BufferSize ) ;
160+ PrngGen ( ref bufferedState . State , bufferedState . Buffer ) ;
135161 bufferedState . BufferIndex = 0 ;
136162 }
137163
138- private void PrngGen ( ref RawState state , Span < byte > buffer , nint size )
164+ unsafe private void PrngGen ( ref RawState state , Span < byte > buffer )
139165 {
166+ var size = buffer . Length ;
167+
140168 __m256i
141169 o0 = state . Output [ 0 ] , o1 = state . Output [ 1 ] ,
142170 o2 = state . Output [ 2 ] , o3 = state . Output [ 3 ] ,
@@ -184,13 +212,13 @@ private void PrngGen(ref RawState state, Span<byte> buffer, nint size)
184212 state . Counter = counter ;
185213 }
186214
187- private void FreeState ( )
215+ unsafe private void FreeState ( )
188216 {
189- NativeMemory . AlignedFree ( _state ) ;
217+ NativeMemory . AlignedFree ( _state . ToPointer ( ) ) ;
190218 }
191219
192220 [ StructLayout ( LayoutKind . Sequential ) ]
193- private struct BufferedState
221+ unsafe private struct BufferedState
194222 {
195223 public RawState State ;
196224 private fixed byte _buffer [ BufferSize ] ;
@@ -219,7 +247,7 @@ private struct RawState
219247 }
220248
221249 [ StructLayout ( LayoutKind . Sequential ) ]
222- private struct Seed
250+ unsafe private struct Seed
223251 {
224252 private fixed ulong _value [ 4 ] ;
225253
0 commit comments