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 }