xref: /aosp_15_r20/external/abseil-cpp/absl/random/bit_gen_ref.h (revision 9356374a3709195abf420251b3e825997ff56c0f)
1 //
2 // Copyright 2018 The Abseil Authors.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      https://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 // -----------------------------------------------------------------------------
17 // File: bit_gen_ref.h
18 // -----------------------------------------------------------------------------
19 //
20 // This header defines a bit generator "reference" class, for use in interfaces
21 // that take both Abseil (e.g. `absl::BitGen`) and standard library (e.g.
22 // `std::mt19937`) bit generators.
23 
24 #ifndef ABSL_RANDOM_BIT_GEN_REF_H_
25 #define ABSL_RANDOM_BIT_GEN_REF_H_
26 
27 #include <limits>
28 #include <type_traits>
29 #include <utility>
30 
31 #include "absl/base/attributes.h"
32 #include "absl/base/internal/fast_type_id.h"
33 #include "absl/base/macros.h"
34 #include "absl/meta/type_traits.h"
35 #include "absl/random/internal/distribution_caller.h"
36 #include "absl/random/internal/fast_uniform_bits.h"
37 
38 namespace absl {
39 ABSL_NAMESPACE_BEGIN
40 namespace random_internal {
41 
42 template <typename URBG, typename = void, typename = void, typename = void>
43 struct is_urbg : std::false_type {};
44 
45 template <typename URBG>
46 struct is_urbg<
47     URBG,
48     absl::enable_if_t<std::is_same<
49         typename URBG::result_type,
50         typename std::decay<decltype((URBG::min)())>::type>::value>,
51     absl::enable_if_t<std::is_same<
52         typename URBG::result_type,
53         typename std::decay<decltype((URBG::max)())>::type>::value>,
54     absl::enable_if_t<std::is_same<
55         typename URBG::result_type,
56         typename std::decay<decltype(std::declval<URBG>()())>::type>::value>>
57     : std::true_type {};
58 
59 template <typename>
60 struct DistributionCaller;
61 class MockHelpers;
62 
63 }  // namespace random_internal
64 
65 // -----------------------------------------------------------------------------
66 // absl::BitGenRef
67 // -----------------------------------------------------------------------------
68 //
69 // `absl::BitGenRef` is a type-erasing class that provides a generator-agnostic
70 // non-owning "reference" interface for use in place of any specific uniform
71 // random bit generator (URBG). This class may be used for both Abseil
72 // (e.g. `absl::BitGen`, `absl::InsecureBitGen`) and Standard library (e.g
73 // `std::mt19937`, `std::minstd_rand`) bit generators.
74 //
75 // Like other reference classes, `absl::BitGenRef` does not own the
76 // underlying bit generator, and the underlying instance must outlive the
77 // `absl::BitGenRef`.
78 //
79 // `absl::BitGenRef` is particularly useful when used with an
80 // `absl::MockingBitGen` to test specific paths in functions which use random
81 // values.
82 //
83 // Example:
84 //    void TakesBitGenRef(absl::BitGenRef gen) {
85 //      int x = absl::Uniform<int>(gen, 0, 1000);
86 //    }
87 //
88 class BitGenRef {
89   // SFINAE to detect whether the URBG type includes a member matching
90   // bool InvokeMock(base_internal::FastTypeIdType, void*, void*).
91   //
92   // These live inside BitGenRef so that they have friend access
93   // to MockingBitGen. (see similar methods in DistributionCaller).
94   template <template <class...> class Trait, class AlwaysVoid, class... Args>
95   struct detector : std::false_type {};
96   template <template <class...> class Trait, class... Args>
97   struct detector<Trait, absl::void_t<Trait<Args...>>, Args...>
98       : std::true_type {};
99 
100   template <class T>
101   using invoke_mock_t = decltype(std::declval<T*>()->InvokeMock(
102       std::declval<base_internal::FastTypeIdType>(), std::declval<void*>(),
103       std::declval<void*>()));
104 
105   template <typename T>
106   using HasInvokeMock = typename detector<invoke_mock_t, void, T>::type;
107 
108  public:
109   BitGenRef(const BitGenRef&) = default;
110   BitGenRef(BitGenRef&&) = default;
111   BitGenRef& operator=(const BitGenRef&) = default;
112   BitGenRef& operator=(BitGenRef&&) = default;
113 
114   template <
115       typename URBGRef, typename URBG = absl::remove_cvref_t<URBGRef>,
116       typename absl::enable_if_t<(!std::is_same<URBG, BitGenRef>::value &&
117                                   random_internal::is_urbg<URBG>::value &&
118                                   !HasInvokeMock<URBG>::value)>* = nullptr>
119   BitGenRef(URBGRef&& gen ABSL_ATTRIBUTE_LIFETIME_BOUND)  // NOLINT
120       : t_erased_gen_ptr_(reinterpret_cast<uintptr_t>(&gen)),
121         mock_call_(NotAMock),
122         generate_impl_fn_(ImplFn<URBG>) {}
123 
124   template <typename URBGRef, typename URBG = absl::remove_cvref_t<URBGRef>,
125             typename absl::enable_if_t<(!std::is_same<URBG, BitGenRef>::value &&
126                                         random_internal::is_urbg<URBG>::value &&
127                                         HasInvokeMock<URBG>::value)>* = nullptr>
128   BitGenRef(URBGRef&& gen ABSL_ATTRIBUTE_LIFETIME_BOUND)  // NOLINT
129       : t_erased_gen_ptr_(reinterpret_cast<uintptr_t>(&gen)),
130         mock_call_(&MockCall<URBG>),
131         generate_impl_fn_(ImplFn<URBG>) {}
132 
133   using result_type = uint64_t;
134 
135   static constexpr result_type(min)() {
136     return (std::numeric_limits<result_type>::min)();
137   }
138 
139   static constexpr result_type(max)() {
140     return (std::numeric_limits<result_type>::max)();
141   }
142 
143   result_type operator()() { return generate_impl_fn_(t_erased_gen_ptr_); }
144 
145  private:
146   using impl_fn = result_type (*)(uintptr_t);
147   using mock_call_fn = bool (*)(uintptr_t, base_internal::FastTypeIdType, void*,
148                                 void*);
149 
150   template <typename URBG>
151   static result_type ImplFn(uintptr_t ptr) {
152     // Ensure that the return values from operator() fill the entire
153     // range promised by result_type, min() and max().
154     absl::random_internal::FastUniformBits<result_type> fast_uniform_bits;
155     return fast_uniform_bits(*reinterpret_cast<URBG*>(ptr));
156   }
157 
158   // Get a type-erased InvokeMock pointer.
159   template <typename URBG>
160   static bool MockCall(uintptr_t gen_ptr, base_internal::FastTypeIdType type,
161                        void* result, void* arg_tuple) {
162     return reinterpret_cast<URBG*>(gen_ptr)->InvokeMock(type, result,
163                                                         arg_tuple);
164   }
165   static bool NotAMock(uintptr_t, base_internal::FastTypeIdType, void*, void*) {
166     return false;
167   }
168 
169   inline bool InvokeMock(base_internal::FastTypeIdType type, void* args_tuple,
170                          void* result) {
171     if (mock_call_ == NotAMock) return false;  // avoids an indirect call.
172     return mock_call_(t_erased_gen_ptr_, type, args_tuple, result);
173   }
174 
175   uintptr_t t_erased_gen_ptr_;
176   mock_call_fn mock_call_;
177   impl_fn generate_impl_fn_;
178 
179   template <typename>
180   friend struct ::absl::random_internal::DistributionCaller;  // for InvokeMock
181   friend class ::absl::random_internal::MockHelpers;          // for InvokeMock
182 };
183 
184 ABSL_NAMESPACE_END
185 }  // namespace absl
186 
187 #endif  // ABSL_RANDOM_BIT_GEN_REF_H_
188