Skip to content

Commit

Permalink
Update the random_device implementation so that it:
Browse files Browse the repository at this point in the history
* Supports more platforms optimally, for example CloudABI, OpenBSD, and Windows UWP
* Is easier to maintain as each platform's implementation is in a separate file
* Removes the library dependency on Boost.System
* Is header-only, and thus makes Boost.Random header-only
* Is well-tested for happy and sad paths

Removes the token-based random_device explicit constructor.

Adds a new exception "entropy_error" to handle errors getting entropy.

Removed the detail auto_link implementation inside Boost.Random as it is no longer
necessary - the one in Boost.Config is sufficient.

Also added a top-level Jamfile that builds the example subdirectory with each
build, as one of the examples needed to be updated in order to build.  This will
prevent rot in the example directory.

Note: Other libraries that link against Boost.Random (like Boost.Uuid) will fail
to build until they stop trying to link against Boost.Random.

This fixes boostorg#20
This fixes boostorg#22
  • Loading branch information
jeking3 committed Dec 7, 2017
1 parent e4b0b2f commit 4875f16
Show file tree
Hide file tree
Showing 26 changed files with 1,358 additions and 427 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/doc/html
/doc/reference.xml
/test/rng.saved
/example/rng.saved
**/rng.saved
30 changes: 30 additions & 0 deletions Jamfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Boost.Random Library Jamfile
#
# Copyright (c) 2017 James E. King, III
#
# Use, modification, and distribution are subject to the
# Boost Software License, Version 1.0. (See accompanying file
# LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

project libs/random
: requirements

<warnings>all

<toolset>clang:<cxxflags>-Wextra
<toolset>clang:<cxxflags>-ansi
# <toolset>clang:<cxxflags>-pedantic
<toolset>clang:<cxxflags>-Wno-c++11-long-long

<toolset>gcc:<cxxflags>-Wextra
<toolset>gcc:<cxxflags>-ansi
# <toolset>gcc:<cxxflags>-pedantic
<toolset>gcc:<cxxflags>-Wno-long-long
;

# pedantic mode disabled due to issue in multiprecision
# https://github.com/boostorg/multiprecision/issues/34

# please order by name to ease maintenance
build-project example ;
build-project test ;
18 changes: 0 additions & 18 deletions build/Jamfile.v2

This file was deleted.

100 changes: 70 additions & 30 deletions doc/nondet_random.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,14 @@
{
public:
typedef unsigned int result_type;
static const bool has_fixed_range = true;
static const result_type min_value = /* implementation defined */;
static const result_type max_value = /* implementation defined */;
result_type min() const;
result_type max() const;
explicit random_device(const std::string& token = default_token);
~random_device();
static const bool has_fixed_range = false;
static result_type min();
static result_type max();
double entropy() const;
unsigned int operator()();
void randomize(void *, size_t siz);
const char * name() const;
template<class Iter> void generate(Iter first, Iter last);
};

[endsect]
Expand All @@ -54,15 +53,24 @@ provide a special device for exactly this purpose. It seems to be impossible
to emulate the functionality using Standard C++ only, so users should be aware
that this class may not be available on all platforms.]

The optimal operating system specific implementation for entropy generation
is selected automatically at compile time. See notes below for more details.

[endsect]

[section Members]

explicit random_device(const std::string& token = default_token)
static result_type min()

Returns: the smallest value that the random_device can produce.

Throws: Nothing.

Effects: Constructs a random_device, optionally using the given token as an
access specification (for example, a URL) to some implementation-defined
service for monitoring a stochastic process.
static result_type max()

Returns: the largest value that the random_device can produce.

Throws: Nothing.

double entropy() const

Expand All @@ -72,32 +80,64 @@ generator (e.g. a pseudo-random number engine) has entropy 0.

Throws: Nothing.

unsigned int operator()()

Returns: a random value in the range [min(), max()]

Throws: entropy_error, if an error occurs obtaining entropy from the operating system

void randomize(void *ptr, size_t siz)

Fills a buffer with random bytes.

Throws: entropy_error, if an error occurs obtaining entropy from the operating system

const char * name() const

Returns: the name of the entropy provider

Throws: Nothing.

template<class Iter> void generate(Iter first, Iter last)

Allows random_device to be used as a SeedSeq for
PseudoRandomNumberGeneration seeding.

Throws: entropy_error, if an error occurs obtaining entropy from the operating system

[endsect]

[section Entropy Providers]

The selection logic for the entropy provider is as follows:

1. On CloudABI, or on OpenBSD version 2.1 or later, `arc4random` will be used.
2. On Windows platforms, the `bcrypt` provider is used unless targeting Windows CE or Windows XP, where the `wincrypt` provider is used.
3. On Linux platforms with glibc >= 2.25, `getentropy` is used, otherwise it is treated as a POSIX platform.
4. On POSIX platforms, entropy is obtained by reading from `/dev/urandom`.

[endsect]

Implementation Note for Linux
On the Linux operating system, token is interpreted as a filesystem path. It
is assumed that this path denotes an operating system pseudo-device which
generates a stream of non-deterministic random numbers. The pseudo-device
should never signal an error or end-of-file. Otherwise, std::ios_base::failure
is thrown. By default, random_device uses the /dev/urandom pseudo-device to
retrieve the random numbers. Another option would be to specify the
/dev/random pseudo-device, which blocks on reads if the entropy pool has no
more random bits available.
[section Preprocessor Definitions]

`BOOST_RANDOM_NO_LIB` (Windows) - disable auto-linking for the `bcrypt` and `wincrypt` provider when building with MSVC
`BOOST_RANDOM_PROVIDER_SHOW` - display the chosen entropy provider at compile time

[endsect]

[section Performance]
[section Notes]

The test program nondet_random_speed.cpp measures the execution times of the
nondet_random.hpp implementation of the above algorithms in a tight loop.
The performance has been evaluated on a Pentium Pro 200 MHz with gcc 2.95.2,
Linux 2.2.13, glibc 2.1.2.
To fill a buffer with random bytes, call random_device::randomize. Keep in mind
that there may be operating-system specific setup and teardown costs associated
with entropy generation, therefore if you are going to call this often, you will
want to reuse the random_device.

[table preformance
[[class] [time per invocation \[usec\]]]
[[random_device] [92.0]]
]
It is easy to use `random_device` as a seed sequence for a PseudoRandomNumberGenerator:

boost::mt19937 twister;
boost::random_device rng;
twister.seed(rng);

The measurement error is estimated at +/- 1 usec.
[endsect]

[endsect]
17 changes: 16 additions & 1 deletion example/Jamfile.v2
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,21 @@
# accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)

project libs/random/example
: requirements

# boost.jam defines BOOST_ALL_NO_LIB for builds
# which cannot be undefined?
<toolset>msvc:<define>BOOST_RANDOM_FORCE_AUTO_LINK
<toolset>gcc-mingw:<linkflags>"-lbcrypt"

# boost::random needs this setting for a warning free build:
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS

# link static for easier debugging - uncomment if you need to debug...
# <link>static
;

run die.cpp ;
run weighted_die.cpp ;
run password.cpp /boost//random ;
run password.cpp ;
2 changes: 1 addition & 1 deletion example/password.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
password.
*/


#include <boost/random/random_device.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <iostream>

int main() {
/*<< We first define the characters that we're going
Expand Down
40 changes: 0 additions & 40 deletions include/boost/random/detail/auto_link.hpp

This file was deleted.

2 changes: 1 addition & 1 deletion include/boost/random/detail/generator_seed_seq.hpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* boost random/mersenne_twister.hpp header file
/* boost random/detail/generator_seed_seq.hpp header file
*
* Copyright Jens Maurer 2000-2001
* Copyright Steven Watanabe 2010
Expand Down
75 changes: 75 additions & 0 deletions include/boost/random/detail/random_provider.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//
// Copyright (c) 2017 James E. King III
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENCE_1_0.txt)
//
// Platform-specific random entropy provider
//

#ifndef BOOST_RANDOM_DETAIL_RANDOM_PROVIDER_HPP
#define BOOST_RANDOM_DETAIL_RANDOM_PROVIDER_HPP

#include <boost/core/noncopyable.hpp>
#include <boost/cstdint.hpp>
#include <boost/limits.hpp>
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_integral.hpp>
#include <boost/type_traits/is_unsigned.hpp>
#include <boost/random/entropy_error.hpp>
#include <iterator>

// Detection of the platform is separated from inclusion of the correct
// header to facilitate mock testing of the provider implementations.

#include <boost/random/detail/random_provider_detect_platform.hpp>
#include <boost/random/detail/random_provider_include_platform.hpp>


namespace boost {
namespace random {
namespace detail {

//! \brief Contains code common to all random_provider implementations.
//! \note random_provider_base is required to provide this method:
//! void get_random_bytes(void *buf, size_t siz);
//! \note noncopyable because of some base implementations so
//! this makes it uniform across platforms to avoid any
//! porting surprises
class random_provider
: public detail::random_provider_base,
public noncopyable
{
public:
//! Leverage the provider as a SeedSeq for
//! PseudoRandomNumberGeneration seeding
//! \note: See Boost.Random documentation for more details
template<class Iter>
void generate(Iter first, Iter last)
{
typedef typename std::iterator_traits<Iter>::value_type value_type;
BOOST_STATIC_ASSERT(is_integral<value_type>::value);
BOOST_STATIC_ASSERT(is_unsigned<value_type>::value);
BOOST_STATIC_ASSERT(sizeof(value_type) * CHAR_BIT >= 32);

for (; first != last; ++first)
{
get_random_bytes(&*first, sizeof(*first));
*first &= (std::numeric_limits<boost::uint32_t>::max)();
}
}

//! Return the name of the selected provider
const char * name() const
{
return BOOST_RANDOM_PROVIDER_STRINGIFY(BOOST_RANDOM_PROVIDER_NAME);
}
};

} // detail
} // random
} // boost

#endif // BOOST_RANDOM_DETAIL_RANDOM_PROVIDER_HPP

32 changes: 32 additions & 0 deletions include/boost/random/detail/random_provider_arc4random.ipp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// Copyright (c) 2017 James E. King III
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENCE_1_0.txt)
//
// "A Replacement Call for Random"
// https://man.openbsd.org/arc4random.3
//

#include <stdlib.h>

namespace boost {
namespace random {
namespace detail {

class random_provider_base
{
public:
//! Obtain entropy and place it into a memory location
//! \param[in] buf the location to write entropy
//! \param[in] siz the number of bytes to acquire
void get_random_bytes(void *buf, size_t siz)
{
arc4random_buf(buf, siz);
}
};

} // detail
} // random
} // boost
Loading

0 comments on commit 4875f16

Please sign in to comment.