- // Compile with -lpthread
- // Written by Cléo Saulnier
- #include <stdio.h>
- #include <unistd.h>
- #include <sys/wait.h>
- #include <fcntl.h>
- #include <string.h>
- #include <pthread.h>
- #include <errno.h>
- #include <sys/time.h>
- #define BUFFER_SIZE 32*1024
- #define PACKET_SIZE 1024
- class CStopWatch
- {
- private:
- typedef struct {
- timeval start;
- timeval stop;
- } stopWatch;
- stopWatch timer;
- public:
- void startTimer( ) { gettimeofday(&(timer.start),0); }
- void stopTimer( ) { gettimeofday(&(timer.stop),0); }
- double getElapsedTime()
- {
- timeval res;
- timersub(&(timer.stop),&(timer.start),&res);
- return res.tv_sec + res.tv_usec/1000000.0;
- }
- };
- // Thread stuff
- int threadID;
- int threadPID;
- char threadStack[64*1024];
- // Buffer
- struct timeval tm;
- volatile unsigned char *buffer1;
- volatile unsigned char *buffer2;
- volatile unsigned int *b1;
- volatile unsigned int *b2;
- volatile int rd2;
- volatile int rd1;
- // Communication between threads.
- int pipe1[2];
- int pipe2[2];
- // Write to pipe1
- // Read from pipe2
- int processfile(void *arg)
- {
- struct timeval tm2;
- fd_set fds2;
- FD_ZERO(&fds2);
- FD_SET(pipe2[0],&fds2);
- int count[256];
- memset(count,0,256*sizeof(int));
- for(;;)
- {
- // Wait on pipe2
- // Wait until data ready.
- char d;
- int st = read(pipe2[0],&d,1);
- if (st!=1) return 2;
- if (d==0) return 2;
- if (d==2) break;
- // Process input buffer.
- int rdd = rd1; // Read it in to avoid volatile property.
- int cnt = rdd>>2;
- unsigned int *ptr = (unsigned int*)b1;
- for(register int i=0;i<cnt;i++)
- {
- register int a = *ptr++;
- count[a&255]++;
- count[(a>>8)&255]++;
- count[(a>>16)&255]++;
- count[(a>>24)&255]++;
- }
- // Send that we are ready.
- d = 1;
- write(pipe1[1],&d,1);
- }
- // Print the results.
- int total = 0;
- for(int i='A';i<='Z';i++)
- {
- count[i]+=count[i-'A'+'a'];
- total+=count[i];
- }
- double dtotal = ((double)total)/100.0;
- for(int i='A';i<='Z';i++)
- {
- printf("%c: %.3f%% %d\n",(unsigned char)i,((double)count[i])/dtotal,count[i]);
- }
- printf("total: %d\n",total);
- return 0;
- }
- int main(int argc, char **argv)
- {
- clock_t const start = clock();
- CStopWatch s;
- s.startTimer();
- // Check if filename given.
- if (argc!=2)
- {
- printf("No filename.\n");
- return 1;
- }
- int pid;
- pipe(pipe1);
- pipe(pipe2);
- memset(&tm,0,sizeof(timeval));
- // Spawn thread that will count the data.
- threadID = 1;
- if (0!=pthread_create((pthread_t*)&pid,NULL,(void* (*)(void*))&processfile,(void*)&threadID))
- {
- printf("Error creating thread.\n");
- if (errno==EAGAIN)
- {
- printf("Too many processes.\n");
- }
- else if (errno==ENOMEM)
- {
- printf("Out of memory.\n");
- }
- return 4;
- }
- else
- {
- threadPID = pid;
- }
- // Open file
- int fd = open(argv[1], O_RDONLY, 0);
- if (fd==-1)
- {
- printf("Cannot open file %s.\n",argv[1]);
- char d = 0;
- write(pipe2[1],&d,1);
- int exitcode;
- pthread_join(threadPID,(void**)&exitcode);
- return 2;
- }
- // fd_set for select()
- fd_set fds,fds2;
- FD_ZERO(&fds2);
- FD_SET(pipe1[0],&fds2);
- unsigned int b1size;
- unsigned int b2size;
- // Allocate memory to use for buffer.
- const int maxreadsize = BUFFER_SIZE/PACKET_SIZE;
- int gap = 1; // how much to increase or deacrease read size * PACKET_SIZE bytes.
- int lastdir = 1; // -1 is reduction in size.
- int readsize = 1; // buffersize to load
- unsigned char *buffer = new unsigned char[maxreadsize*PACKET_SIZE*2];
- buffer1 = buffer;
- buffer2 = buffer+(maxreadsize*PACKET_SIZE);
- b1 = (unsigned int*)buffer;
- b2 = (unsigned int*)buffer2;
- // start reading
- b1size = readsize*PACKET_SIZE;
- rd1 = read(fd, (void*)buffer1, b1size);
- if (rd1==-1)
- {
- // Read error
- close(fd);
- delete[] buffer;
- printf("Read error.\n");
- char d = 0;
- write(pipe2[1],&d,1);
- int exitcode;
- pthread_join(threadPID,(void**)&exitcode);
- return 3;
- }
- // Send ok to read.
- char d = rd1?1:2;
- write(pipe2[1],&d,1);
- int lastread;
- while(rd1 > 0)
- {
- // Start reading next group
- b2size = readsize*PACKET_SIZE;
- rd2 = read(fd,(void*)buffer2,b2size);
- if (rd2==-1)
- {
- // Read error
- printf("Read error.\n");
- char d = 0;
- write(pipe2[1],&d,1);
- int exitcode;
- pthread_join(threadPID,(void**)&exitcode);
- close(pipe1[0]);
- close(pipe1[1]);
- close(pipe2[0]);
- close(pipe2[1]);
- close(fd);
- delete[] buffer;
- return 3;
- }
- // Blank end of buffer if necessary so that our stats don't get messed up.
- if (rd2&3)
- {
- int rd3 = 4-(rd2&3);
- for(int i=0;i<rd3;i++)
- {
- buffer2[rd2+i] = '\0';
- }
- rd2+=4;
- rd2&=~3;
- }
- // Wait on pipe1
- // Wait for buffer1 to be free.
- bool increase = false;
- fd_set fds = fds2;
- struct timeval tm2 = tm;
- int r = select(pipe1[0]+1,&fds,NULL,NULL,&tm2);
- if (r<0)
- {
- char d = 0;
- write(pipe2[1],&d,1);
- break;
- }
- if (r==0)
- {
- // We have to wait.
- fds = fds2;
- int r = select(pipe1[0]+1,&fds,NULL,NULL,NULL);
- if (r<0)
- {
- char d = 0;
- write(pipe2[1],&d,1);
- break;
- }
- }
- else
- {
- increase = true;
- }
- // read
- char d;
- int st = read(pipe1[0],&d,1);
- if (st!=1)
- {
- // Pipe error
- printf("Pipe error.\n");
- char d = 0;
- write(pipe2[1],&d,1);
- int exitcode;
- pthread_join(threadPID,(void**)&exitcode);
- close(pipe1[0]);
- close(pipe1[1]);
- close(pipe2[0]);
- close(pipe2[1]);
- close(fd);
- delete[] buffer;
- return 3;
- }
- if (d==0) break;
- // Swap buffers.
- unsigned char *b = (unsigned char*)buffer1;
- buffer1 = buffer2;
- buffer2 = b;
- unsigned int *bb = (unsigned int*)b1;
- b1 = b2;
- b2 = bb;
- unsigned int s = b1size;
- b1size = b2size;
- b2size = s;
- int rr = rd1;
- rd1 = rd2;
- rd2 = rr;
- // report back that buffer1 is ready to be processed.
- d = rd1?1:2;
- write(pipe2[1],&d,1);
- // Adjust gap size.
- if (increase)
- {
- if (rd1==readsize*PACKET_SIZE)
- {
- if (lastdir!=1) {gap=1; lastdir = 1;}
- else if (gap<0x40000000) gap<<=1;
- readsize+=gap;
- if (readsize>maxreadsize) readsize = maxreadsize;
- }
- }
- else
- {
- if (lastdir!=-1) {gap=1; lastdir = -1;}
- else if (gap<0x40000000) gap<<=1;
- readsize-=gap;
- if (readsize<1) readsize = 1;
- }
- }
- int exitcode;
- pthread_join(threadPID,(void**)&exitcode);
- delete[] buffer;
- close(fd);
- close(pipe1[0]);
- close(pipe1[1]);
- close(pipe2[0]);
- close(pipe2[1]);
- s.stopTimer();
- printf("Total runtime: %f seconds\n",s.getElapsedTime());
- clock_t const end = clock();
- printf("Execution time only: %f seconds\n",(((double)end - (double)start) / CLOCKS_PER_SEC));
- return 0;
- }