From f40b6a672aa4c0a3df2165ffaaac49b9a8d935e7 Mon Sep 17 00:00:00 2001 From: don bright Date: Sun, 6 Jan 2013 06:27:42 +0100 Subject: [PATCH 1/7] switch builtin_rands() to use boost::random per issue 234 --- src/func.cc | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/func.cc b/src/func.cc index e427bf2d..a4d48e22 100644 --- a/src/func.cc +++ b/src/func.cc @@ -34,6 +34,14 @@ #include #include "stl-utils.h" #include "printutils.h" +#include +#include +#include + +boost::random::random_device nondeterministic_rng; +boost::random::mt19937 deterministic_rng; + +#include "random_device.cpp" AbstractFunction::~AbstractFunction() { @@ -131,12 +139,13 @@ double frand(double min, double max) Value builtin_rands(const Context *, const std::vector&, const std::vector &args) { + bool deterministic = false; if (args.size() == 3 && args[0].type() == Value::NUMBER && args[1].type() == Value::NUMBER && args[2].type() == Value::NUMBER) { - srand((unsigned int)time(0)); + deterministic = false; } else if (args.size() == 4 && args[0].type() == Value::NUMBER && @@ -144,7 +153,8 @@ Value builtin_rands(const Context *, const std::vector&, const std: args[2].type() == Value::NUMBER && args[3].type() == Value::NUMBER) { - srand((unsigned int)args[3].toDouble()); + deterministic_rng.seed( (unsigned int) args[3].toDouble() ); + deterministic = true; } else { @@ -153,7 +163,14 @@ Value builtin_rands(const Context *, const std::vector&, const std: Value::VectorType vec; for (int i=0; i dist( min, max ); + if ( deterministic ) { + vec.push_back( Value( dist( deterministic_rng ) ) ); + } else { + vec.push_back( Value( dist( nondeterministic_rng ) ) ); + } } return Value(vec); From 84c6d45eaac2d04d4e855dddb88bb208e531451e Mon Sep 17 00:00:00 2001 From: don bright Date: Sun, 6 Jan 2013 06:31:41 +0100 Subject: [PATCH 2/7] experimental workaround for boost linker problems w random_device --- src/random_device.cpp | 217 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 src/random_device.cpp diff --git a/src/random_device.cpp b/src/random_device.cpp new file mode 100644 index 00000000..699a880f --- /dev/null +++ b/src/random_device.cpp @@ -0,0 +1,217 @@ +/* boost random_device.cpp implementation + * + * Copyright Jens Maurer 2000 + * Copyright Steven Watanabe 2010-2011 + * Distributed under 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) + * + * $Id: random_device.cpp 71018 2011-04-05 21:27:52Z steven_watanabe $ + * + */ + +#define BOOST_RANDOM_SOURCE + +#include +#include +#include +#include +#include + +#if !defined(BOOST_NO_INCLASS_MEMBER_INITIALIZATION) && !BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1600)) +// A definition is required even for integral static constants +const bool boost::random::random_device::has_fixed_range; +#endif + + +#if defined(BOOST_WINDOWS) + +#include +#include +#include // std::invalid_argument + +#define BOOST_AUTO_LINK_NOMANGLE +#define BOOST_LIB_NAME "Advapi32" +#include + +#ifdef __MINGW32__ + +extern "C" { + +// mingw's wincrypt.h appears to be missing some things +WINADVAPI +BOOL +WINAPI +CryptEnumProvidersA( + DWORD dwIndex, + DWORD *pdwReserved, + DWORD dwFlags, + DWORD *pdwProvType, + LPSTR szProvName, + DWORD *pcbProvName + ); + +} + +#endif + +namespace { + +const char * const default_token = MS_DEF_PROV_A; + +} + +class boost::random::random_device::impl +{ +public: + impl(const std::string & token) : provider(token) { + char buffer[80]; + DWORD type; + DWORD len; + + // Find the type of the provider + for(DWORD i = 0; ; ++i) { + len = sizeof(buffer); + if(!CryptEnumProvidersA(i, NULL, 0, &type, buffer, &len)) { + error("Could not find provider name"); + } + if(buffer == provider) { + break; + } + } + + if(!CryptAcquireContextA(&hProv, NULL, provider.c_str(), type, + CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { + error("Could not acquire CSP context"); + } + } + + ~impl() { + if(!CryptReleaseContext(hProv, 0)) error("Could not release CSP context"); + } + + unsigned int next() { + unsigned int result; + + if(!CryptGenRandom(hProv, sizeof(result), + static_cast(static_cast(&result)))) { + error("error while reading"); + } + + return result; + } + +private: + void error(const std::string & msg) { + char buf[80]; + DWORD num = FormatMessageA( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + 0, + buf, + sizeof(buf), + NULL); + + throw std::invalid_argument("boost::random_device: " + msg + + " Cryptopraphic Service Provider " + provider + + ": " + std::string(&buf[0], &buf[0] + num)); + } + const std::string provider; + HCRYPTPROV hProv; +}; + +#else + +namespace { +// the default is the unlimited capacity device, using some secure hash +// try "/dev/random" for blocking when the entropy pool has drained +const char * const default_token = "/dev/urandom"; +} + +/* + * This uses the POSIX interface for unbuffered reading. + * Using buffered std::istream would consume entropy which may + * not actually be used. Entropy is a precious good we avoid + * wasting. + */ + +#if defined(__GNUC__) && defined(_CXXRT_STD_NAME) +// I have severe difficulty to get the POSIX includes to work with +// -fhonor-std and Dietmar Kuhl's standard C++ library. Hack around that +// problem for now. +extern "C" { +static const int O_RDONLY = 0; +extern int open(const char *__file, int __oflag, ...); +extern int read(int __fd, __ptr_t __buf, size_t __nbytes); +extern int close(int __fd); +} +#else +#include +#include +#include // open +#include // read, close +#endif + +#include // errno +#include // strerror +#include // std::invalid_argument + + +class boost::random::random_device::impl +{ +public: + impl(const std::string & token) : path(token) { + fd = open(token.c_str(), O_RDONLY); + if(fd < 0) + error("cannot open"); + } + + ~impl() { if(close(fd) < 0) error("could not close"); } + + unsigned int next() { + unsigned int result; + long sz = read(fd, reinterpret_cast(&result), sizeof(result)); + if(sz == -1) + error("error while reading"); + else if(sz != sizeof(result)) { + errno = 0; + error("EOF while reading"); + } + return result; + } + +private: + void error(const std::string & msg) { + throw std::invalid_argument("boost::random_device: " + msg + + " random-number pseudo-device " + path + + ": " + strerror(errno)); + } + const std::string path; + int fd; +}; + +#endif // BOOST_WINDOWS + +BOOST_RANDOM_DECL boost::random::random_device::random_device() + : pimpl(new impl(default_token)) +{} + +BOOST_RANDOM_DECL boost::random::random_device::random_device(const std::string& token) + : pimpl(new impl(token)) +{} + +BOOST_RANDOM_DECL boost::random_device::~random_device() +{ + delete pimpl; +} + +BOOST_RANDOM_DECL double boost::random_device::entropy() const +{ + return 10; +} + +BOOST_RANDOM_DECL unsigned int boost::random_device::operator()() +{ + return pimpl->next(); +} From 30922740a1287230e79aaeb03d3166079fc321d9 Mon Sep 17 00:00:00 2001 From: don bright Date: Sun, 6 Jan 2013 17:03:37 +0100 Subject: [PATCH 3/7] remove boost::random_device, move min/max out of the random generator loop --- src/func.cc | 19 ++-- src/random_device.cpp | 217 ------------------------------------------ 2 files changed, 9 insertions(+), 227 deletions(-) delete mode 100644 src/random_device.cpp diff --git a/src/func.cc b/src/func.cc index a4d48e22..de9060d6 100644 --- a/src/func.cc +++ b/src/func.cc @@ -34,14 +34,13 @@ #include #include "stl-utils.h" #include "printutils.h" -#include #include #include -boost::random::random_device nondeterministic_rng; -boost::random::mt19937 deterministic_rng; - -#include "random_device.cpp" +boost::random::mt19937 deterministic_prng; +// not technically non-deterministic, but boost::random::random_device has +// non-header library and/or version issues that would complicate the build +boost::random::mt19937 nondeterministic_prng( std::time(0) ); AbstractFunction::~AbstractFunction() { @@ -161,15 +160,15 @@ Value builtin_rands(const Context *, const std::vector&, const std: return Value(); } + double min = std::min( args[0].toDouble(), args[1].toDouble() ); + double max = std::max( args[0].toDouble(), args[1].toDouble() ); + boost::random::uniform_real_distribution<> distributor( min, max ); Value::VectorType vec; for (int i=0; i dist( min, max ); if ( deterministic ) { - vec.push_back( Value( dist( deterministic_rng ) ) ); + vec.push_back( Value( distributor( deterministic_rng ) ) ); } else { - vec.push_back( Value( dist( nondeterministic_rng ) ) ); + vec.push_back( Value( distributor( nondeterministic_rng ) ) ); } } diff --git a/src/random_device.cpp b/src/random_device.cpp deleted file mode 100644 index 699a880f..00000000 --- a/src/random_device.cpp +++ /dev/null @@ -1,217 +0,0 @@ -/* boost random_device.cpp implementation - * - * Copyright Jens Maurer 2000 - * Copyright Steven Watanabe 2010-2011 - * Distributed under 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) - * - * $Id: random_device.cpp 71018 2011-04-05 21:27:52Z steven_watanabe $ - * - */ - -#define BOOST_RANDOM_SOURCE - -#include -#include -#include -#include -#include - -#if !defined(BOOST_NO_INCLASS_MEMBER_INITIALIZATION) && !BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1600)) -// A definition is required even for integral static constants -const bool boost::random::random_device::has_fixed_range; -#endif - - -#if defined(BOOST_WINDOWS) - -#include -#include -#include // std::invalid_argument - -#define BOOST_AUTO_LINK_NOMANGLE -#define BOOST_LIB_NAME "Advapi32" -#include - -#ifdef __MINGW32__ - -extern "C" { - -// mingw's wincrypt.h appears to be missing some things -WINADVAPI -BOOL -WINAPI -CryptEnumProvidersA( - DWORD dwIndex, - DWORD *pdwReserved, - DWORD dwFlags, - DWORD *pdwProvType, - LPSTR szProvName, - DWORD *pcbProvName - ); - -} - -#endif - -namespace { - -const char * const default_token = MS_DEF_PROV_A; - -} - -class boost::random::random_device::impl -{ -public: - impl(const std::string & token) : provider(token) { - char buffer[80]; - DWORD type; - DWORD len; - - // Find the type of the provider - for(DWORD i = 0; ; ++i) { - len = sizeof(buffer); - if(!CryptEnumProvidersA(i, NULL, 0, &type, buffer, &len)) { - error("Could not find provider name"); - } - if(buffer == provider) { - break; - } - } - - if(!CryptAcquireContextA(&hProv, NULL, provider.c_str(), type, - CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { - error("Could not acquire CSP context"); - } - } - - ~impl() { - if(!CryptReleaseContext(hProv, 0)) error("Could not release CSP context"); - } - - unsigned int next() { - unsigned int result; - - if(!CryptGenRandom(hProv, sizeof(result), - static_cast(static_cast(&result)))) { - error("error while reading"); - } - - return result; - } - -private: - void error(const std::string & msg) { - char buf[80]; - DWORD num = FormatMessageA( - FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - GetLastError(), - 0, - buf, - sizeof(buf), - NULL); - - throw std::invalid_argument("boost::random_device: " + msg + - " Cryptopraphic Service Provider " + provider + - ": " + std::string(&buf[0], &buf[0] + num)); - } - const std::string provider; - HCRYPTPROV hProv; -}; - -#else - -namespace { -// the default is the unlimited capacity device, using some secure hash -// try "/dev/random" for blocking when the entropy pool has drained -const char * const default_token = "/dev/urandom"; -} - -/* - * This uses the POSIX interface for unbuffered reading. - * Using buffered std::istream would consume entropy which may - * not actually be used. Entropy is a precious good we avoid - * wasting. - */ - -#if defined(__GNUC__) && defined(_CXXRT_STD_NAME) -// I have severe difficulty to get the POSIX includes to work with -// -fhonor-std and Dietmar Kuhl's standard C++ library. Hack around that -// problem for now. -extern "C" { -static const int O_RDONLY = 0; -extern int open(const char *__file, int __oflag, ...); -extern int read(int __fd, __ptr_t __buf, size_t __nbytes); -extern int close(int __fd); -} -#else -#include -#include -#include // open -#include // read, close -#endif - -#include // errno -#include // strerror -#include // std::invalid_argument - - -class boost::random::random_device::impl -{ -public: - impl(const std::string & token) : path(token) { - fd = open(token.c_str(), O_RDONLY); - if(fd < 0) - error("cannot open"); - } - - ~impl() { if(close(fd) < 0) error("could not close"); } - - unsigned int next() { - unsigned int result; - long sz = read(fd, reinterpret_cast(&result), sizeof(result)); - if(sz == -1) - error("error while reading"); - else if(sz != sizeof(result)) { - errno = 0; - error("EOF while reading"); - } - return result; - } - -private: - void error(const std::string & msg) { - throw std::invalid_argument("boost::random_device: " + msg + - " random-number pseudo-device " + path + - ": " + strerror(errno)); - } - const std::string path; - int fd; -}; - -#endif // BOOST_WINDOWS - -BOOST_RANDOM_DECL boost::random::random_device::random_device() - : pimpl(new impl(default_token)) -{} - -BOOST_RANDOM_DECL boost::random::random_device::random_device(const std::string& token) - : pimpl(new impl(token)) -{} - -BOOST_RANDOM_DECL boost::random_device::~random_device() -{ - delete pimpl; -} - -BOOST_RANDOM_DECL double boost::random_device::entropy() const -{ - return 10; -} - -BOOST_RANDOM_DECL unsigned int boost::random_device::operator()() -{ - return pimpl->next(); -} From db971485846d54a043777b3db7ba43f31d486939 Mon Sep 17 00:00:00 2001 From: don bright Date: Sun, 6 Jan 2013 10:09:21 -0600 Subject: [PATCH 4/7] fix typo --- src/func.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/func.cc b/src/func.cc index de9060d6..0f9329d3 100644 --- a/src/func.cc +++ b/src/func.cc @@ -37,10 +37,10 @@ #include #include -boost::random::mt19937 deterministic_prng; +boost::random::mt19937 deterministic_rng; // not technically non-deterministic, but boost::random::random_device has // non-header library and/or version issues that would complicate the build -boost::random::mt19937 nondeterministic_prng( std::time(0) ); +boost::random::mt19937 nondeterministic_rng( std::time(0) ); AbstractFunction::~AbstractFunction() { From 8aa349b15f65dd5106d182decd26c1027dcb7b7a Mon Sep 17 00:00:00 2001 From: don bright Date: Sun, 6 Jan 2013 17:35:31 +0100 Subject: [PATCH 5/7] also use process ID to seed pseudo random number generator --- src/func.cc | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/func.cc b/src/func.cc index 0f9329d3..d32b3823 100644 --- a/src/func.cc +++ b/src/func.cc @@ -37,10 +37,19 @@ #include #include +#ifdef __WIN32__ +#include +int process_id = _getpid(); +#else +#include +#include +int process_id = getpid(); +#endif + boost::random::mt19937 deterministic_rng; -// not technically non-deterministic, but boost::random::random_device has -// non-header library and/or version issues that would complicate the build -boost::random::mt19937 nondeterministic_rng( std::time(0) ); +// this is technically not non-deterministic, but boost::random::random_device +// has non-header library and/or version issues that would complicate the build +boost::random::mt19937 nondeterministic_rng( std::time(0) + process_id ); AbstractFunction::~AbstractFunction() { From 3825a7249944daa7765ebbdf836557c5032b2836 Mon Sep 17 00:00:00 2001 From: don bright Date: Sun, 6 Jan 2013 11:06:55 -0600 Subject: [PATCH 6/7] clarify that its not nondeterministic, we are supporting older systems where the C++/boost random_device stuff doesn't work. boost random_device in particular has a lot of issues with old versions not working, with -lboost_random not being installed by default, etc, that complicate the build too much. --- src/func.cc | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/func.cc b/src/func.cc index d32b3823..ee269d04 100644 --- a/src/func.cc +++ b/src/func.cc @@ -34,6 +34,14 @@ #include #include "stl-utils.h" #include "printutils.h" + +/* + Random numbers + + Newer versions of boost/C++ include a non-deterministic random_device and + auto/bind()s for random function objects, but we are supporting older systems. +*/ + #include #include @@ -47,9 +55,7 @@ int process_id = getpid(); #endif boost::random::mt19937 deterministic_rng; -// this is technically not non-deterministic, but boost::random::random_device -// has non-header library and/or version issues that would complicate the build -boost::random::mt19937 nondeterministic_rng( std::time(0) + process_id ); +boost::random::mt19937 lessdeterministic_rng( std::time(0) + process_id ); AbstractFunction::~AbstractFunction() { @@ -177,7 +183,7 @@ Value builtin_rands(const Context *, const std::vector&, const std: if ( deterministic ) { vec.push_back( Value( distributor( deterministic_rng ) ) ); } else { - vec.push_back( Value( distributor( nondeterministic_rng ) ) ); + vec.push_back( Value( distributor( lessdeterministic_rng ) ) ); } } From 00ac79c11155bd7fc5e3489b675d50dd027207ad Mon Sep 17 00:00:00 2001 From: don bright Date: Sun, 6 Jan 2013 18:12:00 +0100 Subject: [PATCH 7/7] remove unneeded frand functions --- src/func.cc | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/func.cc b/src/func.cc index ee269d04..5dcb3e97 100644 --- a/src/func.cc +++ b/src/func.cc @@ -141,16 +141,6 @@ Value builtin_sign(const Context *, const std::vector&, const std:: return Value(); } -double frand() -{ - return rand()/(double(RAND_MAX)+1); -} - -double frand(double min, double max) -{ - return (min>max) ? frand()*(min-max)+max : frand()*(max-min)+min; -} - Value builtin_rands(const Context *, const std::vector&, const std::vector &args) { bool deterministic = false;