1 module chloride.random;
2 
3 import chloride.core;
4 
5 import std.array : uninitializedArray;
6 import std.traits;
7 
8 import deimos.sodium.randombytes;
9 
10 
11 /**
12  * Allocate `n` bytes of cryptographic random data.
13  */
14 ubyte[] randomBytes(int n) {
15     ubyte[] buf = uninitializedArray!(ubyte[])(n);
16     fillRandom(buf);
17     return buf;
18 }
19 
20 /**
21  * Create an array of `n` elements filled with random data.
22  * The array can be static or dynamic. If the array is dynamic, the size
23  * must be passed as a runtime argument.
24  */
25 U randomArray(U: T[n], T, int n)() if (isScalarType!T) {
26     U data = void;
27     fillRandom(data);
28     return data;
29 }
30 
31 ///
32 U randomArray(U: T[], T)(int n) if (isScalarType!T) {
33     U data = uninitializedArray!(U)(n);
34     fillRandom(data);
35     return data;
36 }
37 
38 
39 /**
40  * Fill `buf` with cryptographic random data.
41  */
42 void fillRandom(void[] buf) {
43     randombytes_buf(buf.ptr, buf.length);
44 }
45 
46 /**
47  * Get a single random Integer of type T. T can be any
48  * integral type with four or fewer bytes.
49  */
50 T random(T)() if (isIntegral!T && (T.sizeof < 4)) {
51     uint bytes = random(Unsigned!T.max);
52     return cast(T) bytes;
53 }
54 
55 /// ditto
56 T random(T : uint = uint)() {
57     return randombytes_random();
58 }
59 
60 /**
61  * Get a random integer between 0 and n (excluded). It does its best
62  * to guarantee a uniform distribution.
63  */
64 uint random(uint n) {
65     return randombytes_uniform(n);
66 }
67 
68 
69 /**
70  * An input range that generates random bytes.
71  */
72 struct RandomByteRange {
73     private uint data;
74     private ubyte count;
75 
76     @property ubyte front() const pure {
77         return cast(ubyte) data;
78     }
79 
80     void popFront() {
81         count++;
82         if (count < 4) {
83             data >>= 8;
84         } else {
85             count = 0;
86             data = random();
87         }
88     }
89 
90     enum empty = false;
91 }
92 
93 /**
94  * Create an instance of RandomByteRange
95  */
96 RandomByteRange randomByteRange() {
97     return RandomByteRange(random(), 0);
98 }