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