]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | /* | |
4 | * Ceph - scalable distributed file system | |
5 | * | |
6 | * Copyright (C) 2017 SUSE LINUX GmbH | |
7 | * | |
8 | * This is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Lesser General Public | |
10 | * License version 2.1, as published by the Free Software | |
11 | * Foundation. See file COPYING. | |
12 | * | |
13 | */ | |
14 | ||
15 | #ifndef CEPH_RANDOM_H | |
16 | #define CEPH_RANDOM_H 1 | |
17 | ||
18 | #include <mutex> | |
19 | #include <random> | |
20 | #include <type_traits> | |
21 | #include <boost/optional.hpp> | |
22 | ||
f67539c2 TL |
23 | // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85494 |
24 | #ifdef __MINGW32__ | |
25 | #include <boost/random/random_device.hpp> | |
26 | ||
27 | using random_device_t = boost::random::random_device; | |
28 | #else | |
29 | using random_device_t = std::random_device; | |
30 | #endif | |
31 | ||
9f95a23c | 32 | // Basic random number facility (see N3551 for inspiration): |
11fdf7f2 TL |
33 | namespace ceph::util { |
34 | ||
9f95a23c | 35 | inline namespace version_1_0_3 { |
11fdf7f2 TL |
36 | |
37 | namespace detail { | |
38 | ||
39 | template <typename T0, typename T1> | |
40 | using larger_of = typename std::conditional< | |
41 | sizeof(T0) >= sizeof(T1), | |
42 | T0, T1> | |
43 | ::type; | |
44 | ||
45 | // avoid mixing floating point and integers: | |
46 | template <typename NumberT0, typename NumberT1> | |
47 | using has_compatible_numeric_types = | |
48 | std::disjunction< | |
49 | std::conjunction< | |
50 | std::is_floating_point<NumberT0>, std::is_floating_point<NumberT1> | |
51 | >, | |
52 | std::conjunction< | |
53 | std::is_integral<NumberT0>, std::is_integral<NumberT1> | |
54 | > | |
55 | >; | |
56 | ||
57 | ||
58 | // Select the larger of type compatible numeric types: | |
59 | template <typename NumberT0, typename NumberT1> | |
60 | using select_number_t = std::enable_if_t<detail::has_compatible_numeric_types<NumberT0, NumberT1>::value, | |
61 | detail::larger_of<NumberT0, NumberT1>>; | |
62 | ||
63 | } // namespace detail | |
64 | ||
65 | namespace detail { | |
66 | ||
67 | // Choose default distribution for appropriate types: | |
68 | template <typename NumberT, | |
69 | bool IsIntegral> | |
70 | struct select_distribution | |
71 | { | |
72 | using type = std::uniform_int_distribution<NumberT>; | |
73 | }; | |
74 | ||
75 | template <typename NumberT> | |
76 | struct select_distribution<NumberT, false> | |
77 | { | |
78 | using type = std::uniform_real_distribution<NumberT>; | |
79 | }; | |
80 | ||
81 | template <typename NumberT> | |
82 | using default_distribution = typename | |
83 | select_distribution<NumberT, std::is_integral<NumberT>::value>::type; | |
84 | ||
85 | } // namespace detail | |
86 | ||
87 | namespace detail { | |
88 | ||
89 | template <typename EngineT> | |
90 | EngineT& engine(); | |
91 | ||
92 | template <typename MutexT, typename EngineT, | |
93 | typename SeedT = typename EngineT::result_type> | |
94 | void randomize_rng(const SeedT seed, MutexT& m, EngineT& e) | |
95 | { | |
96 | std::lock_guard<MutexT> lg(m); | |
97 | e.seed(seed); | |
98 | } | |
99 | ||
100 | template <typename MutexT, typename EngineT> | |
101 | void randomize_rng(MutexT& m, EngineT& e) | |
102 | { | |
f67539c2 | 103 | random_device_t rd; |
11fdf7f2 TL |
104 | |
105 | std::lock_guard<MutexT> lg(m); | |
106 | e.seed(rd()); | |
107 | } | |
108 | ||
109 | template <typename EngineT = std::default_random_engine, | |
110 | typename SeedT = typename EngineT::result_type> | |
111 | void randomize_rng(const SeedT n) | |
112 | { | |
113 | detail::engine<EngineT>().seed(n); | |
114 | } | |
115 | ||
116 | template <typename EngineT = std::default_random_engine> | |
117 | void randomize_rng() | |
118 | { | |
f67539c2 | 119 | random_device_t rd; |
11fdf7f2 TL |
120 | detail::engine<EngineT>().seed(rd()); |
121 | } | |
122 | ||
123 | template <typename EngineT> | |
124 | EngineT& engine() | |
125 | { | |
126 | thread_local boost::optional<EngineT> rng_engine; | |
127 | ||
128 | if (!rng_engine) { | |
129 | rng_engine.emplace(EngineT()); | |
130 | randomize_rng<EngineT>(); | |
131 | } | |
132 | ||
133 | return *rng_engine; | |
134 | } | |
135 | ||
136 | } // namespace detail | |
137 | ||
138 | namespace detail { | |
139 | ||
140 | template <typename NumberT, | |
141 | typename DistributionT = detail::default_distribution<NumberT>, | |
142 | typename EngineT> | |
143 | NumberT generate_random_number(const NumberT min, const NumberT max, | |
144 | EngineT& e) | |
145 | { | |
146 | DistributionT d { min, max }; | |
147 | ||
148 | using param_type = typename DistributionT::param_type; | |
149 | return d(e, param_type { min, max }); | |
150 | } | |
151 | ||
152 | template <typename NumberT, | |
153 | typename MutexT, | |
154 | typename DistributionT = detail::default_distribution<NumberT>, | |
155 | typename EngineT> | |
156 | NumberT generate_random_number(const NumberT min, const NumberT max, | |
157 | MutexT& m, EngineT& e) | |
158 | { | |
159 | DistributionT d { min, max }; | |
160 | ||
161 | using param_type = typename DistributionT::param_type; | |
162 | ||
163 | std::lock_guard<MutexT> lg(m); | |
164 | return d(e, param_type { min, max }); | |
165 | } | |
166 | ||
167 | template <typename NumberT, | |
168 | typename DistributionT = detail::default_distribution<NumberT>, | |
169 | typename EngineT> | |
170 | NumberT generate_random_number(const NumberT min, const NumberT max) | |
171 | { | |
172 | return detail::generate_random_number<NumberT, DistributionT, EngineT> | |
173 | (min, max, detail::engine<EngineT>()); | |
174 | } | |
175 | ||
176 | template <typename MutexT, | |
177 | typename EngineT, | |
178 | typename NumberT = int, | |
179 | typename DistributionT = detail::default_distribution<NumberT>> | |
180 | NumberT generate_random_number(MutexT& m, EngineT& e) | |
181 | { | |
182 | return detail::generate_random_number<NumberT, MutexT, DistributionT, EngineT> | |
183 | (0, std::numeric_limits<NumberT>::max(), m, e); | |
184 | } | |
185 | ||
186 | template <typename NumberT, typename MutexT, typename EngineT> | |
187 | NumberT generate_random_number(const NumberT max, MutexT& m, EngineT& e) | |
188 | { | |
189 | return generate_random_number<NumberT>(0, max, m, e); | |
190 | } | |
191 | ||
192 | } // namespace detail | |
193 | ||
194 | template <typename EngineT = std::default_random_engine> | |
195 | void randomize_rng() | |
196 | { | |
197 | detail::randomize_rng<EngineT>(); | |
198 | } | |
199 | ||
200 | template <typename NumberT = int, | |
201 | typename DistributionT = detail::default_distribution<NumberT>, | |
202 | typename EngineT = std::default_random_engine> | |
203 | NumberT generate_random_number() | |
204 | { | |
205 | return detail::generate_random_number<NumberT, DistributionT, EngineT> | |
206 | (0, std::numeric_limits<NumberT>::max()); | |
207 | } | |
208 | ||
209 | template <typename NumberT0, typename NumberT1, | |
210 | typename NumberT = detail::select_number_t<NumberT0, NumberT1> | |
211 | > | |
212 | NumberT generate_random_number(const NumberT0 min, const NumberT1 max) | |
213 | { | |
214 | return detail::generate_random_number<NumberT, | |
215 | detail::default_distribution<NumberT>, | |
216 | std::default_random_engine> | |
217 | (static_cast<NumberT>(min), static_cast<NumberT>(max)); | |
218 | } | |
219 | ||
220 | template <typename NumberT0, typename NumberT1, | |
221 | typename DistributionT, | |
222 | typename EngineT, | |
223 | typename NumberT = detail::select_number_t<NumberT0, NumberT1> | |
224 | > | |
225 | NumberT generate_random_number(const NumberT min, const NumberT max, | |
226 | EngineT& e) | |
227 | { | |
228 | return detail::generate_random_number<NumberT, | |
229 | DistributionT, | |
230 | EngineT>(static_cast<NumberT>(min), static_cast<NumberT>(max), e); | |
231 | } | |
232 | ||
233 | template <typename NumberT> | |
234 | NumberT generate_random_number(const NumberT max) | |
235 | { | |
236 | return generate_random_number<NumberT>(0, max); | |
237 | } | |
238 | ||
239 | // Function object: | |
240 | template <typename NumberT> | |
241 | class random_number_generator final | |
242 | { | |
243 | std::mutex l; | |
f67539c2 | 244 | random_device_t rd; |
11fdf7f2 TL |
245 | std::default_random_engine e; |
246 | ||
247 | using seed_type = typename decltype(e)::result_type; | |
248 | ||
249 | public: | |
250 | using number_type = NumberT; | |
251 | using random_engine_type = decltype(e); | |
252 | using random_device_type = decltype(rd); | |
253 | ||
254 | public: | |
255 | random_device_type& random_device() noexcept { return rd; } | |
256 | random_engine_type& random_engine() noexcept { return e; } | |
257 | ||
258 | public: | |
259 | random_number_generator() { | |
260 | detail::randomize_rng(l, e); | |
261 | } | |
262 | ||
263 | explicit random_number_generator(const seed_type seed) { | |
264 | detail::randomize_rng(seed, l, e); | |
265 | } | |
266 | ||
267 | random_number_generator(random_number_generator&& rhs) | |
268 | : e(std::move(rhs.e)) | |
269 | {} | |
270 | ||
271 | public: | |
272 | random_number_generator(const random_number_generator&) = delete; | |
273 | random_number_generator& operator=(const random_number_generator&) = delete; | |
274 | ||
275 | public: | |
276 | NumberT operator()() { | |
277 | return detail::generate_random_number(l, e); | |
278 | } | |
279 | ||
280 | NumberT operator()(const NumberT max) { | |
281 | return detail::generate_random_number<NumberT>(max, l, e); | |
282 | } | |
283 | ||
284 | NumberT operator()(const NumberT min, const NumberT max) { | |
285 | return detail::generate_random_number<NumberT>(min, max, l, e); | |
286 | } | |
287 | ||
288 | public: | |
289 | void seed(const seed_type n) { | |
290 | detail::randomize_rng(n, l, e); | |
291 | } | |
292 | }; | |
293 | ||
9f95a23c TL |
294 | template <typename NumberT> |
295 | random_number_generator(const NumberT max) -> random_number_generator<NumberT>; | |
296 | ||
11fdf7f2 TL |
297 | } // inline namespace version_* |
298 | ||
299 | } // namespace ceph::util | |
300 | ||
301 | #endif |