svn commit: r357066 - head/lib/libc/stdlib
Conrad Meyer
cem at FreeBSD.org
Fri Jan 24 01:39:30 UTC 2020
Author: cem
Date: Fri Jan 24 01:39:29 2020
New Revision: 357066
URL: https://svnweb.freebsd.org/changeset/base/357066
Log:
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
Added:
head/lib/libc/stdlib/random.h (contents, props changed)
Modified:
head/lib/libc/stdlib/random.c
Modified: head/lib/libc/stdlib/random.c
==============================================================================
--- head/lib/libc/stdlib/random.c Fri Jan 24 01:32:16 2020 (r357065)
+++ head/lib/libc/stdlib/random.c Fri Jan 24 01:39:29 2020 (r357066)
@@ -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,25 +247,31 @@ 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);
+}
+
/*
* srandomdev:
*
@@ -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));
}
Added: head/lib/libc/stdlib/random.h
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ head/lib/libc/stdlib/random.h Fri Jan 24 01:39:29 2020 (r357066)
@@ -0,0 +1,46 @@
+/*-
+ * Copyright 2020 Conrad Meyer <cem at 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 *);
More information about the svn-src-all
mailing list