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 }