Skip to content

Commit

Permalink
Use the proper RNG called PCG instead of rand()
Browse files Browse the repository at this point in the history
Fixes #7.
  • Loading branch information
Tharre committed Aug 26, 2017
1 parent 5d35df9 commit 3d81d35
Show file tree
Hide file tree
Showing 8 changed files with 231 additions and 13 deletions.
3 changes: 2 additions & 1 deletion bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ $CC $CFLAGS -o out/filepath.o -c src/filepath.c
$CC $CFLAGS -o out/sha1.o -c src/sha1.c
$CC $CFLAGS -o out/DSV.o -c src/DSV.c
$CC $CFLAGS -o out/redo.o -c src/redo.c
$CC $CFLAGS -o out/pcg.o -c src/pcg.c
$CC -o out/redo out/redo.o out/util.o out/build.o out/filepath.o out/sha1.o \
out/DSV.o $LDFLAGS
out/DSV.o out/pcg.o $LDFLAGS
)

ln -sf redo out/redo-ifchange
Expand Down
2 changes: 1 addition & 1 deletion out/redo.do
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
. ./config.sh

DEPS="redo.o build.o util.o filepath.o sha1.o DSV.o"
DEPS="redo.o build.o util.o filepath.o sha1.o DSV.o pcg.o"
redo-ifchange $DEPS config.sh
$CC -o $3 $DEPS $LDFLAGS
13 changes: 11 additions & 2 deletions src/build.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <inttypes.h>

#include <libgen.h> /* dirname(), basename() */

Expand Down Expand Up @@ -394,6 +395,14 @@ void add_prereq_path(const char *target, const char *parent, int ident) {
free(reltarget);
}

static uint32_t get_magic_number() {
uint32_t magic;
if (sscanf(getenv("REDO_MAGIC"), "%"SCNu32, &magic) < 1)
die("redo: failed to parse REDO_MAGIC (%s)", getenv("REDO_MAGIC"));

return magic;
}

/* Update hash & ctime information stored in the given dep_info struct */
static void update_dep_info(dep_info *dep, const char *target) {
FILE *fp = fopen(target, "rb");
Expand All @@ -419,10 +428,10 @@ static void write_dep_information(dep_info *dep) {
sha1_to_hex(dep->hash, hash);
char *flags = (dep->flags & DEP_SOURCE) ? "s" : "l";

int magic = atoi(getenv("REDO_MAGIC"));
uint32_t magic = get_magic_number();

/* TODO: casting time_t to long long is probably not entirely portable */
if (fprintf(fd, "%s:%lld.%.9ld:%010d:%s\n", hash,
if (fprintf(fd, "%s:%lld.%.9ld:%"PRIu32":%s\n", hash,
(long long)dep->ctime.tv_sec, dep->ctime.tv_nsec, magic, flags) < 0)
fatal("redo: failed to write to %s", dep->path);

Expand Down
116 changes: 116 additions & 0 deletions src/pcg.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* PCG Random Number Generation for C.
*
* Copyright 2014 Melissa O'Neill <[email protected]>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For additional information about the PCG random number generation scheme,
* including its license and other licensing options, visit
*
* http://www.pcg-random.org
*/

/*
* This code is derived from the full C implementation, which is in turn
* derived from the canonical C++ PCG implementation. The C++ version
* has many additional features and is preferable if you can use C++ in
* your project.
*/

#include "pcg.h"

// state for global RNGs

static pcg32_random_t pcg32_global = PCG32_INITIALIZER;

// pcg32_srandom(initstate, initseq)
// pcg32_srandom_r(rng, initstate, initseq):
// Seed the rng. Specified in two parts, state initializer and a
// sequence selection constant (a.k.a. stream id)

void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initstate, uint64_t initseq)
{
rng->state = 0U;
rng->inc = (initseq << 1u) | 1u;
pcg32_random_r(rng);
rng->state += initstate;
pcg32_random_r(rng);
}

void pcg32_srandom(uint64_t seed, uint64_t seq)
{
pcg32_srandom_r(&pcg32_global, seed, seq);
}

// pcg32_random()
// pcg32_random_r(rng)
// Generate a uniformly distributed 32-bit random number

uint32_t pcg32_random_r(pcg32_random_t* rng)
{
uint64_t oldstate = rng->state;
rng->state = oldstate * 6364136223846793005ULL + rng->inc;
uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
uint32_t rot = oldstate >> 59u;
return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
}

uint32_t pcg32_random()
{
return pcg32_random_r(&pcg32_global);
}


// pcg32_boundedrand(bound):
// pcg32_boundedrand_r(rng, bound):
// Generate a uniformly distributed number, r, where 0 <= r < bound

uint32_t pcg32_boundedrand_r(pcg32_random_t* rng, uint32_t bound)
{
// To avoid bias, we need to make the range of the RNG a multiple of
// bound, which we do by dropping output less than a threshold.
// A naive scheme to calculate the threshold would be to do
//
// uint32_t threshold = 0x100000000ull % bound;
//
// but 64-bit div/mod is slower than 32-bit div/mod (especially on
// 32-bit platforms). In essence, we do
//
// uint32_t threshold = (0x100000000ull-bound) % bound;
//
// because this version will calculate the same modulus, but the LHS
// value is less than 2^32.

uint32_t threshold = -bound % bound;

// Uniformity guarantees that this loop will terminate. In practice, it
// should usually terminate quickly; on average (assuming all bounds are
// equally likely), 82.25% of the time, we can expect it to require just
// one iteration. In the worst case, someone passes a bound of 2^31 + 1
// (i.e., 2147483649), which invalidates almost 50% of the range. In
// practice, bounds are typically small and only a tiny amount of the range
// is eliminated.
for (;;) {
uint32_t r = pcg32_random_r(rng);
if (r >= threshold)
return r % bound;
}
}


uint32_t pcg32_boundedrand(uint32_t bound)
{
return pcg32_boundedrand_r(&pcg32_global, bound);
}

78 changes: 78 additions & 0 deletions src/pcg.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* PCG Random Number Generation for C.
*
* Copyright 2014 Melissa O'Neill <[email protected]>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For additional information about the PCG random number generation scheme,
* including its license and other licensing options, visit
*
* http://www.pcg-random.org
*/

/*
* This code is derived from the full C implementation, which is in turn
* derived from the canonical C++ PCG implementation. The C++ version
* has many additional features and is preferable if you can use C++ in
* your project.
*/

#ifndef PCG_BASIC_H_INCLUDED
#define PCG_BASIC_H_INCLUDED 1

#include <inttypes.h>

#if __cplusplus
extern "C" {
#endif

struct pcg_state_setseq_64 { // Internals are *Private*.
uint64_t state; // RNG state. All values are possible.
uint64_t inc; // Controls which RNG sequence (stream) is
// selected. Must *always* be odd.
};
typedef struct pcg_state_setseq_64 pcg32_random_t;

// If you *must* statically initialize it, here's one.

#define PCG32_INITIALIZER { 0x853c49e6748fea9bULL, 0xda3e39cb94b95bdbULL }

// pcg32_srandom(initstate, initseq)
// pcg32_srandom_r(rng, initstate, initseq):
// Seed the rng. Specified in two parts, state initializer and a
// sequence selection constant (a.k.a. stream id)

void pcg32_srandom(uint64_t initstate, uint64_t initseq);
void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initstate,
uint64_t initseq);

// pcg32_random()
// pcg32_random_r(rng)
// Generate a uniformly distributed 32-bit random number

uint32_t pcg32_random(void);
uint32_t pcg32_random_r(pcg32_random_t* rng);

// pcg32_boundedrand(bound):
// pcg32_boundedrand_r(rng, bound):
// Generate a uniformly distributed number, r, where 0 <= r < bound

uint32_t pcg32_boundedrand(uint32_t bound);
uint32_t pcg32_boundedrand_r(pcg32_random_t* rng, uint32_t bound);

#if __cplusplus
}
#endif

#endif // PCG_BASIC_H_INCLUDED
18 changes: 9 additions & 9 deletions src/redo.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,7 @@
#include "dbg.h"
#include "filepath.h"


/* Returns the amount of digits a number n has in decimal. */
static inline unsigned digits(unsigned n) {
return n ? 1 + digits(n/10) : n;
}
#include "pcg.h"

void prepare_env() {
if (getenv("REDO_ROOT") && getenv("REDO_PARENT_TARGET")
Expand All @@ -39,17 +35,21 @@ void prepare_env() {
fatal("redo: failed to setenv() REDO_ROOT to %s", cwd);
free(cwd);

/* initialize random number generator */
int rounds = 73;
pcg32_random_t rng;
pcg32_srandom_r(&rng, generate_seed(), (intptr_t)&rounds);

/* set REDO_MAGIC */
char magic_str[digits(UINT_MAX) + 1];
sprintf(magic_str, "%u", rand());
char magic_str[11];
sprintf(magic_str, "%"PRIu32, pcg32_random_r(&rng));
if (setenv("REDO_MAGIC", magic_str, 0))
fatal("redo: failed to setenv() REDO_MAGIC to %s", magic_str);
}

int DBG_LVL;

int main(int argc, char *argv[]) {
srand(time(NULL));
char *argv_base = xbasename(argv[0]);

if (!strcmp(argv_base, "redo")) {
Expand All @@ -62,7 +62,6 @@ int main(int argc, char *argv[]) {
}
} else {
char ident;
char **temp;
if (!strcmp(argv_base, "redo-ifchange"))
ident = 'c';
else if (!strcmp(argv_base, "redo-ifcreate"))
Expand All @@ -89,6 +88,7 @@ int main(int argc, char *argv[]) {
add_prereq(parent, parent, ident);
else
for (int i = 1; i < argc; ++i) {
char **temp;
do {
temp = &argv[rand() % (argc-1) + 1];
} while (!*temp);
Expand Down
12 changes: 12 additions & 0 deletions src/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,15 @@ void hex_to_sha1(const char *s, unsigned char *sha1) {
*sha1 = ((strchr(hex, *s) - hex) << 4) + strchr(hex, *(s+1)) - hex;
}

uint32_t generate_seed() {
uint32_t seed;
FILE *fp = fopen("/dev/urandom", "rb");
if (!fp)
fatal("redo: failed to open /dev/urandom");

if (fread(&seed, 1, 4, fp) < 4)
fatal("redo: failed to read from /dev/urandom");

fclose(fp);
return seed;
}
2 changes: 2 additions & 0 deletions src/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#define __RUTIL_H__

#include <stddef.h>
#include <stdint.h>
#include <stdio.h>

extern void __attribute__((noreturn)) die_(const char *err, ...);
Expand All @@ -20,5 +21,6 @@ extern char *concat(size_t count, ...);
extern unsigned char *hash_file(FILE *fp);
extern void sha1_to_hex(const unsigned char *sha1, char *buf);
extern void hex_to_sha1(const char *s, unsigned char *sha1);
extern uint32_t generate_seed();

#endif

0 comments on commit 3d81d35

Please sign in to comment.