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