/* aiorandom.c - randomly perform asynchronous reads */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/asynch.h>
#include <sys/time.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>

#define NUM_RESULT	MAXASYNCHIO
#define BUFFERSIZE	(4096)

#define RESULT_OK	(0)
#define RESULT_IN_USE	(1)

#define TRUE	(1)
#define FALSE	(0)

typedef struct result_t {
	aio_result_t	result;
	int		status;
	char		buf[BUFFERSIZE];
} result_t;

result_t	results[NUM_RESULT];
int 		num_outstanding = 0;
int		filesize;
int		numfileblock;
int		numread = 0;
int		totnumread;
int		blksize;

void
init_results()
{
	int i;
	for (i = 0; i < MAXASYNCHIO; i++)
		results[i].status = RESULT_OK;
} /* init_results */

unsigned char
compute_checksum(char *bufp, const int blksz, unsigned char checksum)
{
	int i;
	unsigned char *cp;
	for (i = 0, cp = (unsigned char *)bufp; i < blksz; cp++, i++) 
		checksum ^= (unsigned int) *cp;	
	return(checksum);
} /* compute_checksum */

unsigned char 
compute_many_checksum(int howmany, char *buf, const int blksz)
{
	int i;
	unsigned char checksum = 0;
	/*
	 * This for-loop is the same as howmany times compute_checksum(...)
	 * but I intentionally want to use more CPU.
	 */
	for(i=0; i< howmany; i++) 
		checksum = compute_checksum(buf, blksz, checksum);
	return(checksum);
} /* compute_many_checksum */

result_t *
nextresult()
{
	static int idx = -1;

	while (TRUE) {
		idx = ++idx % NUM_RESULT;
		if (results[idx].status == RESULT_OK) {
			results[idx].status = RESULT_IN_USE;
			return( &(results[idx]) );
		}
	}

} /* nextresult */

void
aio_read_many(int fd, int num_readahead)
{
	result_t *rp;
	int i;
	int offset;
	long r;

	for (i=0; i< num_readahead; i++) {
		if (numread >= totnumread)
			return;
		offset = ((r=lrand48()) % numfileblock) * blksize;
		if (offset > filesize) {
			fprintf(stderr, "#### fatal erorr \n");
			break;
		}
		rp = nextresult();
		if (aioread(fd, rp->buf, blksize, offset, SEEK_SET, &(rp->result)) ==
			-1) {
			perror("aioread");
		}
		else {
			numread++;
			num_outstanding++;
		}
	}

	return;

} /* aio_read_many */

main(int argc, char ** argv)
{
	int fd;
	aio_result_t *rp;
	int num_readahead;
	int threshold;
	int num_checksum;
	unsigned char total_checksum = 0;
	struct stat statbuf;

	if (argc != 8) {
		fprintf(stderr, "Usage: %s <file name> \
<buffer size> <file size> <total # of read> \
<# of read ahead> <threshold> <number of checksum>\n", argv[0]);
		exit(1);
	}
	blksize = atoi(argv[2]);
	filesize = atoi(argv[3]);
	totnumread = atoi(argv[4]);
	num_readahead = atoi(argv[5]);
	threshold = atoi(argv[6]);
	num_checksum = atoi(argv[7]);

	srand48(11);
	init_results();

	if ((fd = open(argv[1], O_RDWR)) == -1) {
		perror("open");
		exit(1);
	}

	if (filesize <= 0) {
		fstat(fd, &statbuf);
		filesize = statbuf.st_size;
	}
	numfileblock = filesize / blksize;
	if (filesize % blksize)
		numfileblock++;
	if (totnumread <= 0) {
		totnumread = numfileblock;
	}

	printf("block size=%d filesize= %d numfileblock= %d\n", 
		blksize, filesize, numfileblock);
	printf("total_#_of_read = %d read_ahead=%d threshold= %d #_of_checksum= %d\n",
		totnumread, num_readahead, threshold, num_checksum);


	aio_read_many(fd, num_readahead);
	while( (rp=aiowait(NULL)) != (aio_result_t *)-1) {	/* wait for aio to complete */
		if (--num_outstanding < threshold) {
			aio_read_many(fd, num_readahead);
		}	
		if (rp->aio_return <= 0) {
			/* ignore EOF and IO errors */
			continue;
		}

		total_checksum ^= compute_many_checksum(num_checksum, 
			((result_t *) rp)->buf, rp->aio_return);
		((result_t *) rp)->status = RESULT_OK;
	}

	printf("AIO: total numread= %d checksum = %d = 0x%x\n", numread,
		(int)total_checksum, (int) total_checksum);
	exit(0);
}
