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 }