1 // Written in the D programming language.
2 /**
3  * Provides access to non-deterministic sources of randomness.  These
4  * are implemented as Input Ranges and in many cases are architecture-
5  * or OS-dependent.
6  *
7  * As with the pseudo-random number generators provided elsewhere in
8  * this package, all random devices are implemented as final classes
9  * to ensure reference semantics.
10  *
11  * Warning: This module is currently experimental and should be used
12  * with caution.  It is not imported automatically as part of the
13  * hap.random package but must be imported individually in its own
14  * right.  Its API may change in future releases.
15  *
16  * Copyright: © 2014 Joseph Rushton Wakeling
17  *
18  * License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
19  *
20  * Authors: $(WEB braingam.es, Joseph Rushton Wakeling)
21  *
22  * Source: $(HAPSRC hap/random/_device.d)
23  */
24 module hap.random.device;
25 
26 import hap.random.traits;
27 
28 import std.range, std.traits, std.typetuple;
29 
30 /**
31  * $(D TypeTuple) of all random devices defined in this module.
32  * that act as a uniform random number generator.  Note that the
33  * available random devices may be dependent on operating system
34  * and/or hardware architecture.
35  */
36 version (Posix)
37 {
38     pragma(msg, "Module hap.random.device is experimental.  Use with caution.");
39     alias UniformRandomDeviceTypes =
40         TypeTuple!(DevRandom!ushort, DevRandom!uint, DevRandom!ulong,
41                    DevURandom!ushort, DevURandom!uint, DevURandom!ulong);
42 }
43 else
44 {
45     pragma(msg, "Module hap.random.device is not currently supported on your operating system.");
46     alias UniformRandomDeviceTypes =
47         TypeTuple!();
48 }
49 
50 unittest
51 {
52     foreach (Dev; UniformRandomDeviceTypes)
53     {
54         static assert (isUniformRNG!Dev);
55 
56         auto rnd = new Dev;
57 
58         auto init = rnd.front;
59         size_t i = 50;
60         do
61         {
62             rnd.popFront();
63         }
64         while (--i && rnd.front == init);
65 
66         assert(i > 0);
67         assert(i < 50);
68     }
69 }
70 
71 /**
72  * Reads randomness from an infinite filestream.  In practice this
73  * will typically be used to read from system sources of randomness
74  * such as (on Posix) $(D /dev/random) or $(D /dev/urandom).
75  *
76  * The random numbers generated will be unsigned integers of type
77  * $(D T) in the range [$(D T.min), $(D T.max)].  A good source of
78  * randomness should ensure these are uniformly distributed.
79  *
80  * It is the responsibility of the user to ensure that the specified
81  * source of randomness indeed contains sufficient data to serve the
82  * requirements of their program.
83  */
84 final class RandomFileStream(string filename, T)
85     if (isIntegral!T && isUnsigned!T)
86 {
87   private:
88     import std.stdio;
89     File _dev;
90     T _value;
91 
92   public:
93     alias source = filename;
94     enum T min = T.min;
95     enum T max = T.max;
96 
97     enum bool isUniformRandom = true;
98 
99     static assert (this.min == 0);
100 
101     this()
102     {
103         _dev = File(filename, "r");
104         popFront();
105     }
106 
107     /// Range primitives
108     enum bool empty = false;
109 
110     /// ditto
111     T front() @property @safe const nothrow pure
112     {
113         return _value;
114     }
115 
116     /// ditto
117     void popFront()
118     {
119         _dev.rawRead((&_value)[0 .. 1]);
120     }
121 }
122 
123 version (Posix)
124 {
125     /**
126      * Generates uniformly distributed random numbers in the interval
127      * [$(D T.min), $(D T.max)] using $(D /dev/random) as the source
128      * of randomness.
129      *
130      * Caution should be taken when using this as $(D /dev/random) is
131      * a blocking device.
132      */
133     template DevRandom(T)
134         if (isIntegral!T && isUnsigned!T)
135     {
136         alias DevRandom = RandomFileStream!("/dev/random", T);
137     }
138 
139     /**
140      * Generates uniformly distributed random numbers in the interval
141      * [$(D T.min), $(D T.max)] using $(D /dev/urandom) as the source
142      * of randomness.
143      */
144     template DevURandom(T)
145         if (isIntegral!T && isUnsigned!T)
146     {
147         alias DevURandom = RandomFileStream!("/dev/urandom", T);
148     }
149 }