]> git.proxmox.com Git - ceph.git/blame - ceph/src/include/random.h
update ceph source to reef 18.2.1
[ceph.git] / ceph / src / include / random.h
CommitLineData
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
27using random_device_t = boost::random::random_device;
28#else
29using random_device_t = std::random_device;
30#endif
31
9f95a23c 32// Basic random number facility (see N3551 for inspiration):
11fdf7f2
TL
33namespace ceph::util {
34
9f95a23c 35inline namespace version_1_0_3 {
11fdf7f2
TL
36
37namespace detail {
38
39template <typename T0, typename T1>
40using larger_of = typename std::conditional<
41 sizeof(T0) >= sizeof(T1),
42 T0, T1>
43 ::type;
44
45// avoid mixing floating point and integers:
46template <typename NumberT0, typename NumberT1>
47using 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:
59template <typename NumberT0, typename NumberT1>
60using 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
65namespace detail {
66
67// Choose default distribution for appropriate types:
68template <typename NumberT,
69 bool IsIntegral>
70struct select_distribution
71{
72 using type = std::uniform_int_distribution<NumberT>;
73};
74
75template <typename NumberT>
76struct select_distribution<NumberT, false>
77{
78 using type = std::uniform_real_distribution<NumberT>;
79};
80
81template <typename NumberT>
82using default_distribution = typename
83 select_distribution<NumberT, std::is_integral<NumberT>::value>::type;
84
85} // namespace detail
86
87namespace detail {
88
89template <typename EngineT>
90EngineT& engine();
91
92template <typename MutexT, typename EngineT,
93 typename SeedT = typename EngineT::result_type>
94void randomize_rng(const SeedT seed, MutexT& m, EngineT& e)
95{
96 std::lock_guard<MutexT> lg(m);
97 e.seed(seed);
98}
99
100template <typename MutexT, typename EngineT>
101void 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
109template <typename EngineT = std::default_random_engine,
110 typename SeedT = typename EngineT::result_type>
111void randomize_rng(const SeedT n)
112{
113 detail::engine<EngineT>().seed(n);
114}
115
116template <typename EngineT = std::default_random_engine>
117void randomize_rng()
118{
f67539c2 119 random_device_t rd;
11fdf7f2
TL
120 detail::engine<EngineT>().seed(rd());
121}
122
123template <typename EngineT>
124EngineT& 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
138namespace detail {
139
140template <typename NumberT,
141 typename DistributionT = detail::default_distribution<NumberT>,
142 typename EngineT>
143NumberT 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
152template <typename NumberT,
153 typename MutexT,
154 typename DistributionT = detail::default_distribution<NumberT>,
155 typename EngineT>
156NumberT 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
167template <typename NumberT,
168 typename DistributionT = detail::default_distribution<NumberT>,
169 typename EngineT>
170NumberT 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
176template <typename MutexT,
177 typename EngineT,
178 typename NumberT = int,
179 typename DistributionT = detail::default_distribution<NumberT>>
180NumberT 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
186template <typename NumberT, typename MutexT, typename EngineT>
187NumberT 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
194template <typename EngineT = std::default_random_engine>
195void randomize_rng()
196{
197 detail::randomize_rng<EngineT>();
198}
199
200template <typename NumberT = int,
201 typename DistributionT = detail::default_distribution<NumberT>,
202 typename EngineT = std::default_random_engine>
203NumberT generate_random_number()
204{
205 return detail::generate_random_number<NumberT, DistributionT, EngineT>
206 (0, std::numeric_limits<NumberT>::max());
207}
208
209template <typename NumberT0, typename NumberT1,
210 typename NumberT = detail::select_number_t<NumberT0, NumberT1>
211 >
212NumberT 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
220template <typename NumberT0, typename NumberT1,
221 typename DistributionT,
222 typename EngineT,
223 typename NumberT = detail::select_number_t<NumberT0, NumberT1>
224 >
225NumberT 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
233template <typename NumberT>
234NumberT generate_random_number(const NumberT max)
235{
236 return generate_random_number<NumberT>(0, max);
237}
238
239// Function object:
240template <typename NumberT>
241class 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
294template <typename NumberT>
295random_number_generator(const NumberT max) -> random_number_generator<NumberT>;
296
11fdf7f2
TL
297} // inline namespace version_*
298
299} // namespace ceph::util
300
301#endif