1 // Boost random_generator.hpp header file ----------------------------------------------// 2 3 // Copyright 2010 Andy Tompkins. 4 // Copyright 2017 James E. King III 5 // Distributed under the Boost Software License, Version 1.0. (See 6 // accompanying file LICENSE_1_0.txt or copy at 7 // https://www.boost.org/LICENSE_1_0.txt) 8 9 #ifndef BOOST_UUID_RANDOM_GENERATOR_HPP 10 #define BOOST_UUID_RANDOM_GENERATOR_HPP 11 12 #include <boost/config.hpp> 13 #include <boost/assert.hpp> 14 #include <boost/move/core.hpp> 15 #include <boost/move/utility_core.hpp> 16 #include <boost/core/enable_if.hpp> 17 #include <boost/random/mersenne_twister.hpp> 18 #include <boost/random/uniform_int.hpp> 19 #include <boost/random/variate_generator.hpp> 20 #include <boost/tti/has_member_function.hpp> 21 #include <boost/uuid/detail/random_provider.hpp> 22 #include <boost/uuid/uuid.hpp> 23 #include <limits> 24 25 namespace boost { 26 namespace uuids { 27 28 namespace detail { 29 template<class U> set_uuid_random_vv(U & u)30 U& set_uuid_random_vv(U& u) 31 { 32 // set variant 33 // must be 0b10xxxxxx 34 *(u.begin() + 8) &= 0xBF; 35 *(u.begin() + 8) |= 0x80; 36 37 // set version 38 // must be 0b0100xxxx 39 *(u.begin() + 6) &= 0x4F; //0b01001111 40 *(u.begin() + 6) |= 0x40; //0b01000000 41 42 return u; 43 } 44 45 BOOST_TTI_HAS_MEMBER_FUNCTION(seed) 46 } 47 48 //! generate a random-based uuid 49 //! \param[in] UniformRandomNumberGenerator see Boost.Random documentation 50 template <typename UniformRandomNumberGenerator> 51 class basic_random_generator 52 { 53 BOOST_MOVABLE_BUT_NOT_COPYABLE(basic_random_generator) 54 55 private: 56 typedef uniform_int<unsigned long> distribution_type; 57 typedef variate_generator<UniformRandomNumberGenerator*, distribution_type> generator_type; 58 59 struct impl 60 { 61 generator_type generator; 62 implboost::uuids::basic_random_generator::impl63 explicit impl(UniformRandomNumberGenerator* purng_arg) : 64 generator(purng_arg, distribution_type((std::numeric_limits<unsigned long>::min)(), (std::numeric_limits<unsigned long>::max)())) 65 { 66 } 67 ~implboost::uuids::basic_random_generator::impl68 virtual ~impl() {} 69 70 BOOST_DELETED_FUNCTION(impl(impl const&)) 71 BOOST_DELETED_FUNCTION(impl& operator= (impl const&)) 72 }; 73 74 struct urng_holder 75 { 76 UniformRandomNumberGenerator urng; 77 }; 78 79 #if defined(BOOST_MSVC) 80 #pragma warning(push) 81 // 'this' : used in base member initializer list 82 #pragma warning(disable: 4355) 83 #endif 84 85 struct self_contained_impl : 86 public urng_holder, 87 public impl 88 { self_contained_implboost::uuids::basic_random_generator::self_contained_impl89 self_contained_impl() : impl(&this->urng_holder::urng) 90 { 91 } 92 }; 93 94 #if defined(BOOST_MSVC) 95 #pragma warning(pop) 96 #endif 97 98 public: 99 typedef uuid result_type; 100 101 // default constructor creates the random number generator and 102 // if the UniformRandomNumberGenerator is a PseudoRandomNumberGenerator 103 // then it gets seeded by a random_provider. basic_random_generator()104 basic_random_generator() : m_impl(new self_contained_impl()) 105 { 106 // seed the random number generator if it is capable 107 seed(static_cast< self_contained_impl* >(m_impl)->urng); 108 } 109 110 // keep a reference to a random number generator 111 // don't seed a given random number generator basic_random_generator(UniformRandomNumberGenerator & gen)112 explicit basic_random_generator(UniformRandomNumberGenerator& gen) : m_impl(new impl(&gen)) 113 { 114 } 115 116 // keep a pointer to a random number generator 117 // don't seed a given random number generator basic_random_generator(UniformRandomNumberGenerator * gen)118 explicit basic_random_generator(UniformRandomNumberGenerator* gen) : m_impl(new impl(gen)) 119 { 120 BOOST_ASSERT(!!gen); 121 } 122 basic_random_generator(BOOST_RV_REF (basic_random_generator)that)123 basic_random_generator(BOOST_RV_REF(basic_random_generator) that) BOOST_NOEXCEPT : m_impl(that.m_impl) 124 { 125 that.m_impl = 0; 126 } 127 operator =(BOOST_RV_REF (basic_random_generator)that)128 basic_random_generator& operator= (BOOST_RV_REF(basic_random_generator) that) BOOST_NOEXCEPT 129 { 130 delete m_impl; 131 m_impl = that.m_impl; 132 that.m_impl = 0; 133 return *this; 134 } 135 ~basic_random_generator()136 ~basic_random_generator() 137 { 138 delete m_impl; 139 } 140 operator ()()141 result_type operator()() 142 { 143 result_type u; 144 145 int i=0; 146 unsigned long random_value = m_impl->generator(); 147 for (uuid::iterator it = u.begin(), end = u.end(); it != end; ++it, ++i) { 148 if (i==sizeof(unsigned long)) { 149 random_value = m_impl->generator(); 150 i = 0; 151 } 152 153 // static_cast gets rid of warnings of converting unsigned long to boost::uint8_t 154 *it = static_cast<uuid::value_type>((random_value >> (i*8)) & 0xFF); 155 } 156 157 return detail::set_uuid_random_vv(u); 158 } 159 160 private: 161 // Detect whether UniformRandomNumberGenerator has a seed() method which indicates that 162 // it is a PseudoRandomNumberGenerator and needs a seed to initialize it. This allows 163 // basic_random_generator to take any type of UniformRandomNumberGenerator and still 164 // meet the post-conditions for the default constructor. 165 166 template<class MaybePseudoRandomNumberGenerator> 167 typename boost::enable_if<detail::has_member_function_seed<MaybePseudoRandomNumberGenerator, void> >::type seed(MaybePseudoRandomNumberGenerator & rng)168 seed(MaybePseudoRandomNumberGenerator& rng) 169 { 170 detail::random_provider seeder; 171 rng.seed(seeder); 172 } 173 174 template<class MaybePseudoRandomNumberGenerator> 175 typename boost::disable_if<detail::has_member_function_seed<MaybePseudoRandomNumberGenerator, void> >::type seed(MaybePseudoRandomNumberGenerator &)176 seed(MaybePseudoRandomNumberGenerator&) 177 { 178 } 179 180 impl* m_impl; 181 }; 182 183 //! \brief a far less complex random generator that uses 184 //! operating system provided entropy which will 185 //! satisfy the majority of use cases 186 class random_generator_pure 187 { 188 BOOST_MOVABLE_BUT_NOT_COPYABLE(random_generator_pure) 189 190 public: 191 typedef uuid result_type; 192 random_generator_pure()193 BOOST_DEFAULTED_FUNCTION(random_generator_pure(), {}) 194 195 random_generator_pure(BOOST_RV_REF(random_generator_pure) that) BOOST_NOEXCEPT : 196 prov_(boost::move(that.prov_)) 197 { 198 } 199 operator =(BOOST_RV_REF (random_generator_pure)that)200 random_generator_pure& operator= (BOOST_RV_REF(random_generator_pure) that) BOOST_NOEXCEPT 201 { 202 prov_ = boost::move(that.prov_); 203 return *this; 204 } 205 206 //! \returns a random, valid uuid 207 //! \throws entropy_error operator ()()208 result_type operator()() 209 { 210 result_type result; 211 prov_.get_random_bytes(&result, sizeof(result_type)); 212 return detail::set_uuid_random_vv(result); 213 } 214 215 private: 216 detail::random_provider prov_; 217 }; 218 219 #if defined(BOOST_UUID_RANDOM_GENERATOR_COMPAT) 220 typedef basic_random_generator<mt19937> random_generator; 221 #else 222 typedef random_generator_pure random_generator; 223 typedef basic_random_generator<mt19937> random_generator_mt19937; 224 #endif 225 226 }} // namespace boost::uuids 227 228 #endif // BOOST_UUID_RANDOM_GENERATOR_HPP 229