Clean up, simplify, and re-work Perl_seed(). Fixes #2296

DEBUG_U doesn't work? We'll just use a normal comment for now

epoch should be U64
This commit is contained in:
Scott Baker 2026-01-19 17:33:30 -08:00 committed by Karl Williamson
parent 134107b181
commit 76771b2f9a

97
util.c
View File

@ -4573,41 +4573,28 @@ PERL_STATIC_INLINE U32 S_ptr_hash(PTRV u) {
return (U32)u;
}
/* Splitmix64 is a simple PRNG and integer hashing function. It was
* introducted in 2015: https://gee.cs.oswego.edu/dl/papers/oopsla14.pdf
* Examples: https://rosettacode.org/wiki/Pseudo-random_numbers/Splitmix64
*/
U64
splitmix64(U64 *state)
{
U64 z = (*state += 0x9e3779b97f4a7c15);
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
return z ^ (z >> 31);
}
U64
Perl_seed(pTHX)
{
/*
* This is really just a quick hack which grabs various garbage
* values. It really should be a real hash algorithm which
* spreads the effect of every input bit onto every output bit,
* if someone who knows about such things would bother to write it.
* Might be a good idea to add that function to CORE as well.
* No numbers below come from careful analysis or anything here,
* except they are primes and SEED_C1 > 1E6 to get a full-width
* value from (tv_sec * SEED_C1 + tv_usec). The multipliers should
* probably be bigger too.
*/
#if RANDBITS > 16
# define SEED_C1 1000003
#define SEED_C4 73819
#else
# define SEED_C1 25747
#define SEED_C4 20639
#endif
#define SEED_C2 3
#define SEED_C3 269
#define SEED_C5 26107
#ifndef PERL_NO_DEV_RANDOM
int fd;
#endif
U32 u;
#ifdef HAS_GETTIMEOFDAY
struct timeval when;
#else
Time_t when;
#endif
/*
* Attempt to read from /dev/urandom to generate a pseudo-random number.
* If that does not work, or it is unavailable, we fall back to gathering
* several state variables and hashing them into a seed value.
*/
/* This test is an escape hatch, this symbol isn't set by Configure. */
#ifndef PERL_NO_DEV_RANDOM
@ -4617,35 +4604,57 @@ Perl_seed(pTHX)
* PERL_RANDOM_DEVICE to it if you'd prefer Perl to block until there
* is enough real entropy to fill the seed. */
# ifdef __amigaos4__
# define PERL_RANDOM_DEVICE "RANDOM:SIZE=4"
/* https://wiki.amigaos.net/wiki/AmigaOS_Manual%3A_AmigaDOS_Additional_Amiga_Directories#Random-Handler_(RANDOM:) */
# define PERL_RANDOM_DEVICE "RANDOM:"
# else
# define PERL_RANDOM_DEVICE "/dev/urandom"
# endif
#endif
fd = PerlLIO_open_cloexec(PERL_RANDOM_DEVICE, 0);
U64 u;
int fd = PerlLIO_open_cloexec(PERL_RANDOM_DEVICE, 0);
if (fd != -1) {
if (PerlLIO_read(fd, (void*)&u, sizeof u) != sizeof u)
if (PerlLIO_read(fd, (void*)&u, sizeof u) != sizeof u) {
u = 0;
}
PerlLIO_close(fd);
if (u)
if (u) {
return u;
}
}
#endif
/* We only get this far if /dev/urandom is not available or the read fails.
* Grab several state variables and hash those for randomness instead. */
#ifdef HAS_GETTIMEOFDAY
struct timeval when;
PerlProc_gettimeofday(&when,NULL);
u = (U32)SEED_C1 * when.tv_sec + (U32)SEED_C2 * when.tv_usec;
/* Milliseconds */
U64 epoch = ((U64)when.tv_sec * 1000000) + when.tv_usec;
#else
Time_t when;
(void)time(&when);
u = (U32)SEED_C1 * when;
/* Seconds */
U64 epoch = when;
#endif
u += SEED_C3 * (U32)PerlProc_getpid();
u += SEED_C4 * (U32)PTR2UV(PL_stack_sp);
#ifndef PLAN9 /* XXX Plan9 assembler chokes on this; fix needed */
UV ptruv = PTR2UV(&when);
u += SEED_C5 * ptr_hash(ptruv);
#endif
return u;
UV pid = PerlProc_getpid();
UV time_ptr = PTR2UV(&when);
/* epoch in microseconds is ~52 bits, PIDs are ~22 bits, PTRs are ~48 bits.
* We mix the bits for all three together to get a good spread of entropy */
U64 tmp = (time_ptr << 16) | (pid << 8) | (epoch);
U64 ret = splitmix64(&tmp);
/* PerlIO_printf(Perl_debug_log, "XXXX: TIME:%lu PID:%lu PTR:%lu\n", epoch, pid, time_ptr); */
/* PerlIO_printf(Perl_debug_log, "SEED: %lu\n", ret); */
return ret;
}
void