#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <errno.h>

#define equal	!strcmp
#define equaln	!strncmp
#define MAXCOUNT	100000

#define SLASHC      '/'
#define SLASHSTR    "/"
//extern int errno;

//
// pipe [maxcount]
//
// This program does pipes.
//
// We create two execution environments and pass a token back and forth
// between them as fast as we can. We count the number and time and
// produce a context switches per second number.
//

unsigned long threadreturn;
unsigned long Pipereader(void);
pthread_t tA;

size_t atoik(char *);
void *Malloc(size_t sz);
void tstart(void);
void tend(void);
double tval(void);
unsigned long maxcount = MAXCOUNT;
size_t nbytes = 4;
char *applname = "";
int mult = 1;

unsigned long *token;
unsigned long *rtoken;

unsigned long amount = 0;
unsigned long ramount = 0;
unsigned long readtot = 0;
unsigned long writetot = 0;

unsigned long x;

double t1,t2;

int fd1[2];     // pipe file descriptors


int main(int ac, char *av[])
{
	int i;
	double t;

	applname = av[0];
    if(strchr(applname,SLASHC))
        applname = strrchr(applname,SLASHC) + 1;

	if(ac == 1) {
		printf("USAGE: %s [-x mult] count [bytes]\n", applname);
        printf("\t-x mult - mult*bytes is the size of the input\n");
        printf("\t          and output buffers (default=1).\n");
		printf("\tcount   - the number of loops.\n");
        printf("\tbytes   - the amount written through each pass.\n");
		return 0;
	}

    while(ac > 1 && av[1][0] == '-') {
        if(equaln(av[1],"-x",2)) {
            if(av[1][2])
                mult = atoik(&av[1][2]);
            else {
                ac--;
                av++;
                mult = atoik(av[1]);
            }
        }
        ac--;
        av++;
    }
	if(ac > 1 && isdigit(av[1][0])) {
		maxcount = atoik(av[1]);
		ac--;
		av++;
		if(maxcount == 0)
			maxcount = 1;
	}
	if(ac > 1 && isdigit(av[1][0])) {
		nbytes = atoik(av[1]);
		ac--;
		av++;
		if(nbytes <= 0) nbytes = 1;
	}
	token = (unsigned long *)Malloc(nbytes);
	rtoken = (unsigned long *)Malloc(nbytes);
	memset(token, '\0', nbytes);
	memset(rtoken, '\0', nbytes);

    if(pipe(fd1)) {
        printf("pipe() FAILED: errno=%d\n",errno);
        return 1;
    }
#define DEC (void *(*)(void *))
    if(pthread_create(&tA, NULL, DEC Pipereader, (void*)"A")) {
        printf("pthread_create FAILED: err=%d\n",errno);
        return 1;
    }

	tstart();
	for(i = 0; i < maxcount; i++) {
		writetot = 0;
		while(writetot < nbytes) {

            amount = write(fd1[1], &token[writetot],nbytes-writetot);
            if(amount == -1) {
                printf("write FAILED: errno=%d\n",errno);
                return 1;
            }
            //printf("write ret=%d\n",amount);
			writetot += amount;
		}
    }
	tend();
	t1 = tval();

    if(pthread_join(tA, (void **)&threadreturn)) {
        printf("pthread_join FAILED: err=%d\n",errno);
        return 1;
    }

    t2 = tval();

	printf("%s ",applname);
    printf("-x %d ",mult);
	printf("%d %d %8.3f Seconds",
			maxcount,nbytes,t2);
	printf(" -- %8.3f MB/sec\n", (((double)maxcount*(double)nbytes)/t2)/1e6);

	return 0;
}


unsigned long Pipereader(void)
{
    int i;

    for(i = 0; i < maxcount; i++) {
        readtot = 0;
        while(readtot < nbytes) {
            ramount = read(fd1[0], &rtoken[readtot], nbytes-readtot);
            if(ramount == -1) {
                printf("read FAILED: errno=%d\n",errno);
                return 1;
            }
            else if(ramount == 0)
                break;
            //printf("read ret=%d (nbytes=%d)\n",ramount,nbytes);
            readtot += ramount;
        }
    }
    pthread_exit(0);
}

#include <ctype.h>

size_t atoik(char *s)
{
	size_t ret = 0;
	size_t base;

	if(*s == '0') {
		base = 8;
		if(*++s == 'x' || *s == 'X') {
			base = 16;
			s++;
		}
	}
	else
		base = 10;

	for(; isxdigit(*s); s++) {
		if(base == 16)
			if(isalpha(*s))
				ret = base*ret + (toupper(*s) - 'A');
			else
				ret = base*ret + (*s - '0');
		else if(isdigit(*s))
				ret = base*ret + (*s - '0');
		else
			break;
	}
	for(; isalpha(*s); s++) {
		switch(toupper(*s)) {
		case 'K': ret *= 1024; break;
		case 'M': ret *= 1024*1024; break;
		default:
			return ret;
		}
	}
	return ret;
}

void *Malloc(size_t sz)
{
	char *p;

	p = (char *)malloc(sz);
	if(p == NULL) {
		(void)printf("malloc(%d) failed\n",sz);
		exit(1);
	}
	return (void *)p;
}

static struct timeval _tstart, _tend;
static struct timezone tz;

void tstart(void)
{
	gettimeofday(&_tstart, &tz);
}
void tend(void)
{
	gettimeofday(&_tend,&tz);
}

double tval()
{
	double t1, t2;

	t1 =  (double)_tstart.tv_sec + (double)_tstart.tv_usec/(1000*1000);
	t2 =  (double)_tend.tv_sec + (double)_tend.tv_usec/(1000*1000);
	return t2-t1;
}
