@ -42,7 +42,7 @@ namespace random_internal {
// 'Strong' (well-distributed, unpredictable, backtracking-resistant) random
// 'Strong' (well-distributed, unpredictable, backtracking-resistant) random
// generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32.
// generator, faster in some benchmarks than std::mt19937_64 and pcg64_c32.
template < typename T >
template < typename T >
class alignas ( 16 ) randen_engine {
class alignas ( 8 ) randen_engine {
public :
public :
// C++11 URBG interface:
// C++11 URBG interface:
using result_type = T ;
using result_type = T ;
@ -58,7 +58,8 @@ class alignas(16) randen_engine {
return ( std : : numeric_limits < result_type > : : max ) ( ) ;
return ( std : : numeric_limits < result_type > : : max ) ( ) ;
}
}
explicit randen_engine ( result_type seed_value = 0 ) { seed ( seed_value ) ; }
randen_engine ( ) : randen_engine ( 0 ) { }
explicit randen_engine ( result_type seed_value ) { seed ( seed_value ) ; }
template < class SeedSequence ,
template < class SeedSequence ,
typename = typename absl : : enable_if_t <
typename = typename absl : : enable_if_t <
@ -67,17 +68,27 @@ class alignas(16) randen_engine {
seed ( seq ) ;
seed ( seq ) ;
}
}
randen_engine ( const randen_engine & ) = default ;
// alignment requirements dictate custom copy and move constructors.
randen_engine ( const randen_engine & other )
: next_ ( other . next_ ) , impl_ ( other . impl_ ) {
std : : memcpy ( state ( ) , other . state ( ) , kStateSizeT * sizeof ( result_type ) ) ;
}
randen_engine & operator = ( const randen_engine & other ) {
next_ = other . next_ ;
impl_ = other . impl_ ;
std : : memcpy ( state ( ) , other . state ( ) , kStateSizeT * sizeof ( result_type ) ) ;
return * this ;
}
// Returns random bits from the buffer in units of result_type.
// Returns random bits from the buffer in units of result_type.
result_type operator ( ) ( ) {
result_type operator ( ) ( ) {
// Refill the buffer if needed (unlikely).
// Refill the buffer if needed (unlikely).
auto * begin = state ( ) ;
if ( next_ > = kStateSizeT ) {
if ( next_ > = kStateSizeT ) {
next_ = kCapacityT ;
next_ = kCapacityT ;
impl_ . Generate ( state_ ) ;
impl_ . Generate ( begin ) ;
}
}
return little_endian : : ToHost ( begin [ next_ + + ] ) ;
return little_endian : : ToHost ( state_ [ next_ + + ] ) ;
}
}
template < class SeedSequence >
template < class SeedSequence >
@ -92,9 +103,10 @@ class alignas(16) randen_engine {
void seed ( result_type seed_value = 0 ) {
void seed ( result_type seed_value = 0 ) {
next_ = kStateSizeT ;
next_ = kStateSizeT ;
// Zeroes the inner state and fills the outer state with seed_value to
// Zeroes the inner state and fills the outer state with seed_value to
// mimics behaviour of reseed
// mimic the behaviour of reseed
std : : fill ( std : : begin ( state_ ) , std : : begin ( state_ ) + kCapacityT , 0 ) ;
auto * begin = state ( ) ;
std : : fill ( std : : begin ( state_ ) + kCapacityT , std : : end ( state_ ) , seed_value ) ;
std : : fill ( begin , begin + kCapacityT , 0 ) ;
std : : fill ( begin + kCapacityT , begin + kStateSizeT , seed_value ) ;
}
}
// Inserts entropy into (part of) the state. Calling this periodically with
// Inserts entropy into (part of) the state. Calling this periodically with
@ -105,7 +117,6 @@ class alignas(16) randen_engine {
using sequence_result_type = typename SeedSequence : : result_type ;
using sequence_result_type = typename SeedSequence : : result_type ;
static_assert ( sizeof ( sequence_result_type ) = = 4 ,
static_assert ( sizeof ( sequence_result_type ) = = 4 ,
" SeedSequence::result_type must be 32-bit " ) ;
" SeedSequence::result_type must be 32-bit " ) ;
constexpr size_t kBufferSize =
constexpr size_t kBufferSize =
Randen : : kSeedBytes / sizeof ( sequence_result_type ) ;
Randen : : kSeedBytes / sizeof ( sequence_result_type ) ;
alignas ( 16 ) sequence_result_type buffer [ kBufferSize ] ;
alignas ( 16 ) sequence_result_type buffer [ kBufferSize ] ;
@ -119,8 +130,8 @@ class alignas(16) randen_engine {
if ( entropy_size < kBufferSize ) {
if ( entropy_size < kBufferSize ) {
// ... and only request that many values, or 256-bits, when unspecified.
// ... and only request that many values, or 256-bits, when unspecified.
const size_t requested_entropy = ( entropy_size = = 0 ) ? 8u : entropy_size ;
const size_t requested_entropy = ( entropy_size = = 0 ) ? 8u : entropy_size ;
std : : fill ( std : : begin ( buffer ) + requested_entropy , std : : end ( buffer ) , 0 ) ;
std : : fill ( buffer + requested_entropy , buffer + kBufferSize , 0 ) ;
seq . generate ( std : : begin ( buffer ) , std : : begin ( buffer ) + requested_entropy ) ;
seq . generate ( buffer , buffer + requested_entropy ) ;
# ifdef ABSL_IS_BIG_ENDIAN
# ifdef ABSL_IS_BIG_ENDIAN
// Randen expects the seed buffer to be in Little Endian; reverse it on
// Randen expects the seed buffer to be in Little Endian; reverse it on
// Big Endian platforms.
// Big Endian platforms.
@ -146,9 +157,9 @@ class alignas(16) randen_engine {
std : : swap ( buffer [ - - dst ] , buffer [ - - src ] ) ;
std : : swap ( buffer [ - - dst ] , buffer [ - - src ] ) ;
}
}
} else {
} else {
seq . generate ( std : : begin ( buffer ) , std : : end ( buffer ) ) ;
seq . generate ( buffer , buffer + kBufferSize ) ;
}
}
impl_ . Absorb ( buffer , state_ ) ;
impl_ . Absorb ( buffer , state ( ) ) ;
// Generate will be called when operator() is called
// Generate will be called when operator() is called
next_ = kStateSizeT ;
next_ = kStateSizeT ;
@ -159,9 +170,10 @@ class alignas(16) randen_engine {
count - = step ;
count - = step ;
constexpr uint64_t kRateT = kStateSizeT - kCapacityT ;
constexpr uint64_t kRateT = kStateSizeT - kCapacityT ;
auto * begin = state ( ) ;
while ( count > 0 ) {
while ( count > 0 ) {
next_ = kCapacityT ;
next_ = kCapacityT ;
impl_ . Generate ( stat e_ ) ;
impl_ . Generate ( * reinterpret_ca st< r esult _type ( * ) [ kStateSizeT ] > ( begin ) ) ;
step = std : : min < uint64_t > ( kRateT , count ) ;
step = std : : min < uint64_t > ( kRateT , count ) ;
count - = step ;
count - = step ;
}
}
@ -169,9 +181,9 @@ class alignas(16) randen_engine {
}
}
bool operator = = ( const randen_engine & other ) const {
bool operator = = ( const randen_engine & other ) const {
const auto * begin = state ( ) ;
return next_ = = other . next_ & &
return next_ = = other . next_ & &
std : : equal ( std : : begin ( state_ ) , std : : end ( state_ ) ,
std : : equal ( begin , begin + kStateSizeT , other . state ( ) ) ;
std : : begin ( other . state_ ) ) ;
}
}
bool operator ! = ( const randen_engine & other ) const {
bool operator ! = ( const randen_engine & other ) const {
@ -185,11 +197,12 @@ class alignas(16) randen_engine {
using numeric_type =
using numeric_type =
typename random_internal : : stream_format_type < result_type > : : type ;
typename random_internal : : stream_format_type < result_type > : : type ;
auto saver = random_internal : : make_ostream_state_saver ( os ) ;
auto saver = random_internal : : make_ostream_state_saver ( os ) ;
for ( const auto & elem : engine . state_ ) {
auto * it = engine . state ( ) ;
for ( auto * end = it + kStateSizeT ; it < end ; + + it ) {
// In the case that `elem` is `uint8_t`, it must be cast to something
// In the case that `elem` is `uint8_t`, it must be cast to something
// larger so that it prints as an integer rather than a character. For
// larger so that it prints as an integer rather than a character. For
// simplicity, apply the cast all circumstances.
// simplicity, apply the cast all circumstances.
os < < static_cast < numeric_type > ( little_endian : : FromHost ( elem ) )
os < < static_cast < numeric_type > ( little_endian : : FromHost ( * it ) )
< < os . fill ( ) ;
< < os . fill ( ) ;
}
}
os < < engine . next_ ;
os < < engine . next_ ;
@ -215,7 +228,7 @@ class alignas(16) randen_engine {
if ( is . fail ( ) ) {
if ( is . fail ( ) ) {
return is ;
return is ;
}
}
std : : memcpy ( engine . state_ , state , sizeof ( engine . state_ ) ) ;
std : : memcpy ( engine . state ( ) , state , sizeof ( state ) ) ;
engine . next_ = next ;
engine . next_ = next ;
return is ;
return is ;
}
}
@ -226,9 +239,21 @@ class alignas(16) randen_engine {
static constexpr size_t kCapacityT =
static constexpr size_t kCapacityT =
Randen : : kCapacityBytes / sizeof ( result_type ) ;
Randen : : kCapacityBytes / sizeof ( result_type ) ;
// First kCapacityT are `inner', the others are accessible random bits.
// Returns the state array pointer, which is aligned to 16 bytes.
alignas ( 16 ) result_type state_ [ kStateSizeT ] ;
// The first kCapacityT are the `inner' sponge; the remainder are available.
size_t next_ ; // index within state_
result_type * state ( ) {
return reinterpret_cast < result_type * > (
( reinterpret_cast < uintptr_t > ( & raw_state_ ) & 0xf ) ? ( raw_state_ + 8 )
: raw_state_ ) ;
}
const result_type * state ( ) const {
return const_cast < randen_engine * > ( this ) - > state ( ) ;
}
// raw state array, manually aligned in state(). This overallocates
// by 8 bytes since C++ does not guarantee extended heap alignment.
alignas ( 8 ) char raw_state_ [ Randen : : kStateBytes + 8 ] ;
size_t next_ ; // index within state()
Randen impl_ ;
Randen impl_ ;
} ;
} ;