star-sp

Random number generators and distributions
git clone git://git.meso-star.fr/star-sp.git
Log | Files | Refs | README | LICENSE

commit 6d0f65e757e3840588b0de5175400993ccc857f2
parent 1cfc01ff3b8b1dd26f98fa268fba021b1f9370a4
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Tue,  1 Dec 2020 11:53:59 +0100

Add and test the ssp_rng_<read|write>_cstr functions

[De]serialize the RNG state into a C string, i.e. a null terminated
buffer of bytes.

Diffstat:
Msrc/ssp.h | 20+++++++++++++++++---
Msrc/ssp_rng.c | 125+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Msrc/ssp_rng_proxy.c | 20++++++++++++++++++++
Msrc/test_ssp_rng.c | 38++++++++++++++++++++++++++++++++++++--
4 files changed, 186 insertions(+), 17 deletions(-)

diff --git a/src/ssp.h b/src/ssp.h @@ -76,7 +76,9 @@ struct ssp_rng_type { uint64_t (*get)(void* state); res_T (*discard)(void* state, uint64_t n); res_T (*read)(void* state, FILE* file); + res_T (*read_cstr)(void* state, const char* cstr); res_T (*write)(const void* state, FILE* file); + res_T (*write_cstr)(const void* state, char* buf, const size_t sz, size_t* len); double (*entropy)(const void* state); uint64_t min; @@ -202,15 +204,27 @@ ssp_rng_max (struct ssp_rng* rng); SSP_API res_T -ssp_rng_write - (const struct ssp_rng* rng, +ssp_rng_read + (struct ssp_rng* rng, FILE* stream); SSP_API res_T -ssp_rng_read +ssp_rng_read_cstr (struct ssp_rng* rng, + const char* cstr); /* Null terminated string */ + +SSP_API res_T +ssp_rng_write + (const struct ssp_rng* rng, FILE* stream); +SSP_API res_T +ssp_rng_write_cstr + (const struct ssp_rng* rng, + char* buf, /* May be NULL */ + const size_t bufsz, /* buf capacity */ + size_t* len); /* May be NULL. #chars to write into buf without null char */ + SSP_API double ssp_rng_entropy (const struct ssp_rng* rng); diff --git a/src/ssp_rng.c b/src/ssp_rng.c @@ -107,19 +107,19 @@ rng_kiss_read(void* data, FILE* file) { struct rng_kiss* rng = (struct rng_kiss*)data; int n; - res_T res = RES_OK; ASSERT(data && file); + n = fscanf(file, "%u %u %u %u\n", &rng->x, &rng->y, &rng->z, &rng->c); + return (n == EOF || n < 4) ? RES_IO_ERR : RES_OK; +} - n = fscanf(file, "%u %u %u %u", &rng->x, &rng->y, &rng->z, &rng->c); - if(n == EOF || n < 4) { - res = RES_IO_ERR; - goto error; - } - -exit: - return res; -error: - goto exit; +static res_T +rng_kiss_read_cstr(void* data, const char* cstr) +{ + struct rng_kiss* rng = (struct rng_kiss*)data; + int n; + ASSERT(data && cstr); + n = sscanf(cstr, "%u %u %u %u\n", &rng->x, &rng->y, &rng->z, &rng->c); + return (n == EOF || n < 4) ? RES_IO_ERR : RES_OK; } static res_T @@ -133,6 +133,26 @@ rng_kiss_write(const void* data, FILE* file) } static res_T +rng_kiss_write_cstr + (const void* data, + char* buf, + const size_t bufsz, + size_t* out_len) +{ + const struct rng_kiss* rng = (const struct rng_kiss*)data; + int len = 0; + ASSERT(data); + + len = snprintf(buf, bufsz, "%u %u %u %u\n", rng->x, rng->y, rng->z, rng->c); + CHK(len > 0); + if((size_t)len >= (bufsz - 1/*null char*/)) { + buf[bufsz-1] = '\0'; + } + if(out_len) *out_len = (size_t)len; + return RES_OK; +} + +static res_T rng_kiss_init(struct mem_allocator* allocator, void* data) { (void)allocator; @@ -170,7 +190,9 @@ const struct ssp_rng_type ssp_rng_kiss = { rng_kiss_get, rng_kiss_discard, rng_kiss_read, + rng_kiss_read_cstr, rng_kiss_write, + rng_kiss_write_cstr, rng_kiss_entropy, 0, UINT32_MAX, @@ -231,6 +253,37 @@ rng_cxx_write<RAN_NAMESPACE::random_device>(const void* data, FILE* file) template<typename RNG> static res_T +rng_cxx_write_cstr + (const void* data, + char* buf, + const size_t bufsz, + size_t* out_len) +{ + int len = 0; + std::stringstream stream; + RNG* rng = (RNG*)data; + ASSERT(rng); + stream << *rng << std::endl; + len = snprintf(buf, bufsz, "%s", stream.str().c_str()); + CHK(len > 0); + if((size_t)len >= (bufsz - 1/*null char*/)) { + buf[bufsz-1] = '\0'; + } + if(out_len) *out_len = (size_t)len; + return RES_OK; +} + +template<> +res_T +rng_cxx_write_cstr<RAN_NAMESPACE::random_device> + (const void* data, char* buf, const size_t bufsz, size_t* out_len) +{ + (void)data; (void)buf, (void)bufsz, (void)out_len; + return RES_BAD_OP; +} + +template<typename RNG> +static res_T rng_cxx_read(void* data, FILE* file) { std::stringstream stream; @@ -250,7 +303,27 @@ template<> res_T rng_cxx_read<RAN_NAMESPACE::random_device>(void* data, FILE* file) { - (void) data; (void) file; + (void) data; (void)file; + return RES_BAD_OP; +} + +template<typename RNG> +static res_T +rng_cxx_read_cstr(void* data, const char* cstr) +{ + std::stringstream stream; + RNG* rng = (RNG*)data; + ASSERT(rng && cstr && cstr[strlen(cstr)-1] == '\n'); + stream << std::string(cstr); + stream >> *rng; + return stream.fail() ? RES_IO_ERR : RES_OK; +} + +template<> +res_T +rng_cxx_read_cstr<RAN_NAMESPACE::random_device>(void* data, const char* cstr) +{ + (void)data, (void)cstr; return RES_BAD_OP; } @@ -318,7 +391,9 @@ const struct ssp_rng_type ssp_rng_mt19937_64 = { rng_cxx_get<RAN_NAMESPACE::mt19937_64>, rng_cxx_discard<RAN_NAMESPACE::mt19937_64>, rng_cxx_read<RAN_NAMESPACE::mt19937_64>, + rng_cxx_read_cstr<RAN_NAMESPACE::mt19937_64>, rng_cxx_write<RAN_NAMESPACE::mt19937_64>, + rng_cxx_write_cstr<RAN_NAMESPACE::mt19937_64>, rng_cxx_entropy<RAN_NAMESPACE::mt19937_64>, RAN_NAMESPACE::mt19937_64::min(), RAN_NAMESPACE::mt19937_64::max(), @@ -334,7 +409,9 @@ const struct ssp_rng_type ssp_rng_ranlux48 = { rng_cxx_get<RAN_NAMESPACE::ranlux48>, rng_cxx_discard<RAN_NAMESPACE::ranlux48>, rng_cxx_read<RAN_NAMESPACE::ranlux48>, + rng_cxx_read_cstr<RAN_NAMESPACE::ranlux48>, rng_cxx_write<RAN_NAMESPACE::ranlux48>, + rng_cxx_write_cstr<RAN_NAMESPACE::ranlux48>, rng_cxx_entropy<RAN_NAMESPACE::ranlux48>, RAN_NAMESPACE::ranlux48::min(), RAN_NAMESPACE::ranlux48::max(), @@ -350,7 +427,9 @@ const struct ssp_rng_type ssp_rng_random_device = { rng_cxx_get<RAN_NAMESPACE::random_device>, rng_cxx_discard<RAN_NAMESPACE::random_device>, rng_cxx_read<RAN_NAMESPACE::random_device>, + rng_cxx_read_cstr<RAN_NAMESPACE::random_device>, rng_cxx_write<RAN_NAMESPACE::random_device>, + rng_cxx_write_cstr<RAN_NAMESPACE::random_device>, rng_cxx_entropy<RAN_NAMESPACE::random_device>, RAN_NAMESPACE::random_device::min(), RAN_NAMESPACE::random_device::max(), @@ -390,7 +469,9 @@ const struct ssp_rng_type ssp_rng_threefry = { rng_cxx_get<threefry_T>, rng_cxx_discard<threefry_T>, rng_cxx_read<threefry_T>, + rng_cxx_read_cstr<threefry_T>, rng_cxx_write<threefry_T>, + rng_cxx_write_cstr<threefry_T>, rng_cxx_entropy<threefry_T>, threefry_T::min(), threefry_T::max(), @@ -407,7 +488,9 @@ const struct ssp_rng_type ssp_rng_aes = { rng_cxx_get<aes_T>, rng_cxx_discard<aes_T>, rng_cxx_read<aes_T>, + rng_cxx_read_cstr<aes_T>, rng_cxx_write<aes_T>, + rng_cxx_write_cstr<aes_T>, rng_cxx_entropy<aes_T>, aes_T::min(), aes_T::max(), @@ -643,12 +726,30 @@ ssp_rng_read(struct ssp_rng* rng, FILE* stream) } res_T +ssp_rng_read_cstr(struct ssp_rng* rng, const char* cstr) +{ + if(!rng || !cstr) return RES_BAD_ARG; + return rng->type.read_cstr(rng->state, cstr); +} + +res_T ssp_rng_write(const struct ssp_rng* rng, FILE* stream) { if(!rng || !stream) return RES_BAD_ARG; return rng->type.write(rng->state, stream); } +res_T +ssp_rng_write_cstr + (const struct ssp_rng* rng, + char* buf, + const size_t bufsz, + size_t* len) +{ + if(!rng) return RES_BAD_ARG; + return rng->type.write_cstr(rng->state, buf, bufsz, len); +} + double ssp_rng_entropy(const struct ssp_rng* rng) { diff --git a/src/ssp_rng_proxy.c b/src/ssp_rng_proxy.c @@ -224,6 +224,13 @@ rng_bucket_read(void* data, FILE* file) } static res_T +rng_bucket_read_cstr(void* data, const char* cstr) +{ + (void)data, (void)cstr; + return RES_BAD_OP; +} + +static res_T rng_bucket_write(const void* data, FILE* file) { (void)data, (void)file; @@ -231,6 +238,17 @@ rng_bucket_write(const void* data, FILE* file) } static res_T +rng_bucket_write_cstr + (const void* data, + char* buf, + const size_t bufsz, + size_t* len) +{ + (void)data, (void)buf, (void)bufsz, (void)len; + return RES_BAD_OP; +} + +static res_T rng_bucket_init(struct mem_allocator* allocator, void* data) { struct rng_bucket* rng = (struct rng_bucket*)data; @@ -280,7 +298,9 @@ static const struct ssp_rng_type RNG_BUCKET_NULL = { rng_bucket_get, rng_bucket_discard, rng_bucket_read, + rng_bucket_read_cstr, rng_bucket_write, + rng_bucket_write_cstr, rng_bucket_entropy, INT_MAX, /* Min dummy value */ 0, /* Max dummy value */ diff --git a/src/test_ssp_rng.c b/src/test_ssp_rng.c @@ -51,7 +51,9 @@ test_rng(const struct ssp_rng_type* type) uint64_t datai1[NRAND]; double datad[NRAND]; float dataf[NRAND]; + size_t len; char buf[512]; + char* cstr = NULL; int i, j; res_T r; const char can_set = (type != &ssp_rng_random_device); @@ -180,7 +182,7 @@ test_rng(const struct ssp_rng_type* type) time_dump(&t0, TIME_SEC|TIME_MSEC|TIME_USEC, NULL, buf, sizeof(buf)); printf("1,000,000 random numbers in %s\n", buf); - if (can_have_entropy) { + if(can_have_entropy) { printf("Entropy for this implementation and system: %f\n", ssp_rng_entropy(rng)); } @@ -192,6 +194,15 @@ test_rng(const struct ssp_rng_type* type) CHK(ssp_rng_write(NULL, stream) == RES_BAD_ARG); CHK(ssp_rng_write(rng, stream) == (can_rw ? RES_OK : RES_BAD_OP)); + CHK(ssp_rng_write_cstr(NULL, NULL, 0, &len) == RES_BAD_ARG); + CHK(ssp_rng_write_cstr(rng, NULL, 0, NULL) == (can_rw ? RES_OK : RES_BAD_OP)); + CHK(ssp_rng_write_cstr(rng, NULL, 0, &len) == (can_rw ? RES_OK : RES_BAD_OP)); + cstr = mem_calloc(len+1, 1); + CHK(ssp_rng_write_cstr(rng, cstr, len+1, NULL) == (can_rw ? RES_OK : RES_BAD_OP)); + + /* Reserialize the RNG state */ + CHK(ssp_rng_write(rng, stream) == (can_rw ? RES_OK : RES_BAD_OP)); + FOR_EACH(i, 0, NRAND) datai0[i] = ssp_rng_get(rng); @@ -200,14 +211,37 @@ test_rng(const struct ssp_rng_type* type) CHK(ssp_rng_read(NULL, stream) == RES_BAD_ARG); CHK(ssp_rng_read(rng, stream) == (can_rw ? RES_IO_ERR : RES_BAD_OP)); rewind(stream); + + /* Read the first state of the stream */ CHK(ssp_rng_read(rng, stream) == (can_rw ? RES_OK : RES_BAD_OP)); + if(can_rw) { + FOR_EACH(i, 0, NRAND) { + uint64_t rn = ssp_rng_get(rng); + CHK(rn == datai0[i]); + } + } + + /* Read the second state of the stream */ + CHK(ssp_rng_read(rng, stream) == (can_rw ? RES_OK : RES_BAD_OP)); + if(can_rw) { + FOR_EACH(i, 0, NRAND) { + uint64_t rn = ssp_rng_get(rng); + CHK(rn == datai0[i]); + } + } - if (can_rw) { + /* Read the RNG state from an in memory buffer */ + CHK(ssp_rng_read_cstr(NULL, cstr) == RES_BAD_ARG); + CHK(ssp_rng_read_cstr(rng, NULL) == RES_BAD_ARG); + CHK(ssp_rng_read_cstr(rng, cstr) == (can_rw ? RES_OK : RES_BAD_OP)); + if(can_rw) { FOR_EACH(i, 0, NRAND) { uint64_t rn = ssp_rng_get(rng); CHK(rn == datai0[i]); } } + + mem_rm(cstr); fclose(stream); CHK(ssp_rng_ref_put(rng) == RES_OK);