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 "absl/base/macros.h" 28 #include "absl/meta/type_traits.h" 29 #include "absl/random/internal/distribution_caller.h" 30 #include "absl/random/internal/fast_uniform_bits.h" 31 #include "absl/random/internal/mocking_bit_gen_base.h" 32 33 namespace absl { 34 ABSL_NAMESPACE_BEGIN 35 namespace random_internal { 36 37 template <typename URBG, typename = void, typename = void, typename = void> 38 struct is_urbg : std::false_type {}; 39 40 template <typename URBG> 41 struct is_urbg< 42 URBG, 43 absl::enable_if_t<std::is_same< 44 typename URBG::result_type, 45 typename std::decay<decltype((URBG::min)())>::type>::value>, 46 absl::enable_if_t<std::is_same< 47 typename URBG::result_type, 48 typename std::decay<decltype((URBG::max)())>::type>::value>, 49 absl::enable_if_t<std::is_same< 50 typename URBG::result_type, 51 typename std::decay<decltype(std::declval<URBG>()())>::type>::value>> 52 : std::true_type {}; 53 54 } // namespace random_internal 55 56 // ----------------------------------------------------------------------------- 57 // absl::BitGenRef 58 // ----------------------------------------------------------------------------- 59 // 60 // `absl::BitGenRef` is a type-erasing class that provides a generator-agnostic 61 // non-owning "reference" interface for use in place of any specific uniform 62 // random bit generator (URBG). This class may be used for both Abseil 63 // (e.g. `absl::BitGen`, `absl::InsecureBitGen`) and Standard library (e.g 64 // `std::mt19937`, `std::minstd_rand`) bit generators. 65 // 66 // Like other reference classes, `absl::BitGenRef` does not own the 67 // underlying bit generator, and the underlying instance must outlive the 68 // `absl::BitGenRef`. 69 // 70 // `absl::BitGenRef` is particularly useful when used with an 71 // `absl::MockingBitGen` to test specific paths in functions which use random 72 // values. 73 // 74 // Example: 75 // void TakesBitGenRef(absl::BitGenRef gen) { 76 // int x = absl::Uniform<int>(gen, 0, 1000); 77 // } 78 // 79 class BitGenRef { 80 public: 81 using result_type = uint64_t; 82 83 BitGenRef(const absl::BitGenRef&) = default; 84 BitGenRef(absl::BitGenRef&&) = default; 85 BitGenRef& operator=(const absl::BitGenRef&) = default; 86 BitGenRef& operator=(absl::BitGenRef&&) = default; 87 88 template <typename URBG, 89 typename absl::enable_if_t< 90 (!std::is_same<URBG, BitGenRef>::value && 91 random_internal::is_urbg<URBG>::value)>* = nullptr> 92 BitGenRef(URBG& gen) // NOLINT 93 : mocked_gen_ptr_(MakeMockPointer(&gen)), 94 t_erased_gen_ptr_(reinterpret_cast<uintptr_t>(&gen)), 95 generate_impl_fn_(ImplFn<URBG>) { 96 } 97 98 static constexpr result_type(min)() { 99 return (std::numeric_limits<result_type>::min)(); 100 } 101 102 static constexpr result_type(max)() { 103 return (std::numeric_limits<result_type>::max)(); 104 } 105 106 result_type operator()() { return generate_impl_fn_(t_erased_gen_ptr_); } 107 108 private: 109 friend struct absl::random_internal::DistributionCaller<absl::BitGenRef>; 110 using impl_fn = result_type (*)(uintptr_t); 111 using mocker_base_t = absl::random_internal::MockingBitGenBase; 112 113 // Convert an arbitrary URBG pointer into either a valid mocker_base_t 114 // pointer or a nullptr. 115 static inline mocker_base_t* MakeMockPointer(mocker_base_t* t) { return t; } 116 static inline mocker_base_t* MakeMockPointer(void*) { return nullptr; } 117 118 template <typename URBG> 119 static result_type ImplFn(uintptr_t ptr) { 120 // Ensure that the return values from operator() fill the entire 121 // range promised by result_type, min() and max(). 122 absl::random_internal::FastUniformBits<result_type> fast_uniform_bits; 123 return fast_uniform_bits(*reinterpret_cast<URBG*>(ptr)); 124 } 125 126 mocker_base_t* mocked_gen_ptr_; 127 uintptr_t t_erased_gen_ptr_; 128 impl_fn generate_impl_fn_; 129 }; 130 131 namespace random_internal { 132 133 template <> 134 struct DistributionCaller<absl::BitGenRef> { 135 template <typename DistrT, typename FormatT, typename... Args> 136 static typename DistrT::result_type Call(absl::BitGenRef* gen_ref, 137 Args&&... args) { 138 auto* mock_ptr = gen_ref->mocked_gen_ptr_; 139 if (mock_ptr == nullptr) { 140 DistrT dist(std::forward<Args>(args)...); 141 return dist(*gen_ref); 142 } else { 143 return mock_ptr->template Call<DistrT, FormatT>( 144 std::forward<Args>(args)...); 145 } 146 } 147 }; 148 149 } // namespace random_internal 150 ABSL_NAMESPACE_END 151 } // namespace absl 152 153 #endif // ABSL_RANDOM_BIT_GEN_REF_H_ 154