random(3): Abstract routines into _r versions on explicit state
The existing APIs simply pass the implicit global state to the _r variants. No functional change. Note that these routines are not exported from libc and are not intended to be exported. If someone wished to export them from libc (which I would discourage), they should first be modified to match the inconsistent parameter type / order of the glibc public interfaces of the same names. I know Ravi will ask, so: the eventual goal of this series is to replace rand(3) with the implementation from random(3) (D23290). However, I'd like to wait a bit longer on that one to see if more feedback emerges. Reviewed by: kevans, markm Differential Revision: https://reviews.freebsd.org/D23289
This commit is contained in:
parent
7382fafe45
commit
510b01833e
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=357066
@ -38,10 +38,13 @@ __FBSDID("$FreeBSD$");
|
||||
#include "namespace.h"
|
||||
#include <sys/param.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "un-namespace.h"
|
||||
|
||||
#include "random.h"
|
||||
|
||||
/*
|
||||
* random.c:
|
||||
*
|
||||
@ -144,19 +147,6 @@ __FBSDID("$FreeBSD$");
|
||||
static const int degrees[MAX_TYPES] = { DEG_0, DEG_1, DEG_2, DEG_3, DEG_4 };
|
||||
static const int seps [MAX_TYPES] = { SEP_0, SEP_1, SEP_2, SEP_3, SEP_4 };
|
||||
|
||||
/* A full instance of the random(3) generator. */
|
||||
struct random_state {
|
||||
uint32_t *rst_fptr;
|
||||
uint32_t *rst_rptr;
|
||||
uint32_t *rst_state;
|
||||
int rst_type;
|
||||
int rst_deg;
|
||||
int rst_sep;
|
||||
uint32_t *rst_end_ptr;
|
||||
/* Flexible array member must be last. */
|
||||
uint32_t rst_randtbl[];
|
||||
};
|
||||
|
||||
/*
|
||||
* Initially, everything is set up as if from:
|
||||
*
|
||||
@ -170,7 +160,7 @@ struct random_state {
|
||||
*
|
||||
* MAX_TYPES * (rptr - state) + TYPE_3 == TYPE_3.
|
||||
*/
|
||||
static struct random_state implicit = {
|
||||
static struct __random_state implicit = {
|
||||
.rst_randtbl = {
|
||||
TYPE_3,
|
||||
0x2cf41758, 0x27bb3711, 0x4916d4d1, 0x7b02f59f, 0x9b8e28eb, 0xc0e80269,
|
||||
@ -257,23 +247,29 @@ parkmiller32(uint32_t ctx)
|
||||
* for default usage relies on values produced by this routine.
|
||||
*/
|
||||
void
|
||||
srandom(unsigned int x)
|
||||
srandom_r(struct __random_state *estate, unsigned x)
|
||||
{
|
||||
int i, lim;
|
||||
|
||||
implicit.rst_state[0] = (uint32_t)x;
|
||||
if (implicit.rst_type == TYPE_0)
|
||||
estate->rst_state[0] = (uint32_t)x;
|
||||
if (estate->rst_type == TYPE_0)
|
||||
lim = NSHUFF;
|
||||
else {
|
||||
for (i = 1; i < implicit.rst_deg; i++)
|
||||
implicit.rst_state[i] =
|
||||
parkmiller32(implicit.rst_state[i - 1]);
|
||||
implicit.rst_fptr = &implicit.rst_state[implicit.rst_sep];
|
||||
implicit.rst_rptr = &implicit.rst_state[0];
|
||||
lim = 10 * implicit.rst_deg;
|
||||
for (i = 1; i < estate->rst_deg; i++)
|
||||
estate->rst_state[i] =
|
||||
parkmiller32(estate->rst_state[i - 1]);
|
||||
estate->rst_fptr = &estate->rst_state[estate->rst_sep];
|
||||
estate->rst_rptr = &estate->rst_state[0];
|
||||
lim = 10 * estate->rst_deg;
|
||||
}
|
||||
for (i = 0; i < lim; i++)
|
||||
(void)random();
|
||||
(void)random_r(estate);
|
||||
}
|
||||
|
||||
void
|
||||
srandom(unsigned x)
|
||||
{
|
||||
srandom_r(&implicit, x);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -289,20 +285,20 @@ srandom(unsigned int x)
|
||||
* derived from the LC algorithm applied to a fixed seed.
|
||||
*/
|
||||
void
|
||||
srandomdev(void)
|
||||
srandomdev_r(struct __random_state *estate)
|
||||
{
|
||||
int mib[2];
|
||||
size_t expected, len;
|
||||
|
||||
if (implicit.rst_type == TYPE_0)
|
||||
len = sizeof(implicit.rst_state[0]);
|
||||
if (estate->rst_type == TYPE_0)
|
||||
len = sizeof(estate->rst_state[0]);
|
||||
else
|
||||
len = implicit.rst_deg * sizeof(implicit.rst_state[0]);
|
||||
len = estate->rst_deg * sizeof(estate->rst_state[0]);
|
||||
expected = len;
|
||||
|
||||
mib[0] = CTL_KERN;
|
||||
mib[1] = KERN_ARND;
|
||||
if (sysctl(mib, 2, implicit.rst_state, &len, NULL, 0) == -1 ||
|
||||
if (sysctl(mib, 2, estate->rst_state, &len, NULL, 0) == -1 ||
|
||||
len != expected) {
|
||||
/*
|
||||
* The sysctl cannot fail. If it does fail on some FreeBSD
|
||||
@ -313,14 +309,20 @@ srandomdev(void)
|
||||
abort();
|
||||
}
|
||||
|
||||
if (implicit.rst_type != TYPE_0) {
|
||||
implicit.rst_fptr = &implicit.rst_state[implicit.rst_sep];
|
||||
implicit.rst_rptr = &implicit.rst_state[0];
|
||||
if (estate->rst_type != TYPE_0) {
|
||||
estate->rst_fptr = &estate->rst_state[estate->rst_sep];
|
||||
estate->rst_rptr = &estate->rst_state[0];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
srandomdev(void)
|
||||
{
|
||||
srandomdev_r(&implicit);
|
||||
}
|
||||
|
||||
/*
|
||||
* initstate:
|
||||
* initstate_r:
|
||||
*
|
||||
* Initialize the state information in the given array of n bytes for future
|
||||
* random number generation. Based on the number of bytes we are given, and
|
||||
@ -328,65 +330,95 @@ srandomdev(void)
|
||||
* one we can and set things up for it. srandom() is then called to
|
||||
* initialize the state information.
|
||||
*
|
||||
* Note that on return from srandom(), we set state[-1] to be the type
|
||||
* multiplexed with the current value of the rear pointer; this is so
|
||||
* successive calls to initstate() won't lose this information and will be
|
||||
* able to restart with setstate().
|
||||
* Returns zero on success, or an error number on failure.
|
||||
*
|
||||
* Note: There is no need for a setstate_r(); just use a new context.
|
||||
*/
|
||||
int
|
||||
initstate_r(struct __random_state *estate, unsigned seed, uint32_t *arg_state,
|
||||
size_t sz)
|
||||
{
|
||||
if (sz < BREAK_0)
|
||||
return (EINVAL);
|
||||
|
||||
if (sz < BREAK_1) {
|
||||
estate->rst_type = TYPE_0;
|
||||
estate->rst_deg = DEG_0;
|
||||
estate->rst_sep = SEP_0;
|
||||
} else if (sz < BREAK_2) {
|
||||
estate->rst_type = TYPE_1;
|
||||
estate->rst_deg = DEG_1;
|
||||
estate->rst_sep = SEP_1;
|
||||
} else if (sz < BREAK_3) {
|
||||
estate->rst_type = TYPE_2;
|
||||
estate->rst_deg = DEG_2;
|
||||
estate->rst_sep = SEP_2;
|
||||
} else if (sz < BREAK_4) {
|
||||
estate->rst_type = TYPE_3;
|
||||
estate->rst_deg = DEG_3;
|
||||
estate->rst_sep = SEP_3;
|
||||
} else {
|
||||
estate->rst_type = TYPE_4;
|
||||
estate->rst_deg = DEG_4;
|
||||
estate->rst_sep = SEP_4;
|
||||
}
|
||||
estate->rst_state = arg_state + 1;
|
||||
estate->rst_end_ptr = &estate->rst_state[estate->rst_deg];
|
||||
srandom_r(estate, seed);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* initstate:
|
||||
*
|
||||
* Note: the first thing we do is save the current state, if any, just like
|
||||
* setstate() so that it doesn't matter when initstate is called.
|
||||
*
|
||||
* Note that on return from initstate_r(), we set state[-1] to be the type
|
||||
* multiplexed with the current value of the rear pointer; this is so
|
||||
* successive calls to initstate() won't lose this information and will be able
|
||||
* to restart with setstate().
|
||||
*
|
||||
* Returns a pointer to the old state.
|
||||
*
|
||||
* Note: The Sparc platform requires that arg_state begin on an int
|
||||
* word boundary; otherwise a bus error will occur. Even so, lint will
|
||||
* complain about mis-alignment, but you should disregard these messages.
|
||||
* Despite the misleading "char *" type, arg_state must alias an array of
|
||||
* 32-bit unsigned integer values. Naturally, such an array is 32-bit aligned.
|
||||
* Usually objects are naturally aligned to at least 32-bits on all platforms,
|
||||
* but if you treat the provided 'state' as char* you may inadvertently
|
||||
* misalign it. Don't do that.
|
||||
*/
|
||||
char *
|
||||
initstate(unsigned int seed, char *arg_state, size_t n)
|
||||
{
|
||||
char *ostate = (char *)(&implicit.rst_state[-1]);
|
||||
uint32_t *int_arg_state = (uint32_t *)arg_state;
|
||||
int error;
|
||||
|
||||
if (n < BREAK_0)
|
||||
return (NULL);
|
||||
/*
|
||||
* Persist rptr offset and rst_type in the first word of the prior
|
||||
* state we are replacing.
|
||||
*/
|
||||
if (implicit.rst_type == TYPE_0)
|
||||
implicit.rst_state[-1] = implicit.rst_type;
|
||||
else
|
||||
implicit.rst_state[-1] = MAX_TYPES *
|
||||
(implicit.rst_rptr - implicit.rst_state) +
|
||||
implicit.rst_type;
|
||||
if (n < BREAK_1) {
|
||||
implicit.rst_type = TYPE_0;
|
||||
implicit.rst_deg = DEG_0;
|
||||
implicit.rst_sep = SEP_0;
|
||||
} else if (n < BREAK_2) {
|
||||
implicit.rst_type = TYPE_1;
|
||||
implicit.rst_deg = DEG_1;
|
||||
implicit.rst_sep = SEP_1;
|
||||
} else if (n < BREAK_3) {
|
||||
implicit.rst_type = TYPE_2;
|
||||
implicit.rst_deg = DEG_2;
|
||||
implicit.rst_sep = SEP_2;
|
||||
} else if (n < BREAK_4) {
|
||||
implicit.rst_type = TYPE_3;
|
||||
implicit.rst_deg = DEG_3;
|
||||
implicit.rst_sep = SEP_3;
|
||||
} else {
|
||||
implicit.rst_type = TYPE_4;
|
||||
implicit.rst_deg = DEG_4;
|
||||
implicit.rst_sep = SEP_4;
|
||||
}
|
||||
implicit.rst_state = int_arg_state + 1; /* first location */
|
||||
/* must set end_ptr before srandom */
|
||||
implicit.rst_end_ptr = &implicit.rst_state[implicit.rst_deg];
|
||||
srandom(seed);
|
||||
|
||||
error = initstate_r(&implicit, seed, int_arg_state, n);
|
||||
if (error != 0)
|
||||
return (NULL);
|
||||
|
||||
/*
|
||||
* Persist rptr offset and rst_type of the new state in its first word.
|
||||
*/
|
||||
if (implicit.rst_type == TYPE_0)
|
||||
int_arg_state[0] = implicit.rst_type;
|
||||
else
|
||||
int_arg_state[0] = MAX_TYPES *
|
||||
(implicit.rst_rptr - implicit.rst_state) +
|
||||
implicit.rst_type;
|
||||
|
||||
return (ostate);
|
||||
}
|
||||
|
||||
@ -456,33 +488,39 @@ setstate(char *arg_state)
|
||||
* Returns a 31-bit random number.
|
||||
*/
|
||||
long
|
||||
random(void)
|
||||
random_r(struct __random_state *estate)
|
||||
{
|
||||
uint32_t i;
|
||||
uint32_t *f, *r;
|
||||
|
||||
if (implicit.rst_type == TYPE_0) {
|
||||
i = implicit.rst_state[0];
|
||||
if (estate->rst_type == TYPE_0) {
|
||||
i = estate->rst_state[0];
|
||||
i = parkmiller32(i);
|
||||
implicit.rst_state[0] = i;
|
||||
estate->rst_state[0] = i;
|
||||
} else {
|
||||
/*
|
||||
* Use local variables rather than static variables for speed.
|
||||
*/
|
||||
f = implicit.rst_fptr;
|
||||
r = implicit.rst_rptr;
|
||||
f = estate->rst_fptr;
|
||||
r = estate->rst_rptr;
|
||||
*f += *r;
|
||||
i = *f >> 1; /* chucking least random bit */
|
||||
if (++f >= implicit.rst_end_ptr) {
|
||||
f = implicit.rst_state;
|
||||
if (++f >= estate->rst_end_ptr) {
|
||||
f = estate->rst_state;
|
||||
++r;
|
||||
}
|
||||
else if (++r >= implicit.rst_end_ptr) {
|
||||
r = implicit.rst_state;
|
||||
else if (++r >= estate->rst_end_ptr) {
|
||||
r = estate->rst_state;
|
||||
}
|
||||
|
||||
implicit.rst_fptr = f;
|
||||
implicit.rst_rptr = r;
|
||||
estate->rst_fptr = f;
|
||||
estate->rst_rptr = r;
|
||||
}
|
||||
return ((long)i);
|
||||
}
|
||||
|
||||
long
|
||||
random(void)
|
||||
{
|
||||
return (random_r(&implicit));
|
||||
}
|
||||
|
46
lib/libc/stdlib/random.h
Normal file
46
lib/libc/stdlib/random.h
Normal file
@ -0,0 +1,46 @@
|
||||
/*-
|
||||
* Copyright 2020 Conrad Meyer <cem@FreeBSD.org>. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* A full instance of the random(3) generator. */
|
||||
struct __random_state {
|
||||
uint32_t *rst_fptr;
|
||||
uint32_t *rst_rptr;
|
||||
uint32_t *rst_state;
|
||||
int rst_type;
|
||||
int rst_deg;
|
||||
int rst_sep;
|
||||
uint32_t *rst_end_ptr;
|
||||
/* Flexible array member must be last. */
|
||||
uint32_t rst_randtbl[];
|
||||
};
|
||||
|
||||
int initstate_r(struct __random_state *, unsigned, uint32_t *, size_t);
|
||||
long random_r(struct __random_state *);
|
||||
void srandom_r(struct __random_state *, unsigned);
|
||||
void srandomdev_r(struct __random_state *);
|
Loading…
Reference in New Issue
Block a user