/* rawsynchronous.c
 *
 * $Id: rawsynchronous.c,v 1.9 2000/12/19 02:49:01 alderman Exp $
 *
 * Read synchronously from raw filesystem.
 *
 * Current max: 59 streams.
 */

/* #define _FILE_OFFSET_BITS 64 */
// #define DEBUG

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "global.h"
#include "options.h"
#include "timer.h"
#include "since.h"

// #define USESMALLREADS
// #define DEBUG

/* The version has a single thread that reads several streams
 * synchronously.
 */

struct streamdata {		/* data structure for each stream */
	int fileno;		/* The filenumber for this stream */
	int pos;		/* The position in the file */
#ifdef __SOLARIS__
	off64_t offset;		/* The file's offset */
#else
	off_t offset;		
#endif /* __SOLARIS__ */
} streamdata[MAX_STREAMS];

/* The device file descriptor */
int dev_fd;

/* This is the size of the file in bytes.  Hardcoded. */
int how_big;

// int readStream(int streamno);
int readMeg(char *buf, int fd);


int main(int argc, char *argv[]) {
	int i = 0, j = 0;
#ifdef DO_SUM
        int t = 0;
        char sum = 0;
#endif
	char buf[MAX_BUF_SIZE];
	int ctr = 0, errctr = 0;
        
	/* see options.c */
	extern int block_size, num_streams, bit_rate, num_blocks;


	/* see options.c */
        parseArgs(argc, argv);
        if(validateParams() != VALIDATION_SUCCESS) {
                printUsage();
                exit(1);
        }

	how_big = num_blocks;

        /* Seed the random number generator */
        srandom(1);

	/* Open the device */
#ifdef __SOLARIS__
        dev_fd = open64(RAWDEV, O_RDONLY | O_LARGEFILE, NULL);
#else
        dev_fd = open(RAWDEV, O_RDONLY, NULL);
#endif /* __SOLARIS__ */
        if(dev_fd < 1) { 
                printf("open error");
                exit(1);
        }

	/* Initialize the data structures which contain per stream info
	 * including opening files */
	for (i = 0; i < num_streams; i++) {
		streamdata[i].fileno = i;
                /* Each stream starts at a random position within it's buffer area for a more realistic simulation */
                streamdata[i].pos = random() % how_big;
                /* Each stream's buffer area is spaced across the whole disk.
                 * The disks we're using have DISKSIZE MEGS, so we space across that. */
		streamdata[i].offset = ( (unsigned long long) i * ((DISKSIZE / 100) / num_streams)  ) * block_size * (unsigned long long)how_big;
#ifdef DEBUG		
		fprintf(stderr,"Initialized stream: %d with offset 0x%llX and position %d\n",
                       i,streamdata[i].offset, streamdata[i].pos);
#endif
	}

#ifdef DEBUG
	printf("how_big: %d, block_size: %d, file_size: %d\n",
		how_big, block_size, how_big * block_size);
#endif
        
	/* See timer.c */
	init_timer(block_size, bit_rate);

        /* See since.c */
        init_since();

	for (i = 0; i < how_big; i++) {
                now_since();
		for(j = 0; j < num_streams; j++) {
#ifdef DEBUG
                	printf("Reading chunk %d in stream %d.\n",i, j);
#endif
                	if(readMeg(buf, j) < 0) {
                       		fprintf(stderr, "Exiting because of stream %d.\n", j);
                        	exit(1);
                	}
#ifdef DO_SUM
                        sum = 0;
                        for(t=0; t < NUMBYTES; t++) {
                                sum ^= buf[t];
                        }
                        
                        printf("Block %d, sum %d\n", ctr, sum);
#endif /* DO_SUM */
                        
#ifdef DEBUG
                	fprintf (stderr, "Stream %d block %d.\n", j, i);
#endif
		}
                since();
		++ctr;
		if(sleep_end_quantum_increment()) {
			++errctr;
		}
		printf("Period %d, %d errors\n",ctr,errctr);
        }
        end_since();
        printf("Total errors: %d\n", errctr);
	exit(0);
}

int readMeg(char *buf, int fn) {
        int curByte = 0;
        int rv;
#ifdef __SOLARIS__
        off64_t offset;
#else
	off_t offset;
#endif /* __SOLARIS__ */

	extern int block_size;

        offset = streamdata[fn].offset + block_size * streamdata[fn].pos;
        while(1) {
#ifdef DEBUG
                fprintf(stderr, "Commencing read %d, %d, %d, %d, 0x%llX\n", fn, (int)&buf, block_size, curByte, offset);
#endif
#ifdef __SOLARIS__
#ifdef USESMALLREADS
//                printf("here\n");
                rv = pread64(dev_fd, buf + curByte, 8192, offset);
//                printf("%d\n",rv);
#else
                rv = pread64(dev_fd, buf + curByte, block_size - curByte, offset);
#endif /* USESMALLREADS */
#else
                rv = pread(dev_fd, buf + curByte, block_size - curByte, offset);
#endif __SOLARIS__
#ifdef DEBUG
                fprintf(stderr, "Read %d bytes.\n",rv);
#endif
                curByte += rv;
                if(rv > 0) {
                        offset += (unsigned long long)rv;
                        //fprintf(stderr, "Read %d bytes; offset now 0x%llX.\n", rv, offset);
                        if (curByte < block_size) {
                                continue;
                        } else {
                                if(++streamdata[fn].pos >= 100) {
                                        streamdata[fn].pos = 0;
                                }
                                
				return curByte;
			}
                } else if (rv == 0) {
                        fprintf(stderr, "Read Zero bytes.\n");
                        return rv;
                } else {
                        fprintf(stderr, "Read returned negative: %d.\n", rv);
                        return rv;
                }
        }
        return rv;
}
