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 }