forked from Telodendria/Telodendria
Make Rand use a provided Mersenne Twister.
This implementation is loosely inspired by the original paper on the Mersenne Twister, and borrows code from a public-domain implementation of it, adapting it to fit the style of Telodendria's code, and fixing a few bugs regarding the size of the data type used. Neither C nor POSIX provide a good, thread-safe pseudorandom number generator. The OpenBSD linker started complaining about the use of rand_r(), and no standard alternative presented itself as worthy of consideration, so I finally decided it was time to roll my own PRNG.
This commit is contained in:
parent
098eed44a0
commit
d933d12e1b
5 changed files with 198 additions and 48 deletions
89
src/Rand.c
89
src/Rand.c
|
@ -23,12 +23,80 @@
|
|||
*/
|
||||
#include <Rand.h>
|
||||
|
||||
#include <Int.h>
|
||||
#include <Util.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define RAND_STATE_VECTOR_LENGTH 624
|
||||
#define RAND_STATE_VECTOR_M 397
|
||||
|
||||
#define RAND_UPPER_MASK 0x80000000
|
||||
#define RAND_LOWER_MASK 0x7FFFFFFF
|
||||
#define RAND_TEMPER_B 0x9D2C5680
|
||||
#define RAND_TEMPER_C 0xEFC60000
|
||||
|
||||
typedef struct RandState
|
||||
{
|
||||
UInt32 mt[RAND_STATE_VECTOR_LENGTH];
|
||||
int index;
|
||||
} RandState;
|
||||
|
||||
static void
|
||||
RandSeed(RandState *state, UInt32 seed)
|
||||
{
|
||||
state->mt[0] = seed & 0xFFFFFFFF;
|
||||
|
||||
for (state->index = 1; state->index < RAND_STATE_VECTOR_LENGTH; state->index++)
|
||||
{
|
||||
state->mt[state->index] = (6069 * state->mt[state->index - 1]) & 0xFFFFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
static UInt32
|
||||
RandGenerate(RandState *state)
|
||||
{
|
||||
static const UInt32 mag[2] = { 0x0, 0x9908B0DF };
|
||||
|
||||
UInt32 result;
|
||||
|
||||
if (state->index >= RAND_STATE_VECTOR_LENGTH || state->index < 0)
|
||||
{
|
||||
int kk;
|
||||
|
||||
if (state->index >= RAND_STATE_VECTOR_LENGTH + 1 || state->index < 0)
|
||||
{
|
||||
RandSeed(state, 4357);
|
||||
}
|
||||
|
||||
for (kk = 0; kk < RAND_STATE_VECTOR_LENGTH - RAND_STATE_VECTOR_M; kk++)
|
||||
{
|
||||
result = (state->mt[kk] & RAND_UPPER_MASK) | (state->mt[kk + 1] & RAND_LOWER_MASK);
|
||||
state->mt[kk] = state->mt[kk + RAND_STATE_VECTOR_M] ^ (result >> 1) ^ mag[result & 0x1];
|
||||
}
|
||||
|
||||
for (; kk < RAND_STATE_VECTOR_LENGTH - 1; kk++)
|
||||
{
|
||||
result = (state->mt[kk] & RAND_UPPER_MASK) | (state->mt[kk + 1] & RAND_LOWER_MASK);
|
||||
state->mt[kk] = state->mt[kk + (RAND_STATE_VECTOR_M - RAND_STATE_VECTOR_LENGTH)] ^ (result >> 1) ^ mag[result & 0x1];
|
||||
}
|
||||
|
||||
result = (state->mt[RAND_STATE_VECTOR_LENGTH - 1] & RAND_UPPER_MASK) | (state->mt[0] & RAND_LOWER_MASK);
|
||||
state->mt[RAND_STATE_VECTOR_LENGTH - 1] = state->mt[RAND_STATE_VECTOR_M - 1] ^ (result >> 1) ^ mag[result & 0x1];
|
||||
state->index = 0;
|
||||
}
|
||||
|
||||
result = state->mt[state->index++];
|
||||
result ^= (result >> 11);
|
||||
result ^= (result << 7) & RAND_TEMPER_B;
|
||||
result ^= (result << 15) & RAND_TEMPER_C;
|
||||
result ^= (result >> 18);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Generate random numbers using rejection sampling. The basic idea is
|
||||
* to "reroll" if a number happens to be outside the range. However
|
||||
* this could be extremely inefficient.
|
||||
|
@ -44,20 +112,23 @@
|
|||
void
|
||||
RandIntN(int *buf, size_t size, unsigned int max)
|
||||
{
|
||||
static pthread_mutex_t seedLock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static unsigned int seed = 0;
|
||||
int tmp;
|
||||
static pthread_mutex_t stateLock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static UInt32 seed = 0;
|
||||
static RandState state;
|
||||
|
||||
/* Limit the range to banish all previously biased results */
|
||||
const int allowed = RAND_MAX - RAND_MAX % max;
|
||||
|
||||
int tmp;
|
||||
size_t i;
|
||||
|
||||
pthread_mutex_lock(&seedLock);
|
||||
pthread_mutex_lock(&stateLock);
|
||||
|
||||
if (!seed)
|
||||
{
|
||||
/* Generate a seed from the system time, PID, and TID */
|
||||
seed = UtilServerTs() ^ getpid() ^ (unsigned long) pthread_self();
|
||||
RandSeed(&state, seed);
|
||||
}
|
||||
|
||||
/* Generate {size} random numbers. */
|
||||
|
@ -66,13 +137,13 @@ RandIntN(int *buf, size_t size, unsigned int max)
|
|||
/* Most of the time, this will take about 1 loop */
|
||||
do
|
||||
{
|
||||
tmp = rand_r(&seed);
|
||||
} while (tmp >= allowed);
|
||||
/* Since a generated number here is never in the biased range,
|
||||
* we can now safely use modulo. */
|
||||
tmp = RandGenerate(&state);
|
||||
} while (tmp > allowed);
|
||||
|
||||
buf[i] = tmp % max;
|
||||
}
|
||||
pthread_mutex_unlock(&seedLock);
|
||||
|
||||
pthread_mutex_unlock(&stateLock);
|
||||
}
|
||||
|
||||
/* Generate just 1 random number */
|
||||
|
|
55
src/Sha2.c
55
src/Sha2.c
|
@ -23,41 +23,18 @@
|
|||
*/
|
||||
#include <Sha2.h>
|
||||
#include <Memory.h>
|
||||
#include <Int.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
/*
|
||||
* POSIX says that LONG_BIT and WORD_BIT are defined, but some notable
|
||||
* non-conforming systems and compilers don't don't define it, or only
|
||||
* define it in circumstances I'm unwilling to comply with (such as
|
||||
* defining _GNU_SOURCE.
|
||||
*
|
||||
* So, unfortunately, although LONG_BIT and WORD_BIT are the most
|
||||
* elegant solutions, we're forced to do this so we can check LONG_MAX
|
||||
* and INT_MAX.
|
||||
*/
|
||||
#define BIT64_MAX 9223372036854775807
|
||||
#define BIT32_MAX 2147483647
|
||||
#define BIT16_MAX 32767
|
||||
|
||||
#if (defined(LONG_BIT) && LONG_BIT == 32) || (defined(LONG_MAX) && LONG_MAX == BIT32_MAX)
|
||||
typedef unsigned long uint32_t;
|
||||
|
||||
#elif (defined(WORD_BIT) && WORD_BIT == 32) || (defined(INT_MAX) && INT_MAX == BIT32_MAX)
|
||||
typedef unsigned int uint32_t;
|
||||
|
||||
#else
|
||||
#error Unable to find suitable integer type for uint32_t
|
||||
#endif
|
||||
|
||||
#define GET_UINT32(x) \
|
||||
(((uint32_t)(x)[0] << 24) | \
|
||||
((uint32_t)(x)[1] << 16) | \
|
||||
((uint32_t)(x)[2] << 8) | \
|
||||
((uint32_t)(x)[3]))
|
||||
(((UInt32)(x)[0] << 24) | \
|
||||
((UInt32)(x)[1] << 16) | \
|
||||
((UInt32)(x)[2] << 8) | \
|
||||
((UInt32)(x)[3]))
|
||||
|
||||
#define PUT_UINT32(dst, x) { \
|
||||
(dst)[0] = (x) >> 24; \
|
||||
|
@ -79,8 +56,8 @@ typedef unsigned int uint32_t;
|
|||
#define WW(i) (w[i] = w[i - 16] + S0(w[i - 15]) + w[i - 7] + S1(w[i - 2]))
|
||||
|
||||
#define ROUND(a, b, c, d, e, f, g, h, k, w) { \
|
||||
uint32_t tmp0 = h + T0(e) + CH(e, f, g) + k + w; \
|
||||
uint32_t tmp1 = T1(a) + MAJ(a, b, c); \
|
||||
UInt32 tmp0 = h + T0(e) + CH(e, f, g) + k + w; \
|
||||
UInt32 tmp1 = T1(a) + MAJ(a, b, c); \
|
||||
h = tmp0 + tmp1; \
|
||||
d += tmp0; \
|
||||
}
|
||||
|
@ -88,7 +65,7 @@ typedef unsigned int uint32_t;
|
|||
typedef struct Sha256Context
|
||||
{
|
||||
size_t length;
|
||||
uint32_t state[8];
|
||||
UInt32 state[8];
|
||||
size_t bufLen;
|
||||
unsigned char buffer[64];
|
||||
} Sha256Context;
|
||||
|
@ -96,7 +73,7 @@ typedef struct Sha256Context
|
|||
static void
|
||||
Sha256Chunk(Sha256Context * context, unsigned char chunk[64])
|
||||
{
|
||||
const uint32_t rk[64] = {
|
||||
const UInt32 rk[64] = {
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
|
||||
0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
||||
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
|
||||
|
@ -110,8 +87,8 @@ Sha256Chunk(Sha256Context * context, unsigned char chunk[64])
|
|||
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
||||
};
|
||||
|
||||
uint32_t w[64];
|
||||
uint32_t a, b, c, d, e, f, g, h;
|
||||
UInt32 w[64];
|
||||
UInt32 a, b, c, d, e, f, g, h;
|
||||
|
||||
int i;
|
||||
|
||||
|
@ -202,10 +179,10 @@ Sha256(char *str)
|
|||
char *outStr;
|
||||
|
||||
unsigned char fill[64];
|
||||
uint32_t fillLen;
|
||||
UInt32 fillLen;
|
||||
unsigned char buf[8];
|
||||
uint32_t hiLen;
|
||||
uint32_t loLen;
|
||||
UInt32 hiLen;
|
||||
UInt32 loLen;
|
||||
|
||||
if (!str)
|
||||
{
|
||||
|
@ -237,8 +214,8 @@ Sha256(char *str)
|
|||
fill[0] = 0x80;
|
||||
|
||||
fillLen = (context.bufLen < 56) ? 56 - context.bufLen : 120 - context.bufLen;
|
||||
hiLen = (uint32_t) (context.length >> 29);
|
||||
loLen = (uint32_t) (context.length << 3);
|
||||
hiLen = (UInt32) (context.length >> 29);
|
||||
loLen = (UInt32) (context.length << 3);
|
||||
|
||||
PUT_UINT32(&buf[0], hiLen);
|
||||
PUT_UINT32(&buf[4], loLen);
|
||||
|
|
73
src/include/Int.h
Normal file
73
src/include/Int.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
#ifndef TELODENDRIA_INT_H
|
||||
#define TELODENDRIA_INT_H
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#define BIT64_MAX 18446744073709551615
|
||||
#define BIT32_MAX 4294967295
|
||||
#define BIT16_MAX 65535
|
||||
#define BIT8_MAX 255
|
||||
|
||||
#ifndef UCHAR_MAX
|
||||
#error Size of char data type is unknown. Define UCHAR_MAX.
|
||||
#endif
|
||||
|
||||
#ifndef USHRT_MAX
|
||||
#error Size of short data type is unknown. Define USHRT_MAX.
|
||||
#endif
|
||||
|
||||
#ifndef UINT_MAX
|
||||
#error Size of int data type is unknown. Define UINT_MAX.
|
||||
#endif
|
||||
|
||||
#ifndef ULONG_MAX
|
||||
#error Size of long data type is unknown. Define ULONG_MAX.
|
||||
#endif
|
||||
|
||||
#if UCHAR_MAX == BIT8_MAX
|
||||
typedef signed char Int8;
|
||||
typedef unsigned char UInt8;
|
||||
|
||||
#else
|
||||
#error Unable to determine suitable data type for 8-bit integers.
|
||||
#endif
|
||||
|
||||
#if UINT_MAX == BIT16_MAX
|
||||
typedef signed int Int16;
|
||||
typedef unsigned int UInt16;
|
||||
|
||||
#elif USHRT_MAX == BIT16_MAX
|
||||
typedef signed short Int16;
|
||||
typedef unsigned short UInt16;
|
||||
|
||||
#elif UCHAR_MAX == BIT16_MAX
|
||||
typedef signed char Int16;
|
||||
typedef unsigned char UInt16;
|
||||
|
||||
#else
|
||||
#error Unable to determine suitable data type for 16-bit integers.
|
||||
#endif
|
||||
|
||||
#if ULONG_MAX == BIT32_MAX
|
||||
typedef signed long Int32;
|
||||
typedef unsigned long UInt32;
|
||||
|
||||
#elif UINT_MAX == BIT32_MAX
|
||||
typedef signed int Int32;
|
||||
typedef unsigned int UInt32;
|
||||
|
||||
#elif USHRT_MAX == BIT32_MAX
|
||||
typedef signed short Int32;
|
||||
typedef unsigned short UInt32;
|
||||
|
||||
#elif UCHAR_MAX == BIT32_MAX
|
||||
typedef signed char Int32;
|
||||
typedef unsigned char UInt32;
|
||||
|
||||
#else
|
||||
#error Unable to determine suitable data type for 32-bit integers.
|
||||
#endif
|
||||
|
||||
/* The ANSI C standard only guarantees a data size of up to 32 bits. */
|
||||
|
||||
#endif
|
|
@ -154,6 +154,10 @@ recipe_build() {
|
|||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if ! intcheck; then
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
recipe_run() {
|
||||
|
|
25
tools/src/intcheck.c
Normal file
25
tools/src/intcheck.c
Normal file
|
@ -0,0 +1,25 @@
|
|||
#include <Int.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define ASSERT_SIZE(type, size) \
|
||||
if ((sizeof(type) * 8) != size) \
|
||||
{ \
|
||||
fputs(#type " is not " #size " bits.\n", stderr); \
|
||||
return 1; \
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
ASSERT_SIZE(Int8, 8);
|
||||
ASSERT_SIZE(UInt8, 8);
|
||||
|
||||
ASSERT_SIZE(Int16, 16);
|
||||
ASSERT_SIZE(UInt16, 16);
|
||||
|
||||
ASSERT_SIZE(Int32, 32);
|
||||
ASSERT_SIZE(UInt32, 32);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in a new issue