1 /**
2 @nogc random numbers and UUID generation.
3 
4 Copyright:
5   Copyright (c) 2016, Guillaume Piolat.
6   Copyright (c) 2011, Johannes Pfau (std.uuid).
7   Copyright (c) 2008-2009, Andrei Alexandrescu (std.random)
8 
9 License:
10   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
11 */
12 module dplug.core.random;
13 
14 import std.random: Xorshift32, uniform;
15 import std.uuid;
16 
17 import dplug.core.nogc;
18 
19 // Work-around std.random not being @nogc
20 // - unpredictableSeed uses TLS
21 // - MonoTime.currTime.ticks sometimes fails on Mac
22 // => that leaves us only with the RDTSC instruction.
23 uint nogc_unpredictableSeed() @nogc nothrow
24 {
25     // assume we always have CPUID
26     uint result;
27     version(D_InlineAsm_X86)
28     {
29         asm nothrow @nogc
30         {
31             rdtsc;
32             mov result, EAX;
33         }
34 
35     }
36     else version(D_InlineAsm_X86_64)
37     {
38         asm nothrow @nogc
39         {
40             rdtsc;
41             mov result, EAX;
42         }
43     }
44     else version(LDC)
45     {
46         version(AArch64)
47         {
48             // llvm_readcyclecounter is forbidden as it reads a register we
49             // are not allowed to read.
50             import core.sys.posix.sys.time;
51             timeval tv;
52             gettimeofday(&tv, null);
53             result = cast(uint)( cast(ulong)(tv.tv_sec) * 1_000_000 + tv.tv_usec );
54         }
55         else
56         {
57             import ldc.intrinsics;
58             result = cast(uint) llvm_readcyclecounter();
59         }
60     }
61     else
62         static assert(false, "Unsupported");
63     return result;
64 }
65 
66 auto nogc_uniform_int(int min, int max, ref Xorshift32 rng) @nogc nothrow
67 {
68     return assumeNothrowNoGC( (int min, int max, ref Xorshift32 rng)
69                               {
70                                   return uniform(min, max, rng);
71                               } )(min, max, rng);
72 }
73 
74 auto nogc_uniform_float(float min, float max, ref Xorshift32 rng) @nogc nothrow
75 {
76     return assumeNothrowNoGC( (float min, float max, ref Xorshift32 rng)
77                               {
78                                   return uniform(min, max, rng);
79                               } )(min, max, rng);
80 }
81 
82 // The problem with the original rndGen is that it uses TLS, but without runtime TLS 
83 // is disallowed.
84 ref Xorshift32 defaultGlobalRNG() nothrow @nogc
85 {
86     __gshared static Xorshift32 globalRNG;
87     __gshared static bool initialized;
88     if (!initialized) // TODO: this is not thread-safe, use atomic CAS here
89     {
90         globalRNG = Xorshift32(nogc_unpredictableSeed());
91         initialized = true;
92     }
93     return globalRNG;
94 }
95 
96 static UUID generate(ref Xorshift32 randomGen) nothrow @nogc
97 {
98     UUID u;
99     uint* arr = cast(uint*)(u.data.ptr);
100     foreach(i; 0..4)
101     {
102         arr[i] = randomGen.front;
103         randomGen.popFront();
104     }
105 
106     //set variant
107     //must be 0b10xxxxxx
108     u.data[8] &= 0b10111111;
109     u.data[8] |= 0b10000000;
110 
111     //set version
112     //must be 0b0100xxxx
113     u.data[6] &= 0b01001111;
114     u.data[6] |= 0b01000000;
115 
116     return u;
117 }
118 
119 /// Generates a random UUID.
120 UUID generateRandomUUID() nothrow @nogc
121 {
122     UUID u = generate(defaultGlobalRNG());
123     return u;
124 }
125 
126 /// Generate a zero-terminated string with concatenated prefix and an UUDI.
127 /// Random UUID generation is often used to generate names like this.
128 /// Example: "MyPrefix_cb3b51b1-5c34-4412-b6b9-01193f1294b4\0"
129 void generateNullTerminatedRandomUUID(CharType)(CharType[] buffer, const(CharType)[] prefix) nothrow @nogc
130 {
131     assert(buffer.length >= 36 + prefix.length + 1);
132 
133     // Copy prefix
134     buffer[0..prefix.length] = prefix;
135 
136     // Generate an UUID string
137     char[36] uuidString;
138     UUID uuid = generateRandomUUID();
139     uuid.toString(uuidString[]);
140     
141     // Copy UUID
142     for(int i = 0; i < 36; ++i)
143         buffer[prefix.length + i] = cast(CharType)( uuidString[i] );
144 
145     // Add terminal zero
146     buffer[prefix.length + 36] = '\0';
147 }