#include <9pm/windows.h>
#include <9pm/u.h>
#include <9pm/libc.h>
#include <9pm/libsec.h>

typedef struct State{
	uvlong		seed;
	DES3state	des3;
} State;

static int init;
static CRITICAL_SECTION cs;
static double fasttick;

static State x917state;

static uvlong
nsec(void)
{
	LARGE_INTEGER ti;
	FILETIME ft;
	uvlong t;
	
	GetSystemTimeAsFileTime(&ft);
	t = (((uvlong)ft.dwLowDateTime) + (((uvlong)ft.dwHighDateTime)<<32))*100;

	if(fasttick != 0 && QueryPerformanceCounter(&ti)) {
		t += (uvlong)(((double)ti.QuadPart)*fasttick);
		t -= ((uvlong)GetTickCount())*1000000;
	}

	return t;
}

__declspec( naked ) uvlong
rdtsc(void)
{
	_asm {
		_emit 0x0F      ;RDTSC - get beginning timestamp to edx:eax
		_emit 0x31
		ret
        }
}

/*
 * attempt at generating a pretty good random seed
 * the following assumes we are on a pentium - should do
 * something if we do not have the rdtsc instruction
 */
static void
randomseed(uchar *p)
{
	SHAstate *s;
	uvlong tsc;
	LARGE_INTEGER ti;
	int i, j;
	FILETIME ft;

	GetSystemTimeAsFileTime(&ft);
	s = sha1((uchar*)&ft, sizeof(ft), 0, 0);
	for(i=0; i<50; i++) {
		for(j=0; j<10; j++) {
			tsc = rdtsc();
			s = sha1((uchar*)&tsc, sizeof(tsc), 0, s);
			QueryPerformanceCounter(&ti);
			s = sha1((uchar*)&ti, sizeof(ti), 0, s);
			tsc = GetTickCount();
			s = sha1((uchar*)&tsc, sizeof(tsc), 0, s);
		}
		Sleep(10);
	}
	sha1(0, 0, p, s);
}


static void
X917(uchar *rand, int nrand)
{
	int i, m, n8;
	uvlong I, x;

	/* 1. Compute intermediate value I = Ek(time). */
	I = nsec();
	triple_block_cipher(x917state.des3.expanded, (uchar*)&I, 0); /* two-key EDE */

	/* 2. x[i] = Ek(I^seed);  seed = Ek(x[i]^I); */
	m = (nrand+7)/8;
	for(i=0; i<m; i++){
		x = I ^ x917state.seed;
		triple_block_cipher(x917state.des3.expanded, (uchar*)&x, 0);
		n8 = (nrand>8) ? 8 : nrand;
		memcpy(rand, (uchar*)&x, n8);
		rand += 8;
		nrand -= 8;
		x ^= I;
		triple_block_cipher(x917state.des3.expanded, (uchar*)&x, 0);
		x917state.seed = x;
	}
}

static void
X917init(void)
{
	uchar mix[128];
	uchar key3[3][8];
	uchar seed[SHA1dlen];

	randomseed(seed);
	memmove(key3, seed, SHA1dlen);
	setupDES3state(&x917state.des3, key3, nil);
	X917(mix, sizeof mix);
}

static void
genrandominit(void)
{
	static long lock;
	LARGE_INTEGER fi;
	

	/*
	 * initialize only once
	 * have to roll my own lock!
	 * The CRITICAL_SECTION object appears to require
	 * a single thread to initial it -
	 * sort of hard to do without a synchronization
	 * primitive!!
	 */
		
	while(InterlockedExchange(&lock, 1) != 0)
		Sleep(100);

	if(!init) {
		InitializeCriticalSection(&cs);

		if(QueryPerformanceFrequency(&fi))
		if(fi.QuadPart != 0)
			fasttick = 1.e9/fi.QuadPart;

		X917init();
		
		init = 1;
	}

	InterlockedExchange(&lock, 0);
}

long
randomread(void *p, int n)
{
	if(!init)
		genrandominit();
	EnterCriticalSection(&cs);
	X917(p, n);
	LeaveCriticalSection(&cs);
	return n;
}


syntax highlighted by Code2HTML, v. 0.9.1