Re: The POSIX random-number generator
Keith Marshall <[email protected]> Wed, 7 Apr 2021 21:54:28 +0100
| Newsgroups | gmane.comp.gnu.mingw.user |
|---|---|
| Organization | MinGW.org Project |
| Message-ID | <[email protected]> |
This is a multi-part message in MIME format. --------------562AFB5433C889B886E770E9 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit On 02/04/2021 22:36, Keith Marshall wrote: > On 28/03/2021 15:43, Anton Shepelev wrote: >> I cannot build the last version of the Netpbm package because it now >> depends on the random() and srandom() functions from POSIX:> >> https://manned.org/random.3 >> >> Will the maintainers consider providing them in MinGW to simplify the >> building of code that depends on basic POSIX facilities? > > Sorry, but my initial inclination is to say no ... Notwithstanding my reluctance to support this, FWIW I did find https://www.mathstat.dal.ca/~selinger/random/ from which, with hints from the GNULIB random_r.c implementation[1], I've derived the attached patch. While I still will not attest to the statistical reliability of this, if you would care to submit a formal feature request[2], and there is sufficient interest expressed, (by way of up-votes), I may consider incorporation into libmingwex.a, subject to a requirement for the caller to define either _XOPEN_SOURCE >= 500, or _BSD_SOURCE, before including <stdlib.h>, to expose the API declarations. [1] https://git.savannah.gnu.org/cgit/gnulib.git/tree/lib/random_r.c Although GNULIB modules are normally licensed under LGPL, (which is NOT compatible with the licence requirements for libmingwrt.a), this file also exhibits a BSD licence, (which IS compatible). Thus, I believe that I am justified in incorporating hints as to suitable constant definitions, under the terms of the BSD licence. [2] https://mingw.osdn.io/index.html?page=contact.html#feature-request -- Regards, Keith. Public key available from keys.gnupg.net Key fingerprint: C19E C018 1547 DE50 E1D4 8F53 C0AD 36C6 347E 5A3F --------------562AFB5433C889B886E770E9 Content-Type: text/x-patch; charset=UTF-8; name="posix-prng-implementation.patch" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="posix-prng-implementation.patch" # HG changeset patch # Parent dedaf4b5585b853b055f333d18a1d85fbacf6c5f Implement a POSIX.1 compliant pseudo-random number generator. * mingwex/math/random.c: New file; it implements... (__mingw_random, __mingw_srandom, __mingw_initstate) (__mingw_setstate): ...each of these API functions. * include/stdlib.h [_XOPEN_SOURCE >=3D 500]: Declare them; map... (random, srandom, initstate, setstate): ...these aliases. [_BSD_SOURCE]: Likewise. * Makefile.in (libmingwex.a): Require random.$OBJEXT diff --git a/mingwrt/Makefile.in b/mingwrt/Makefile.in --- a/mingwrt/Makefile.in +++ b/mingwrt/Makefile.in @@ -4,12 +4,12 @@ # Makefile template for MinGW.org Runtime Library Package =20 PACKAGE_TARNAME :=3D @PACKAGE_TARNAME@ PACKAGE_VERSION :=3D @PACKAGE_VERSION@ =20 -# Written by Keith Marshall <[email protected]> -# Copyright (C) 2014-2020, MinGW.org Project +# Written by Keith Marshall <[email protected]> +# Copyright (C) 2014-2021, MinGW.org Project # # # Permission is hereby granted, free of charge, to any person obtaining = a # copy of this software and associated documentation files (the "Softwar= e"), # to deal in the Software without restriction, including without limitat= ion @@ -420,10 +420,15 @@ libmingwex.a: $(addsuffix .$(OBJEXT), co powf powl powi powif powil remainder remainderf remainderl remquo remq= uof \ remquol rint rintf rintl round roundf roundl scalbn scalbnf scalbnl si= gnbit \ signbitf signbitl sqrtf sqrtl tgamma tgammaf tgammal trunc truncf trun= cl \ x87cvt x87cvtf x87log x87log1p x87pow x87remquo) =20 +# An experimental implementation of the POSIX.1-1990 random() API; +# once again, this is delivered in libmingwex.a +# +libmingwex.a: random.$(OBJEXT) + # Replacement I/O functions in libmingwex.a, providing better POSIX # compatibility than their Microsoft equivalents. # vpath %.c ${mingwrt_srcdir}/mingwex/stdio libmingwex.a: $(addsuffix .$(OBJEXT), btowc fprintf fseeki64 ftelli64 \ diff --git a/mingwrt/include/stdlib.h b/mingwrt/include/stdlib.h --- a/mingwrt/include/stdlib.h +++ b/mingwrt/include/stdlib.h @@ -5,11 +5,12 @@ * associated macros, and manifest constant definitions. * * $Id$ * * Written by Colin Peters <[email protected]> - * Copyright (C) 1997-2009, 2011, 2014-2016, 2018, 2020, MinGW.org Proje= ct. + * Copyright (C) 1997-2009, 2011, 2014-2016, 2018, 2020, 2021, + * MinGW.org Project. * * * Permission is hereby granted, free of charge, to any person obtaining=20 a * copy of this software and associated documentation files (the "Softwa= re"), * to deal in the Software without restriction, including without limita= tion @@ -539,10 +540,37 @@ wchar_t *_wfullpath (wchar_t *, const wc _CRTIMP __cdecl __MINGW_NOTHROW int rand_s (unsigned int *); =20 #endif /* Win-Vista || MSVCR80.DLL || later */ #endif /* _CRT_RAND_S enabled */ =20 +#if _XOPEN_SOURCE >=3D 500 || defined _BSD_SOURCE +/* The POSIX.1-1990 Extended Systems Interface specification defines ano= ther + * alternative to rand(). While the statistical quality of this alterna= tive + * cannot be assured, this implementation in libmingwex.a may be useful.= + */ +__cdecl __MINGW_NOTHROW long __mingw_random (void); +__cdecl __MINGW_NOTHROW void __mingw_srandom (unsigned int); +__cdecl __MINGW_NOTHROW char *__mingw_initstate (unsigned int, char *, = size_t); +__cdecl __MINGW_NOTHROW char *__mingw_setstate (char *); + +__CRT_ALIAS __JMPSTUB__(( FUNCTION =3D random )) +__cdecl __MINGW_NOTHROW long random (void){ return __mingw_random(); } + +__CRT_ALIAS __JMPSTUB__(( FUNCTION =3D srandom )) +__cdecl __MINGW_NOTHROW void srandom (unsigned int __seed) +{ __mingw_srandom (__seed); } + +__CRT_ALIAS __JMPSTUB__(( FUNCTION =3D initstate )) +__cdecl __MINGW_NOTHROW char *initstate (unsigned int __seed, char *__b= uf, size_t __len) +{ return __mingw_initstate (__seed, __buf, __len); } + +__CRT_ALIAS __JMPSTUB__(( FUNCTION =3D setstate )) +__cdecl __MINGW_NOTHROW char *setstate (char *__buf) +{ return __mingw_setstate (__buf); } + +#endif /* _XOPEN_SOURCE >=3D 500 || _BSD_SOURCE */ + _CRTIMP __cdecl __MINGW_NOTHROW void abort (void) __MINGW_ATTRIB_NORETU= RN; _CRTIMP __cdecl __MINGW_NOTHROW void exit (int) __MINGW_ATTRIB_NORETURN= ; =20 /* Note: this is in startup code, not imported directly from the runtime=20 DLL */ diff --git a/mingwrt/mingwex/math/random.c b/mingwrt/mingwex/math/random.= c new file mode 100644 --- /dev/null +++ b/mingwrt/mingwex/math/random.c @@ -0,0 +1,250 @@ +/* + * random.c + * + * Implementation of a (mostly) POSIX.1-1990 conforming pseudo-random + * number generating API, for use with MinGW applications. + * + * $Id$ + * + * Written by Keith Marshall <[email protected]> + * Copyright (C) 2021, MinGW.org Project + * + * + * This is free software. Permission is granted to copy, modify and + * redistribute this software, under the provisions of the GNU General + * Public License, Version 3, (or, at your option, any later version), + * as published by the Free Software Foundation; see the file COPYING + * for licensing details. + * + * Note, in particular, that this software is provided "as is", in the + * hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not + * even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY + * PARTICULAR PURPOSE. Under no circumstances will the author, or the + * MinGW Project, accept liability for any damages, however caused, + * arising from the use of this software. + * + * + * This pseudo-random number generation suite has been derived from a + * discourse on the GLIBC implementation, by Peter Selinger: + * + * https://www.mathstat.dal.ca/~selinger/random/ + * + * It is believed that the output from the PRNG will closely mimic that + * from the GLIBC implementation; however, neither the author of this + * implementation, nor the MinGW.org Project, offer any assurance as to + * the statistical quality of the generated number sequence. + * + * + * Note: this implementation has been provided for optional compliance + * with the POSIX.1-1990 Extended Systems Interface. + */ +#define _XOPEN_SOURCE 500 + +#include <stdint.h> +#include <stdlib.h> +#include <errno.h> + +struct state +{ /* Internal structure, used to map the PRNG state buffer from its + * arbitrarily specified sequence of 8, 32, 64, 128, or 256 bytes, + * to an array of int32_t entities; the first entry is used as a + * control sturcture, comprising three 8-bit indices, while the + * remaining entries are used to represent PRNG state. + */ + struct + { uint32_t phase:8; /* current state data cycle pointer */ + uint32_t shift:8; /* associated offset data pointer */ + uint32_t limit:8; /* total count of state data entries */ + }; + int32_t data[]; /* array of state data entries */ +}; + +/* Provide a local state buffer, of length 128 bytes, (equivalent + * to 32 int32_t entries), for use when no alternative buffer has + * been specified, prior to calling random(). + */ +static int32_t default_state[32] =3D { 0 }; +static struct state *state =3D (struct state *)(default_state); + +/* The following inline local function is provided to facilitate + * setting of "errno", on abnormal return from any other function. + */ +static __inline__ __attribute__((__always_inline__)) +intptr_t errout( int code, intptr_t retval ){ errno =3D code; return ret= val; } + +static char *assign_state( char *buf, char *prev ) +{ /* A local helper function, to attach a new state buffer to the + * PRNG; aborts the assignment, if the specified buffer is NULL, + * but otherwise, performs no buffer validation. + */ + if( buf =3D=3D NULL ) return (char *)(errout( EINVAL, (intptr_t)(NULL)=20 )); + state =3D (struct state *)(buf); + return prev; +} + +char *__mingw_setstate( char *buf ) +{ /* Public API entry point, exhibiting POSIX.1-1990 semantics, + * for accessing the preceding buffer assignment function; the + * "buf" argument is assumed to point to an existing non-NULL + * buffer, which had been previously initialized by a prior + * initstate() call, (and possibly already used), but, other + * that checking that it is not NULL, no validation of its + * initialization state is performed. + */ + return assign_state( buf, (char *)(state) ); +} + +static size_t normalized_cycle( size_t len ) +{ /* A local helper function, to establish the effective number + * of entries in the PRNG state data array, as a dependency of + * the total byte count specified for the state buffer; note + * that the compiler may be able to compute the resultant at + * compile time, when called with a constant "len" argument, + * and so optimize away such calls. + */ + size_t cycle_counter =3D (len >=3D 32) ? 256 : 8; + while( cycle_counter > len ) cycle_counter >>=3D 1; + return (cycle_counter >> 2) - 1; +} + +static long update_state( void ) +{ /* A local helper function, to update the content of the active + * state buffer with each successive call of random(). + */ + if( state->phase =3D=3D state->limit ) state->phase =3D 0; + else if( state->shift =3D=3D state->limit ) state->shift =3D 0; + return (state->data[state->shift++] +=3D state->data[state->phase++]);= +} + +static void initialize_state_data( unsigned int seed ) +{ /* A local helper function, called after initialization of the + * state buffer length count, to populate the state data array. + * + * In all cases, we record the "seed" value, as the first entry + * in the state data array, (rejecting zero, for which we force + * substitution of 1). + */ + state->data[0] =3D (seed !=3D 0) ? seed : 1; + + /* For a minimum-length state buffer, of only 8 bytes, there is + * no more state data to be recorded... + */ + if( state->limit > 1 ) + { /* ...but for any of the larger permitted buffer sizes, we must + * populate the buffer using a multiplicative sequence of 31-bit + * integers; the multiplication is performed in a 64-bit space, + * then reduced modulo the Mersenne prime of order 31; set up a + * collection of constants, pertinent to the computation... + */ + const unsigned long order =3D 31; + const unsigned long divisor =3D (unsigned long)((1ULL << order) - 1)= ; + const unsigned long long multiplier =3D 16807ULL; + + /* ...then apply to each data array entry, in turn... + */ + for( seed =3D 1; seed < state->limit; seed++ ) + { /* ...computing the sequence of products, in 64-bit space... + */ + unsigned long long tmp =3D multiplier * state->data[seed - 1]; + + /* ...then reduce for storage; note that, since the divisor + * is a Mersenne prime, we can perform the modulo division by + * use of the more computationally efficient mask, shift and + * addition, followed by a conditional correction, in case + * of overflow into the sign bit. + */ + state->data[seed] =3D (int)(tmp =3D (tmp & divisor) + (tmp >> orde= r)); + if( state->data[seed] < 0 ) state->data[seed] +=3D divisor; + } + /* We must also initialize the phase, and shift indices, for + * the PRNG feed-back loop... + */ + state->phase =3D 0; + switch( state->limit ) + { /* ...noting that the shift interval is chosen differently, + * depending on the limiting length of the sequence. + */ + case 63: case 15: state->shift =3D 1; break; + case 31: case 7: state->shift =3D 3; break; + } + /* Finally, run the PRNG through 10 update cycles for each and + * every entry in the state data array, but without generating + * any actual pseudo-random number output. + */ + seed =3D ((state->limit << 2) + state->limit) << 1; + while( seed-- > 0 ) update_state(); + } +} + +char *__mingw_initstate( unsigned int seed, char *buf, size_t len ) +{ /* Public API entry point, implementing the POSIX.1-1990 initstate() + * function; aborts if "buf" is passed as a NULL pointer, or if "len" + * is less than the minimum allowed 8 bytes; otherwise, assigns "buf" + * as a new PRNG state buffer... + */ + char *prev =3D assign_state( buf, (len >=3D 8) ? (char *)(state) : NUL= L ); + if( prev !=3D NULL ) + { + /* ...initializes it to the specified "len", and as if srandom() + * has been called with "seed" as argument... + */ + state->limit =3D normalized_cycle( len ); + initialize_state_data( seed ); + } + /* ...then returns a pointer to the original state buffer. + */ + return prev; +} + +void __mingw_srandom( unsigned int seed ) +{ /* Public API entry point, implementing the POSIX-1.1990 srandom() + * function; this sets the PRNG to a known initial state, which is + * dependent on the "seed" value specified. + */ + if( (state =3D=3D (struct state *)(default_state)) && (state->limit =3D= =3D 0) ) + state->limit =3D normalized_cycle( 128 ); + initialize_state_data( seed ); +} + +long __mingw_random( void ) +{ /* Public API entry point, implementing the POSIX-1.1990 random() + * function; this initially checks whether the state buffer has been + * provided by the user, (in which case we assume that it was properly= + * initialized, by calling initstate()), or if the default buffer is + * in use... + */ + if( state =3D=3D (struct state *)(default_state) ) + { /* ...and we anticipate that, if in its default application start-up= + * state, this buffer may require implicit initialization; when this= + * is the case... + */ + if( state->limit =3D=3D 0 ) + { /* ...as indicated by no buffer size limit having been assigned, + * we initialize it now, as if by calling initstate() with buffer + * size specified appropriately, as 128 bytes, and seed of 1. + */ + state->limit =3D normalized_cycle( 128 ); + initialize_state_data( 1 ); + } + } + else if( state->limit =3D=3D normalized_cycle( 8 ) ) + { /* The state buffer has been explicitly initialized already, using + * the minimum allowed length of 8 bytes; in this case, we use a + * simple linear congruential PRNG, with multiplier of 1103515245, + * and increment of 12345, masked to ensure that the result fits + * within the range of positive 32-bit signed integer values. + */ + const unsigned long long increment =3D 12345ULL; + const unsigned long long multiplier =3D 1103515245ULL; + const unsigned int result_mask =3D ((1ULL << 31) - 1); + unsigned long long tmp =3D multiplier * state->data[0] + increment; + return (long)(state->data[0] =3D (int)(tmp & result_mask)); + } + /* For any state buffer, longer than the 8-byte minimum, update the + * buffer state, and extract the next pseudo-random number to return, + * scaling to discard its least significant bit. + */ + return (long)((unsigned long)(update_state()) >> 1); +} + +/* $RCSfile$: end of file */ --------------562AFB5433C889B886E770E9 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: base64 Content-Disposition: inline X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18KTWluR1ctVXNl cnMgbWFpbGluZyBsaXN0Ck1pbkdXLVVzZXJzQGxpc3RzLm9zZG4ubWUKClRoaXMgbGlzdCBvYnNl cnZlcyB0aGUgUG9zdGluZyBFdGlxdWV0dGUsIGFzIGRlc2NyaWJlZCBhdCBodHRwczovL21pbmd3 Lm9zZG4uaW8vaW5kZXguaHRtbD9wYWdlPW1haWxpbmcuaHRtbCNsaXN0LWV0aXF1ZXR0ZS4KV2Ug YXNrIHRoYXQgeW91IGJlIHBvbGl0ZSBhbmQgZG8gdGhlIHNhbWUuICBEaXNyZWdhcmQgZm9yIHRo ZSBsaXN0IGV0aXF1ZXR0ZSBtYXkgY2F1c2UgeW91ciBhY2NvdW50IHRvIGJlIG1vZGVyYXRlZC4K Cl9fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fCllvdSBtYXkg Y2hhbmdlIHlvdXIgTWluR1cgQWNjb3VudCBPcHRpb25zIG9yIHVuc3Vic2NyaWJlIGF0OgpodHRw czovL2xpc3RzLm9zZG4ubWUvbWFpbG1hbi9saXN0aW5mby9taW5ndy11c2VycwpBbHNvOiBtYWls dG86bWluZ3ctdXNlcnMtcmVxdWVzdEBsaXN0cy5vc2RuLm1lP3N1YmplY3Q9dW5zdWJzY3JpYmU= --------------562AFB5433C889B886E770E9--