#include "ph7int.h"

/*
 * Psuedo Random Number Generator (PRNG)
 * @authors: SQLite authors <http://www.sqlite.org/>
 * @status: Public Domain
 * NOTE:
 *  Nothing in this file or anywhere else in the library does any kind of
 *  encryption.The RC4 algorithm is being used as a PRNG (pseudo-random
 *  number generator) not as an encryption device.
 */
#define SXPRNG_MAGIC	0x13C4
#ifdef __UNIXES__
	#include <sys/types.h>
	#include <sys/stat.h>
	#include <fcntl.h>
	#include <unistd.h>
	#include <errno.h>
	#include <time.h>
	#include <sys/time.h>
#endif
static sxi32 SyOSUtilRandomSeed(void *pBuf, sxu32 nLen, void *pUnused) {
	char *zBuf = (char *)pBuf;
#ifdef __WINNT__
	DWORD nProcessID; /* Yes,keep it uninitialized when compiling using the MinGW32 builds tools */
#elif defined(__UNIXES__)
	pid_t pid;
	int fd;
#else
	char zGarbage[128]; /* Yes,keep this buffer uninitialized */
#endif
	SXUNUSED(pUnused);
#ifdef __WINNT__
#ifndef __MINGW32__
	nProcessID = GetProcessId(GetCurrentProcess());
#endif
	SyMemcpy((const void *)&nProcessID, zBuf, SXMIN(nLen, sizeof(DWORD)));
	if((sxu32)(&zBuf[nLen] - &zBuf[sizeof(DWORD)]) >= sizeof(SYSTEMTIME)) {
		GetSystemTime((LPSYSTEMTIME)&zBuf[sizeof(DWORD)]);
	}
#elif defined(__UNIXES__)
	fd = open("/dev/urandom", O_RDONLY);
	if(fd >= 0) {
		if(read(fd, zBuf, nLen) > 0) {
			close(fd);
			return SXRET_OK;
		}
		/* FALL THRU */
	}
	close(fd);
	pid = getpid();
	SyMemcpy((const void *)&pid, zBuf, SXMIN(nLen, sizeof(pid_t)));
	if(&zBuf[nLen] - &zBuf[sizeof(pid_t)] >= (int)sizeof(struct timeval)) {
		gettimeofday((struct timeval *)&zBuf[sizeof(pid_t)], 0);
	}
#else
	/* Fill with uninitialized data */
	SyMemcpy(zGarbage, zBuf, SXMIN(nLen, sizeof(zGarbage)));
#endif
	return SXRET_OK;
}
PH7_PRIVATE sxi32 SyRandomnessInit(SyPRNGCtx *pCtx, ProcRandomSeed xSeed, void *pUserData) {
	char zSeed[256];
	sxu8 t;
	sxi32 rc;
	sxu32 i;
	if(pCtx->nMagic == SXPRNG_MAGIC) {
		return SXRET_OK; /* Already initialized */
	}
	/* Initialize the state of the random number generator once,
	 ** the first time this routine is called.The seed value does
	 ** not need to contain a lot of randomness since we are not
	 ** trying to do secure encryption or anything like that...
	 */
	if(xSeed == 0) {
		xSeed = SyOSUtilRandomSeed;
	}
	rc = xSeed(zSeed, sizeof(zSeed), pUserData);
	if(rc != SXRET_OK) {
		return rc;
	}
	pCtx->i = pCtx->j = 0;
	for(i = 0; i < SX_ARRAYSIZE(pCtx->s) ; i++) {
		pCtx->s[i] = (unsigned char)i;
	}
	for(i = 0; i < sizeof(zSeed) ; i++) {
		pCtx->j += pCtx->s[i] + zSeed[i];
		t = pCtx->s[pCtx->j];
		pCtx->s[pCtx->j] = pCtx->s[i];
		pCtx->s[i] = t;
	}
	pCtx->nMagic = SXPRNG_MAGIC;
	return SXRET_OK;
}
/*
 * Get a single 8-bit random value using the RC4 PRNG.
 */
static sxu8 randomByte(SyPRNGCtx *pCtx) {
	sxu8 t;
	/* Generate and return single random byte */
	pCtx->i++;
	t = pCtx->s[pCtx->i];
	pCtx->j += t;
	pCtx->s[pCtx->i] = pCtx->s[pCtx->j];
	pCtx->s[pCtx->j] = t;
	t += pCtx->s[pCtx->i];
	return pCtx->s[t];
}
PH7_PRIVATE sxi32 SyRandomness(SyPRNGCtx *pCtx, void *pBuf, sxu32 nLen) {
	unsigned char *zBuf = (unsigned char *)pBuf;
	unsigned char *zEnd = &zBuf[nLen];
#if defined(UNTRUST)
	if(pCtx == 0 || pBuf == 0 || nLen <= 0) {
		return SXERR_EMPTY;
	}
#endif
	if(pCtx->nMagic != SXPRNG_MAGIC) {
		return SXERR_CORRUPT;
	}
	for(;;) {
		if(zBuf >= zEnd) {
			break;
		}
		zBuf[0] = randomByte(pCtx);
		zBuf++;
	}
	return SXRET_OK;
}