forked from lda/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 <Rand.h>
|
||||||
|
|
||||||
|
#include <Int.h>
|
||||||
#include <Util.h>
|
#include <Util.h>
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <unistd.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
|
/* Generate random numbers using rejection sampling. The basic idea is
|
||||||
* to "reroll" if a number happens to be outside the range. However
|
* to "reroll" if a number happens to be outside the range. However
|
||||||
* this could be extremely inefficient.
|
* this could be extremely inefficient.
|
||||||
|
@ -44,20 +112,23 @@
|
||||||
void
|
void
|
||||||
RandIntN(int *buf, size_t size, unsigned int max)
|
RandIntN(int *buf, size_t size, unsigned int max)
|
||||||
{
|
{
|
||||||
static pthread_mutex_t seedLock = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t stateLock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
static unsigned int seed = 0;
|
static UInt32 seed = 0;
|
||||||
int tmp;
|
static RandState state;
|
||||||
|
|
||||||
/* Limit the range to banish all previously biased results */
|
/* Limit the range to banish all previously biased results */
|
||||||
const int allowed = RAND_MAX - RAND_MAX % max;
|
const int allowed = RAND_MAX - RAND_MAX % max;
|
||||||
|
|
||||||
|
int tmp;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
pthread_mutex_lock(&seedLock);
|
pthread_mutex_lock(&stateLock);
|
||||||
|
|
||||||
if (!seed)
|
if (!seed)
|
||||||
{
|
{
|
||||||
/* Generate a seed from the system time, PID, and TID */
|
/* Generate a seed from the system time, PID, and TID */
|
||||||
seed = UtilServerTs() ^ getpid() ^ (unsigned long) pthread_self();
|
seed = UtilServerTs() ^ getpid() ^ (unsigned long) pthread_self();
|
||||||
|
RandSeed(&state, seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Generate {size} random numbers. */
|
/* 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 */
|
/* Most of the time, this will take about 1 loop */
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
tmp = rand_r(&seed);
|
tmp = RandGenerate(&state);
|
||||||
} while (tmp >= allowed);
|
} while (tmp > allowed);
|
||||||
/* Since a generated number here is never in the biased range,
|
|
||||||
* we can now safely use modulo. */
|
|
||||||
buf[i] = tmp % max;
|
buf[i] = tmp % max;
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&seedLock);
|
|
||||||
|
pthread_mutex_unlock(&stateLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Generate just 1 random number */
|
/* Generate just 1 random number */
|
||||||
|
|
55
src/Sha2.c
55
src/Sha2.c
|
@ -23,41 +23,18 @@
|
||||||
*/
|
*/
|
||||||
#include <Sha2.h>
|
#include <Sha2.h>
|
||||||
#include <Memory.h>
|
#include <Memory.h>
|
||||||
|
#include <Int.h>
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <limits.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) \
|
#define GET_UINT32(x) \
|
||||||
(((uint32_t)(x)[0] << 24) | \
|
(((UInt32)(x)[0] << 24) | \
|
||||||
((uint32_t)(x)[1] << 16) | \
|
((UInt32)(x)[1] << 16) | \
|
||||||
((uint32_t)(x)[2] << 8) | \
|
((UInt32)(x)[2] << 8) | \
|
||||||
((uint32_t)(x)[3]))
|
((UInt32)(x)[3]))
|
||||||
|
|
||||||
#define PUT_UINT32(dst, x) { \
|
#define PUT_UINT32(dst, x) { \
|
||||||
(dst)[0] = (x) >> 24; \
|
(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 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) { \
|
#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 tmp0 = h + T0(e) + CH(e, f, g) + k + w; \
|
||||||
uint32_t tmp1 = T1(a) + MAJ(a, b, c); \
|
UInt32 tmp1 = T1(a) + MAJ(a, b, c); \
|
||||||
h = tmp0 + tmp1; \
|
h = tmp0 + tmp1; \
|
||||||
d += tmp0; \
|
d += tmp0; \
|
||||||
}
|
}
|
||||||
|
@ -88,7 +65,7 @@ typedef unsigned int uint32_t;
|
||||||
typedef struct Sha256Context
|
typedef struct Sha256Context
|
||||||
{
|
{
|
||||||
size_t length;
|
size_t length;
|
||||||
uint32_t state[8];
|
UInt32 state[8];
|
||||||
size_t bufLen;
|
size_t bufLen;
|
||||||
unsigned char buffer[64];
|
unsigned char buffer[64];
|
||||||
} Sha256Context;
|
} Sha256Context;
|
||||||
|
@ -96,7 +73,7 @@ typedef struct Sha256Context
|
||||||
static void
|
static void
|
||||||
Sha256Chunk(Sha256Context * context, unsigned char chunk[64])
|
Sha256Chunk(Sha256Context * context, unsigned char chunk[64])
|
||||||
{
|
{
|
||||||
const uint32_t rk[64] = {
|
const UInt32 rk[64] = {
|
||||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
|
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
|
||||||
0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
||||||
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
|
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
|
||||||
|
@ -110,8 +87,8 @@ Sha256Chunk(Sha256Context * context, unsigned char chunk[64])
|
||||||
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
||||||
};
|
};
|
||||||
|
|
||||||
uint32_t w[64];
|
UInt32 w[64];
|
||||||
uint32_t a, b, c, d, e, f, g, h;
|
UInt32 a, b, c, d, e, f, g, h;
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
@ -202,10 +179,10 @@ Sha256(char *str)
|
||||||
char *outStr;
|
char *outStr;
|
||||||
|
|
||||||
unsigned char fill[64];
|
unsigned char fill[64];
|
||||||
uint32_t fillLen;
|
UInt32 fillLen;
|
||||||
unsigned char buf[8];
|
unsigned char buf[8];
|
||||||
uint32_t hiLen;
|
UInt32 hiLen;
|
||||||
uint32_t loLen;
|
UInt32 loLen;
|
||||||
|
|
||||||
if (!str)
|
if (!str)
|
||||||
{
|
{
|
||||||
|
@ -237,8 +214,8 @@ Sha256(char *str)
|
||||||
fill[0] = 0x80;
|
fill[0] = 0x80;
|
||||||
|
|
||||||
fillLen = (context.bufLen < 56) ? 56 - context.bufLen : 120 - context.bufLen;
|
fillLen = (context.bufLen < 56) ? 56 - context.bufLen : 120 - context.bufLen;
|
||||||
hiLen = (uint32_t) (context.length >> 29);
|
hiLen = (UInt32) (context.length >> 29);
|
||||||
loLen = (uint32_t) (context.length << 3);
|
loLen = (UInt32) (context.length << 3);
|
||||||
|
|
||||||
PUT_UINT32(&buf[0], hiLen);
|
PUT_UINT32(&buf[0], hiLen);
|
||||||
PUT_UINT32(&buf[4], loLen);
|
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
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
if ! intcheck; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
recipe_run() {
|
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