//------------------------------------------------------------------------------ /* This file is part of rippled: https://github.com/ripple/rippled Copyright (c) 2012, 2013 Ripple Labs Inc. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ //============================================================================== #ifndef RIPPLE_BASICS_RANDOM_H_INCLUDED #define RIPPLE_BASICS_RANDOM_H_INCLUDED #include #include #include #include #include #include #include #include namespace ripple { #ifndef __INTELLISENSE__ static_assert( std::is_integral::value && std::is_unsigned::value, "The Ripple default PRNG engine must return an unsigned integral type."); static_assert( std::numeric_limits::max() >= std::numeric_limits::max(), "The Ripple default PRNG engine return must be at least 64 bits wide."); #endif namespace detail { // Determines if a type can be called like an Engine template using is_engine = std::is_invocable_r; } // namespace detail /** Return the default random engine. This engine is guaranteed to be deterministic, but by default will be randomly seeded. It is NOT cryptographically secure and MUST NOT be used to generate randomness that will be used for keys, secure cookies, IVs, padding, etc. Each thread gets its own instance of the engine which will be randomly seeded. */ inline beast::xor_shift_engine& default_prng() { // This is used to seed the thread-specific PRNGs on demand static beast::xor_shift_engine seeder = [] { std::random_device rng; std::uniform_int_distribution distribution{1}; return beast::xor_shift_engine(distribution(rng)); }(); // This protects the seeder static std::mutex m; // The thread-specific PRNGs: thread_local beast::xor_shift_engine engine = [] { std::uint64_t seed; { std::lock_guard lk(m); std::uniform_int_distribution distribution{1}; seed = distribution(seeder); } return beast::xor_shift_engine{seed}; }(); return engine; } /** Return a uniformly distributed random integer. @param min The smallest value to return. If not specified the value defaults to 0. @param max The largest value to return. If not specified the value defaults to the largest value that can be represented. The randomness is generated by the specified engine (or the default engine if one is not specified). The result is cryptographically secure only when the engine passed into the function is cryptographically secure. @note The range is always a closed interval, so calling rand_int(-5, 15) can return any integer in the closed interval [-5, 15]; similarly, calling rand_int(7) can return any integer in the closed interval [0, 7]. */ /** @{ */ template std::enable_if_t< std::is_integral::value && detail::is_engine::value, Integral> rand_int(Engine& engine, Integral min, Integral max) { XRPL_ASSERT(max > min, "ripple::rand_int : max over min inputs"); // This should have no state and constructing it should // be very cheap. If that turns out not to be the case // it could be hand-optimized. return std::uniform_int_distribution(min, max)(engine); } template std::enable_if_t::value, Integral> rand_int(Integral min, Integral max) { return rand_int(default_prng(), min, max); } template std::enable_if_t< std::is_integral::value && detail::is_engine::value, Integral> rand_int(Engine& engine, Integral max) { return rand_int(engine, Integral(0), max); } template std::enable_if_t::value, Integral> rand_int(Integral max) { return rand_int(default_prng(), max); } template std::enable_if_t< std::is_integral::value && detail::is_engine::value, Integral> rand_int(Engine& engine) { return rand_int(engine, std::numeric_limits::max()); } template std::enable_if_t::value, Integral> rand_int() { return rand_int(default_prng(), std::numeric_limits::max()); } /** @} */ /** Return a random byte */ /** @{ */ template std::enable_if_t< (std::is_same::value || std::is_same::value) && detail::is_engine::value, Byte> rand_byte(Engine& engine) { return static_cast(rand_int( engine, std::numeric_limits::min(), std::numeric_limits::max())); } template std::enable_if_t< (std::is_same::value || std::is_same::value), Byte> rand_byte() { return rand_byte(default_prng()); } /** @} */ /** Return a random boolean value */ /** @{ */ template inline bool rand_bool(Engine& engine) { return rand_int(engine, 1) == 1; } inline bool rand_bool() { return rand_bool(default_prng()); } /** @} */ } // namespace ripple #endif // RIPPLE_BASICS_RANDOM_H_INCLUDED