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