1 /*
2 Written by Jeffrey Katcher under contract to Network Appliance.
3 Copyright (C) 1997-2001
4 Network Appliance, Inc.
5 
6 This code has been successfully compiled and run by Network
7 Appliance on various platforms, including Solaris 2 on an Ultra-170,
8 and Windows NT on a Compaq ProLiant. However, this PostMark source
9 code is distributed under the Artistic License appended to the end
10 of this file. As such, no support is provided. However, please report
11 any errors to the author, Jeffrey Katcher <katcher@netapp.com>, or to
12 Andy Watson <watson@netapp.com>.
13 
14 Versions:
15 1.00 - Original release - 8/17/97
16 
17 1.01 - Fixed endless loop on EOF,
18        Divide by zero when file_size_high=file_size_low - 10/29/97
19        (Thanks to Chuck Murnane)
20 
21 1.1 - Added new commands to distribute work across multiple directories
22       and/or file systems and multiple work subdirectories.
23 
24       Changed set location command (+,-) to allow file systems & weights
25       Added set subdirectories command and code to distribute work across
26          multiple subdirectories
27       Added file redirect to show and run commands
28       Improved help system - 4/8/98
29 
30 1.11 - Fixed unfortunate problem where read_file opens in append mode thus
31        avoiding actual reads.  (Thanks to Kent Peacock)
32 
33 1.12 - Changed bytes read and written to float.  Hopefully this will avoid
34        overflow when very large file sizes are used.
35 
36 1.13 - Added terse report option allowing results to be easily included in
37        other things.  (Thanks to Walter Wong)
38        Also tweaked help code to allow partial matches
39 
40 1.14 - Automatically stop run if work files are depleted
41 
42 1.5 - Many people (most recently Michael Flaster) have emphasized that the
43       pseudo-random number generator was more pseudo than random.  After
44       a review of the literature and extensive benchmarking, I've replaced
45       the previous PRNG with the Mersenne Twister.  While an excellent PRNG,
46       it retains much of the performance of the previous implementation.
47       URL: http://www.math.keio.ac.jp/~matumoto/emt.html
48       Also changed MB definition to 1024KB, tweaked show command
49 
50 1.51 - Added command to load config file from CLI
51 
52 1.52 - Fixed error in time base for terse report.  (Thanks to Collin Park)
53 
54 1.53 - Fixed error in report of deleted files (Thanks to Alf Wachsmann)
55 */
56 
57 #include <stdio.h>
58 #include <string.h>
59 #include <stdlib.h>
60 #include <time.h>
61 #include <fcntl.h>
62 
63 #define PM_VERSION "v1.51 : 8/14/01"
64 
65 #ifdef _WIN32
66 #include <io.h>
67 #include <direct.h>
68 
69 #define GETWD(x) getcwd(x,MAX_LINE)
70 #define MKDIR(x) mkdir(x)
71 #define SEPARATOR "\\"
72 #else
73 extern char *getwd();
74 
75 #define GETWD(x) getwd(x)
76 #define MKDIR(x) mkdir(x,0700)
77 #define SEPARATOR "/"
78 #endif
79 
80 #define MAX_LINE 255
81 #define MAX_FILENAME 80
82 
83 #define KILOBYTE 1024
84 #define MEGABYTE (KILOBYTE*KILOBYTE)
85 
86 #define PROMPT "pm>"
87 
88 typedef struct { /* ADT for table of CLI commands */
89    char *name;    /* name of command */
90    int (*func)(); /* pointer to callback function */
91    char *help;    /* descriptive help string */
92 } cmd;
93 
94 extern int cli_set_size();
95 extern int cli_set_number();
96 extern int cli_set_seed();
97 extern int cli_set_transactions();
98 extern int cli_set_location();
99 extern int cli_set_subdirs();
100 extern int cli_set_read();
101 extern int cli_set_write();
102 extern int cli_set_buffering();
103 extern int cli_set_bias_read();
104 extern int cli_set_bias_create();
105 extern int cli_set_report();
106 
107 extern int cli_run();
108 extern int cli_load();
109 extern int cli_show();
110 extern int cli_help();
111 extern int cli_quit();
112 
113 cmd command_list[]={ /* table of CLI commands */
114    {"set size",cli_set_size,"Sets low and high bounds of files"},
115    {"set number",cli_set_number,"Sets number of simultaneous files"},
116    {"set seed",cli_set_seed,"Sets seed for random number generator"},
117    {"set transactions",cli_set_transactions,"Sets number of transactions"},
118    {"set location",cli_set_location,"Sets location of working files"},
119    {"set subdirectories",cli_set_subdirs,"Sets number of subdirectories"},
120    {"set read",cli_set_read,"Sets read block size"},
121    {"set write",cli_set_write,"Sets write block size"},
122    {"set buffering",cli_set_buffering,"Sets usage of buffered I/O"},
123    {"set bias read",cli_set_bias_read,
124       "Sets the chance of choosing read over append"},
125    {"set bias create",cli_set_bias_create,
126       "Sets the chance of choosing create over delete"},
127    {"set report",cli_set_report,"Choose verbose or terse report format"},
128    {"run",cli_run,"Runs one iteration of benchmark"},
129    {"load",cli_load,"Read configuration file"},
130    {"show",cli_show,"Displays current configuration"},
131    {"help",cli_help,"Prints out available commands"},
132    {"quit",cli_quit,"Exit program"},
133    NULL
134 };
135 
136 extern void verbose_report();
137 extern void terse_report();
138 void (*reports[])()={verbose_report,terse_report};
139 
140 /* Counters */
141 int files_created;  /* number of files created */
142 int files_deleted;  /* number of files deleted */
143 int files_read;     /* number of files read */
144 int files_appended; /* number of files appended */
145 float bytes_written; /* number of bytes written to files */
146 float bytes_read;    /* number of bytes read from files */
147 
148 /* Configurable Parameters */
149 int file_size_low=500;
150 int file_size_high=10000;       /* file size: fixed or random within range */
151 int simultaneous=500;           /* simultaneous files */
152 int seed=42;                    /* random number generator seed */
153 int transactions=500;           /* number of transactions */
154 int subdirectories=0;		/* Number of subdirectories */
155 int read_block_size=512;        /* I/O block sizes */
156 int write_block_size=512;
157 int bias_read=5;                /* chance of picking read over append */
158 int bias_create=5;              /* chance of picking create over delete */
159 int buffered_io=1;              /* use C library buffered I/O */
160 int report=0;                   /* 0=verbose, 1=terse report format */
161 
162 /* Working Storage */
163 char *file_source; /* pointer to buffer of random text */
164 
165 typedef struct {
166    char name[MAX_FILENAME+1]; /* name of individual file */
167    int size;                  /* current size of file, 0 = unused file slot */
168 } file_entry;
169 
170 file_entry *file_table; /* table of files in use */
171 int file_allocated;     /* pointer to last allocated slot in file_table */
172 
173 typedef struct file_system_struct {
174    file_entry system;
175    struct file_system_struct *next,*prev;
176 } file_system;
177 
178 file_system *file_systems; /* table of file systems/directories to use */
179 int file_system_weight;    /* sum of weights for all file systems */
180 int file_system_count;     /* number of configured file systems */
181 char **location_index;     /* weighted index of file systems */
182 
183 char *read_buffer; /* temporary space for reading file data into */
184 
185 #define RND(x) ((x>0)?(genrand() % (x)):0)
186 extern unsigned long genrand();
187 extern void sgenrand();
188 
189 /* converts integer values to byte/kilobyte/megabyte strings */
scale(i)190 char *scale(i)
191 int i;
192 {
193    static char buffer[MAX_LINE]; /* storage for current conversion */
194 
195    if (i/MEGABYTE)
196       sprintf(buffer,"%.2f megabytes",(float)i/MEGABYTE);
197    else
198       if (i/KILOBYTE)
199          sprintf(buffer,"%.2f kilobytes",(float)i/KILOBYTE);
200       else
201          sprintf(buffer,"%d bytes",i);
202 
203    return(buffer);
204 }
205 
206 /* converts float values to byte/kilobyte/megabyte strings */
scalef(i)207 char *scalef(i)
208 float i;
209 {
210    static char buffer[MAX_LINE]; /* storage for current conversion */
211 
212    if (i/(float)MEGABYTE>1)
213       sprintf(buffer,"%.2f megabytes",i/(float)MEGABYTE);
214    else
215       if (i/(float)KILOBYTE)
216          sprintf(buffer,"%.2f kilobytes",i/(float)KILOBYTE);
217       else
218          sprintf(buffer,"%f bytes",i);
219 
220    return(buffer);
221 }
222 
223 /* UI callback for 'set size' command - sets range of file sizes */
cli_set_size(param)224 int cli_set_size(param)
225 char *param; /* remainder of command line */
226 {
227    char *token;
228    int size;
229 
230    if (param && (size=atoi(param))>0)
231       {
232       file_size_low=size;
233       if ((token=strchr(param,' ')) && (size=atoi(token))>0 &&
234          size>=file_size_low)
235          file_size_high=size;
236       else
237          file_size_high=file_size_low;
238       }
239    else
240       fprintf(stderr,"Error: no file size low or high bounds specified\n");
241 
242    return(1);
243 }
244 
cli_generic_int(var,param,error)245 int cli_generic_int(var,param,error)
246 int *var; /* pointer to variable to set */
247 char *param; /* remainder of command line */
248 char *error; /* error message */
249 {
250    int value;
251 
252    if (param && (value=atoi(param))>0)
253       *var=value;
254    else
255       fprintf(stderr,"Error: %s\n",error);
256 
257    return(1);
258 }
259 
260 /* UI callback for 'set number' command - sets number of files to create */
cli_set_number(param)261 int cli_set_number(param)
262 char *param; /* remainder of command line */
263 {
264    return(cli_generic_int(&simultaneous,param,"invalid number of files"));
265 }
266 
267 /* UI callback for 'set seed' command - initial value for random number gen */
cli_set_seed(param)268 int cli_set_seed(param)
269 char *param; /* remainder of command line */
270 {
271    return(cli_generic_int(&seed,param,"invalid seed for random numbers"));
272 }
273 
274 /* UI callback for 'set transactions' - configure number of transactions */
cli_set_transactions(param)275 int cli_set_transactions(param)
276 char *param; /* remainder of command line */
277 {
278    return(cli_generic_int(&transactions,param,"no transactions specified"));
279 }
280 
parse_weight(params)281 int parse_weight(params)
282 char *params;
283 {
284    int weight=1;
285    char *split;
286 
287    if (split=strrchr(params,' '))
288       {
289       *split='\0';
290       if ((weight=atoi(split+1))<=0)
291          {
292          fprintf(stderr,"Error: ignoring invalid weight '%s'\n",split+1);
293          weight=1;
294          }
295       }
296 
297    return(weight);
298 }
299 
add_location(params,weight)300 void add_location(params,weight)
301 char *params;
302 int weight;
303 {
304    file_system *new_file_system;
305 
306    if (new_file_system=(file_system *)calloc(1,sizeof(file_system)))
307       {
308       strcpy(new_file_system->system.name,params);
309       new_file_system->system.size=weight;
310 
311       if (file_systems)
312          {
313          new_file_system->prev=file_systems->prev;
314          file_systems->prev->next=new_file_system;
315          file_systems->prev=new_file_system;
316          }
317       else
318          {
319          new_file_system->prev=new_file_system;
320          file_systems=new_file_system;
321          }
322 
323       file_system_weight+=weight;
324       file_system_count++;
325       }
326 }
327 
delete_location(loc_name)328 void delete_location(loc_name)
329 char *loc_name;
330 {
331    file_system *traverse;
332 
333    for (traverse=file_systems; traverse; traverse=traverse->next)
334       if (!strcmp(traverse->system.name,loc_name))
335          {
336          file_system_weight-=traverse->system.size;
337          file_system_count--;
338 
339          if (file_systems->prev==file_systems)
340             {
341             free(file_systems);
342             file_systems=NULL;
343             }
344          else
345             {
346             if (file_systems->prev==traverse)
347                file_systems->prev=traverse->prev;
348 
349             if (traverse==file_systems)
350                file_systems=file_systems->next;
351             else
352                traverse->prev->next=traverse->next;
353 
354             if (traverse->next)
355                traverse->next->prev=traverse->prev;
356 
357             free(traverse);
358             }
359 
360          break;
361          }
362 
363    if (!traverse)
364       fprintf(stderr,"Error: cannot find location '%s'\n",loc_name);
365 }
366 
delete_locations()367 void delete_locations()
368 {
369    file_system *next;
370 
371    while (file_systems)
372       {
373       next=file_systems->next;
374       free(file_systems);
375       file_systems=next;
376       }
377 
378    file_system_weight=0;
379    file_system_count=0;
380 }
381 
382 /* UI callback for 'set location' - configure current working directory */
cli_set_location(param)383 int cli_set_location(param)
384 char *param; /* remainder of command line */
385 {
386    if (param)
387       {
388       switch (*param)
389          {
390          case '+': /* add location to list */
391             add_location(param+1,parse_weight(param+1));
392             break;
393 
394          case '-': /* remove location from list */
395             delete_location(param+1);
396             break;
397 
398          default:
399             delete_locations();
400             add_location(param,parse_weight(param));
401          }
402       }
403    else
404       fprintf(stderr,"Error: no directory name specified\n");
405 
406    return(1);
407 }
408 
409 /* UI callback for 'set subdirectories' - configure number of subdirectories */
cli_set_subdirs(param)410 int cli_set_subdirs(param)
411 char *param; /* remainder of command line */
412 {
413    return(cli_generic_int(&subdirectories,param,
414       "invalid number of subdirectories"));
415 }
416 
417 /* UI callback for 'set read' - configure read block size (integer) */
cli_set_read(param)418 int cli_set_read(param)
419 char *param; /* remainder of command line */
420 {
421    return(cli_generic_int(&read_block_size,param,"invalid read block size"));
422 }
423 
424 /* UI callback for 'set write' - configure write block size (integer) */
cli_set_write(param)425 int cli_set_write(param)
426 char *param; /* remainder of command line */
427 {
428    return(cli_generic_int(&write_block_size,param,"invalid write block size"));
429 }
430 
431 /* UI callback for 'set buffering' - sets buffering mode on or off
432    - true = buffered I/O (default), false = raw I/O */
cli_set_buffering(param)433 int cli_set_buffering(param)
434 char *param; /* remainder of command line */
435 {
436    if (param && (!strcmp(param,"true") || !strcmp(param,"false")))
437       buffered_io=(!strcmp(param,"true"))?1:0;
438    else
439       fprintf(stderr,"Error: no buffering mode (true/false) specified\n");
440 
441    return(1);
442 }
443 
444 /* UI callback for 'set bias read' - sets probability of read vs. append */
cli_set_bias_read(param)445 int cli_set_bias_read(param)
446 char *param; /* remainder of command line */
447 {
448    int value;
449 
450    if (param && (value=atoi(param))>=-1 && value<=10)
451       bias_read=value;
452    else
453       fprintf(stderr,
454         "Error: no bias specified (0-10 for greater chance,-1 to disable)\n");
455 
456    return(1);
457 }
458 
459 /* UI callback for 'set bias create' - sets probability of create vs. delete */
cli_set_bias_create(param)460 int cli_set_bias_create(param)
461 char *param; /* remainder of command line */
462 {
463    int value;
464 
465    if (param && (value=atoi(param))>=-1 && value<=10)
466       bias_create=value;
467    else
468       fprintf(stderr,
469          "Error: no bias specified (0-10 for greater chance,-1 to disable)\n");
470 
471    return(1);
472 }
473 
474 /* UI callback for 'set report' - chooses verbose or terse report formats */
cli_set_report(param)475 int cli_set_report(param)
476 char *param; /* remainder of command line */
477 {
478    int match=0;
479 
480    if (param)
481       {
482       if (!strcmp(param,"verbose"))
483          report=0;
484       else
485          if (!strcmp(param,"terse"))
486             report=1;
487          else
488             match=-1;
489       }
490 
491    if (!param || match==-1)
492       fprintf(stderr,"Error: either 'verbose' or 'terse' required\n");
493 
494    return(1);
495 }
496 
497 /* populate file source buffer with 'size' bytes of readable randomness */
initialize_file_source(size)498 char *initialize_file_source(size)
499 int size; /* number of bytes of junk to create */
500 {
501    char *new_source;
502    int i;
503 
504    if ((new_source=(char *)malloc(size))==NULL) /* allocate buffer */
505       fprintf(stderr,"Error: failed to allocate source file of size %d\n",size);
506    else
507       for (i=0; i<size; i++) /* file buffer with junk */
508          new_source[i]=32+RND(95);
509 
510    return(new_source);
511 }
512 
513 /* returns differences in times -
514    1 second is the minimum to avoid divide by zero errors */
diff_time(t1,t0)515 time_t diff_time(t1,t0)
516 time_t t1;
517 time_t t0;
518 {
519    return((t1-=t0)?t1:1);
520 }
521 
522 /* prints out results from running transactions */
verbose_report(fp,end_time,start_time,t_end_time,t_start_time,deleted)523 void verbose_report(fp,end_time,start_time,t_end_time,t_start_time,deleted)
524 FILE *fp;
525 time_t end_time,start_time,t_end_time,t_start_time; /* timers from run */
526 int deleted; /* files deleted back-to-back */
527 {
528    time_t elapsed,t_elapsed;
529    int interval;
530 
531    elapsed=diff_time(end_time,start_time);
532    t_elapsed=diff_time(t_end_time,t_start_time);
533 
534    fprintf(fp,"Time:\n");
535    fprintf(fp,"\t%d seconds total\n",elapsed);
536    fprintf(fp,"\t%d seconds of transactions (%d per second)\n",t_elapsed,
537       transactions/t_elapsed);
538 
539    fprintf(fp,"\nFiles:\n");
540    fprintf(fp,"\t%d created (%d per second)\n",files_created,
541       files_created/elapsed);
542 
543    interval=diff_time(t_start_time,start_time);
544    fprintf(fp,"\t\tCreation alone: %d files (%d per second)\n",simultaneous,
545       simultaneous/interval);
546    fprintf(fp,"\t\tMixed with transactions: %d files (%d per second)\n",
547       files_created-simultaneous,(files_created-simultaneous)/t_elapsed);
548    fprintf(fp,"\t%d read (%d per second)\n",files_read,files_read/t_elapsed);
549    fprintf(fp,"\t%d appended (%d per second)\n",files_appended,
550       files_appended/t_elapsed);
551    fprintf(fp,"\t%d deleted (%d per second)\n",files_deleted,
552       files_deleted/elapsed);
553 
554    interval=diff_time(end_time,t_end_time);
555    fprintf(fp,"\t\tDeletion alone: %d files (%d per second)\n",deleted,
556       deleted/interval);
557    fprintf(fp,"\t\tMixed with transactions: %d files (%d per second)\n",
558       files_deleted-deleted,(files_deleted-deleted)/t_elapsed);
559 
560    fprintf(fp,"\nData:\n");
561    fprintf(fp,"\t%s read ",scalef(bytes_read));
562    fprintf(fp,"(%s per second)\n",scalef(bytes_read/(float)elapsed));
563    fprintf(fp,"\t%s written ",scalef(bytes_written));
564    fprintf(fp,"(%s per second)\n",scalef(bytes_written/(float)elapsed));
565 }
566 
terse_report(fp,end_time,start_time,t_end_time,t_start_time,deleted)567 void terse_report(fp,end_time,start_time,t_end_time,t_start_time,deleted)
568 FILE *fp;
569 time_t end_time,start_time,t_end_time,t_start_time; /* timers from run */
570 int deleted; /* files deleted back-to-back */
571 {
572    time_t elapsed,t_elapsed;
573 
574    elapsed=diff_time(end_time,start_time);
575    t_elapsed=diff_time(t_end_time,t_start_time);
576 
577    fprintf(fp,"%d %d %.2f ", elapsed, t_elapsed,
578       (float)transactions/t_elapsed);
579    fprintf(fp, "%.2f %.2f %.2f ", (float)files_created/elapsed,
580       (float)simultaneous/diff_time(t_start_time,start_time),
581       (float)(files_created-simultaneous)/t_elapsed);
582    fprintf(fp, "%.2f %.2f ", (float)files_read/t_elapsed,
583       (float)files_appended/t_elapsed);
584    fprintf(fp, "%.2f %.2f %.2f ", (float)files_deleted/elapsed,
585       (float)deleted/diff_time(end_time,t_end_time),
586       (float)(files_deleted-deleted)/t_elapsed);
587    fprintf(fp, "%.2f %.2f\n", (float)bytes_read/elapsed,
588       (float)bytes_written/elapsed);
589 }
590 
591 /* returns file_table entry of unallocated file
592    - if not at end of table, then return next entry
593    - else search table for gaps */
find_free_file()594 int find_free_file()
595 {
596    int i;
597 
598    if (file_allocated<simultaneous<<1 && file_table[file_allocated].size==0)
599       return(file_allocated++);
600    else /* search entire table for holes */
601       for (i=0; i<simultaneous<<1; i++)
602          if (file_table[i].size==0)
603             {
604             file_allocated=i;
605             return(file_allocated++);
606             }
607 
608    return(-1); /* return -1 only if no free files found */
609 }
610 
611 /* write 'size' bytes to file 'fd' using unbuffered I/O and close file */
write_blocks(fd,size)612 void write_blocks(fd,size)
613 int fd;
614 int size;   /* bytes to write to file */
615 {
616    int offset=0; /* offset into file */
617    int i;
618 
619    /* write even blocks */
620    for (i=size; i>=write_block_size;
621       i-=write_block_size,offset+=write_block_size)
622       write(fd,file_source+offset,write_block_size);
623 
624    write(fd,file_source+offset,i); /* write remainder */
625 
626    bytes_written+=size; /* update counter */
627 
628    close(fd);
629 }
630 
631 /* write 'size' bytes to file 'fp' using buffered I/O and close file */
fwrite_blocks(fp,size)632 void fwrite_blocks(fp,size)
633 FILE *fp;
634 int size;   /* bytes to write to file */
635 {
636    int offset=0; /* offset into file */
637    int i;
638 
639    /* write even blocks */
640    for (i=size; i>=write_block_size;
641       i-=write_block_size,offset+=write_block_size)
642       fwrite(file_source+offset,write_block_size,1,fp);
643 
644    fwrite(file_source+offset,i,1,fp); /* write remainder */
645 
646    bytes_written+=size; /* update counter */
647 
648    fclose(fp);
649 }
650 
create_file_name(dest)651 void create_file_name(dest)
652 char *dest;
653 {
654    char conversion[MAX_LINE+1];
655 
656    *dest='\0';
657    if (file_system_count)
658       {
659       strcat(dest,
660          location_index[(file_system_count==1)?0:RND(file_system_weight)]);
661       strcat(dest,SEPARATOR);
662       }
663 
664    if (subdirectories>1)
665       {
666       sprintf(conversion,"s%d%s",RND(subdirectories),SEPARATOR);
667       strcat(dest,conversion);
668       }
669 
670    sprintf(conversion,"%d",++files_created);
671    strcat(dest,conversion);
672 }
673 
674 /* creates new file of specified length and fills it with data */
create_file(buffered)675 void create_file(buffered)
676 int buffered; /* 1=buffered I/O (default), 0=unbuffered I/O */
677 {
678    FILE *fp=NULL;
679    int fd=-1;
680    int free_file; /* file_table slot for new file */
681 
682    if ((free_file=find_free_file())!=-1) /* if file space is available */
683       { /* decide on name and initial length */
684       create_file_name(file_table[free_file].name);
685 
686       file_table[free_file].size=
687          file_size_low+RND(file_size_high-file_size_low);
688 
689       if (buffered)
690          fp=fopen(file_table[free_file].name,"w");
691       else
692          fd=open(file_table[free_file].name,O_RDWR|O_CREAT,0644);
693 
694       if (fp || fd!=-1)
695          {
696          if (buffered)
697             fwrite_blocks(fp,file_table[free_file].size);
698          else
699             write_blocks(fd,file_table[free_file].size);
700          }
701       else
702          fprintf(stderr,"Error: cannot open '%s' for writing\n",
703             file_table[free_file].name);
704       }
705 }
706 
707 /* deletes specified file from disk and file_table */
delete_file(number)708 void delete_file(number)
709 int number;
710 {
711    if (file_table[number].size)
712       {
713       if (remove(file_table[number].name))
714          fprintf(stderr,"Error: Cannot delete '%s'\n",file_table[number].name);
715       else
716          { /* reset entry in file_table and update counter */
717          file_table[number].size=0;
718          files_deleted++;
719          }
720       }
721 }
722 
723 /* reads entire specified file into temporary buffer */
read_file(number,buffered)724 void read_file(number,buffered)
725 int number;   /* number of file to read (from file_table) */
726 int buffered; /* 1=buffered I/O (default), 0=unbuffered I/O */
727 {
728    FILE *fp=NULL;
729    int fd=-1;
730    int i;
731 
732    if (buffered)
733       fp=fopen(file_table[number].name,"r");
734    else
735       fd=open(file_table[number].name,O_RDONLY,0644);
736 
737    if (fp || fd!=-1)
738       { /* read as many blocks as possible then read the remainder */
739       if (buffered)
740          {
741          for (i=file_table[number].size; i>=read_block_size; i-=read_block_size)
742             fread(read_buffer,read_block_size,1,fp);
743 
744          fread(read_buffer,i,1,fp);
745 
746          fclose(fp);
747          }
748       else
749          {
750          for (i=file_table[number].size; i>=read_block_size; i-=read_block_size)
751             read(fd,read_buffer,read_block_size);
752 
753          read(fd,read_buffer,i);
754 
755          close(fd);
756          }
757 
758       /* increment counters to record transaction */
759       bytes_read+=file_table[number].size;
760       files_read++;
761       }
762    else
763       fprintf(stderr,"Error: cannot open '%s' for reading\n",
764          file_table[number].name);
765 }
766 
767 /* appends random data to a chosen file up to the maximum configured length */
append_file(number,buffered)768 void append_file(number,buffered)
769 int number;   /* number of file (from file_table) to append date to */
770 int buffered; /* 1=buffered I/O (default), 0=unbuffered I/O */
771 {
772    FILE *fp=NULL;
773    int fd=-1;
774    int block; /* size of data to append */
775 
776    if (file_table[number].size<file_size_high)
777       {
778       if (buffered)
779          fp=fopen(file_table[number].name,"a");
780       else
781          fd=open(file_table[number].name,O_RDWR|O_APPEND,0644);
782 
783       if ((fp || fd!=-1) && file_table[number].size<file_size_high)
784          {
785          block=RND(file_size_high-file_table[number].size)+1;
786 
787          if (buffered)
788             fwrite_blocks(fp,block);
789          else
790             write_blocks(fd,block);
791 
792          file_table[number].size+=block;
793          files_appended++;
794          }
795       else
796          fprintf(stderr,"Error: cannot open '%s' for append\n",
797             file_table[number].name);
798       }
799 }
800 
801 /* finds and returns the offset of a file that is in use from the file_table */
find_used_file()802 int find_used_file() /* only called after files are created */
803 {
804    int used_file;
805 
806    while (file_table[used_file=RND(simultaneous<<1)].size==0)
807       ;
808 
809    return(used_file);
810 }
811 
812 /* reset global counters - done before each test run */
reset_counters()813 void reset_counters()
814 {
815    files_created=0;
816    files_deleted=0;
817    files_read=0;
818    files_appended=0;
819    bytes_written=0;
820    bytes_read=0;
821 }
822 
823 /* perform the configured number of file transactions
824    - a transaction consisted of either a read or append and either a
825      create or delete all chosen at random */
run_transactions(buffered)826 int run_transactions(buffered)
827 int buffered; /* 1=buffered I/O (default), 0=unbuffered I/O */
828 {
829    int percent; /* one tenth of the specified transactions */
830    int i;
831 
832    percent=transactions/10;
833    for (i=0; i<transactions; i++)
834       {
835       if (files_created==files_deleted)
836          {
837          printf("out of files!\n");
838          printf("For this workload, either increase the number of files or\n");
839          printf("decrease the number of transactions.\n");
840          break;
841          }
842 
843       if (bias_read!=-1) /* if read/append not locked out... */
844          {
845          if (RND(10)<bias_read) /* read file */
846             read_file(find_used_file(),buffered);
847          else /* append file */
848             append_file(find_used_file(),buffered);
849          }
850 
851       if (bias_create!=-1) /* if create/delete not locked out... */
852          {
853          if (RND(10)<bias_create) /* create file */
854             create_file(buffered);
855          else /* delete file */
856             delete_file(find_used_file());
857          }
858 
859       if ((i % percent)==0) /* if another tenth of the work is done...*/
860          {
861          putchar('.'); /* print progress indicator */
862          fflush(stdout);
863          }
864       }
865 
866    return(transactions-i);
867 }
868 
build_location_index(list,weight)869 char **build_location_index(list,weight)
870 file_system *list;
871 int weight;
872 {
873    char **index;
874    int count;
875    int i=0;
876 
877    if ((index=(char **)calloc(1,weight*sizeof(char *)))==NULL)
878       fprintf(stderr,"Error: cannot build weighted index of locations\n");
879    else
880       for (; list; list=list->next)
881          for (count=0; count<list->system.size; count++)
882             index[i++]=list->system.name;
883 
884    return(index);
885 }
886 
create_subdirectories(dir_list,base_dir,subdirs)887 void create_subdirectories(dir_list,base_dir,subdirs)
888 file_system *dir_list;
889 char *base_dir;
890 int subdirs;
891 {
892    char dir_name[MAX_LINE+1]; /* buffer holding subdirectory names */
893    char save_dir[MAX_LINE+1];
894    int i;
895 
896    if (dir_list)
897       {
898       for (; dir_list; dir_list=dir_list->next)
899          create_subdirectories(NULL,dir_list->system.name,subdirs);
900       }
901    else
902       {
903       if (base_dir)
904          sprintf(save_dir,"%s%s",base_dir,SEPARATOR);
905       else
906          *save_dir='\0';
907 
908       for (i=0; i<subdirs; i++)
909          {
910          sprintf(dir_name,"%ss%d",save_dir,i);
911          MKDIR(dir_name);
912          }
913       }
914 }
915 
delete_subdirectories(dir_list,base_dir,subdirs)916 void delete_subdirectories(dir_list,base_dir,subdirs)
917 file_system *dir_list;
918 char *base_dir;
919 int subdirs;
920 {
921    char dir_name[MAX_LINE+1]; /* buffer holding subdirectory names */
922    char save_dir[MAX_LINE+1];
923    int i;
924 
925    if (dir_list)
926       {
927       for (; dir_list; dir_list=dir_list->next)
928          delete_subdirectories(NULL,dir_list->system.name,subdirs);
929       }
930    else
931       {
932       if (base_dir)
933          sprintf(save_dir,"%s%s",base_dir,SEPARATOR);
934       else
935          *save_dir='\0';
936 
937       for (i=0; i<subdirs; i++)
938          {
939          sprintf(dir_name,"%ss%d",save_dir,i);
940          rmdir(dir_name);
941          }
942       }
943 }
944 
945 /* CLI callback for 'run' - benchmark execution loop */
cli_run(param)946 int cli_run(param) /* none */
947 char *param; /* unused */
948 {
949    time_t start_time,t_start_time,t_end_time,end_time; /* elapsed timers */
950    int delete_base; /* snapshot of deleted files counter */
951    FILE *fp=NULL; /* file descriptor for directing output */
952    int incomplete;
953    int i; /* generic iterator */
954 
955    reset_counters(); /* reset counters before each run */
956 
957    sgenrand(seed); /* initialize random number generator */
958 
959    /* allocate file space and fill with junk */
960    file_source=initialize_file_source(file_size_high<<1);
961 
962    /* allocate read buffer */
963    read_buffer=(char *)malloc(read_block_size);
964 
965    /* allocate table of files at 2 x simultaneous files */
966    file_allocated=0;
967    if ((file_table=(file_entry *)calloc(simultaneous<<1,sizeof(file_entry)))==
968       NULL)
969       fprintf(stderr,"Error: Failed to allocate table for %d files\n",
970          simultaneous<<1);
971 
972    if (file_system_count>0)
973       location_index=build_location_index(file_systems,file_system_weight);
974 
975    /* create subdirectories if necessary */
976    if (subdirectories>1)
977       {
978       printf("Creating subdirectories...");
979       fflush(stdout);
980       create_subdirectories(file_systems,NULL,subdirectories);
981       printf("Done\n");
982       }
983 
984    time(&start_time); /* store start time */
985 
986    /* create files in specified directory until simultaneous number */
987    printf("Creating files...");
988    fflush(stdout);
989    for (i=0; i<simultaneous; i++)
990       create_file(buffered_io);
991    printf("Done\n");
992 
993    printf("Performing transactions");
994    fflush(stdout);
995    time(&t_start_time);
996    incomplete=run_transactions(buffered_io);
997    time(&t_end_time);
998    if (!incomplete)
999       printf("Done\n");
1000 
1001    /* delete remaining files */
1002    printf("Deleting files...");
1003    fflush(stdout);
1004    delete_base=files_deleted;
1005    for (i=0; i<simultaneous<<1; i++)
1006       delete_file(i);
1007    printf("Done\n");
1008 
1009    /* print end time and difference, transaction numbers */
1010    time(&end_time);
1011 
1012    /* delete previously created subdirectories */
1013    if (subdirectories>1)
1014       {
1015       printf("Deleting subdirectories...");
1016       fflush(stdout);
1017       delete_subdirectories(file_systems,NULL,subdirectories);
1018       printf("Done\n");
1019       }
1020 
1021    if (location_index)
1022       {
1023       free(location_index);
1024       location_index=NULL;
1025       }
1026 
1027    if (param)
1028       if ((fp=fopen(param,"a"))==NULL)
1029          fprintf(stderr,"Error: Cannot direct output to file '%s'\n",param);
1030 
1031    if (!fp)
1032       fp=stdout;
1033 
1034    if (!incomplete)
1035       reports[report](fp,end_time,start_time,t_end_time,t_start_time,
1036          files_deleted-delete_base);
1037 
1038    if (param && fp!=stdout)
1039       fclose(fp);
1040 
1041    /* free resources allocated for this run */
1042    free(file_table);
1043    free(read_buffer);
1044    free(file_source);
1045 
1046    return(1); /* return 1 unless exit requested, then return 0 */
1047 }
1048 
1049 /* CLI callback for 'load' - read configuration file */
cli_load(param)1050 int cli_load(param)
1051 char *param;
1052 {
1053    char buffer[MAX_LINE+1]; /* storage for input command line */
1054 
1055    if (param)
1056       read_config_file(param,buffer,0);
1057    else
1058       fprintf(stderr,"Error: no configuration file specified\n");
1059 
1060    return(1); /* return 1 unless exit requested, then return 0 */
1061 }
1062 
1063 /* CLI callback for 'show' - print values of configuration variables */
cli_show(param)1064 int cli_show(param)
1065 char *param; /* optional: name of output file */
1066 {
1067    char current_dir[MAX_LINE+1]; /* buffer containing working directory */
1068    file_system *traverse;
1069    FILE *fp=NULL; /* file descriptor for directing output */
1070 
1071    if (param)
1072       if ((fp=fopen(param,"a"))==NULL)
1073          fprintf(stderr,"Error: Cannot direct output to file '%s'\n",param);
1074 
1075    if (!fp)
1076       fp=stdout;
1077 
1078    fprintf(fp,"Current configuration is:\n");
1079    fprintf(fp,"The base number of files is %d\n",simultaneous);
1080    fprintf(fp,"Transactions: %d\n",transactions);
1081 
1082    if (file_size_low!=file_size_high)
1083       {
1084       fprintf(fp,"Files range between %s ",scale(file_size_low));
1085       fprintf(fp,"and %s in size\n",scale(file_size_high));
1086       }
1087    else
1088       fprintf(fp,"Files are %s in size\n",scale(file_size_low));
1089 
1090    fprintf(fp,"Working director%s: %s\n",(file_system_count>1)?"ies":"y",
1091       (file_system_count==0)?GETWD(current_dir):"");
1092 
1093    for (traverse=file_systems; traverse; traverse=traverse->next)
1094       printf("\t%s (weight=%d)\n",traverse->system.name,traverse->system.size);
1095 
1096    if (subdirectories>0)
1097       fprintf(fp,"%d subdirector%s will be used\n",subdirectories,
1098          (subdirectories==1)?"y":"ies");
1099 
1100    fprintf(fp,"Block sizes are: read=%s, ",scale(read_block_size));
1101    fprintf(fp,"write=%s\n",scale(write_block_size));
1102    fprintf(fp,"Biases are: read/append=%d, create/delete=%d\n",bias_read,
1103       bias_create);
1104    fprintf(fp,"%ssing Unix buffered file I/O\n",buffered_io?"U":"Not u");
1105    fprintf(fp,"Random number generator seed is %d\n",seed);
1106 
1107    fprintf(fp,"Report format is %s.\n",report?"terse":"verbose");
1108 
1109    if (param && fp!=stdout)
1110       fclose(fp);
1111 
1112    return(1); /* return 1 unless exit requested, then return 0 */
1113 }
1114 
1115 /* CLI callback for 'quit' - returns 0 causing UI to exit */
cli_quit(param)1116 int cli_quit(param) /* none */
1117 char *param; /* unused */
1118 {
1119    return(0); /* return 1 unless exit requested, then return 0 */
1120 }
1121 
1122 /* CLI callback for 'help' - prints help strings from command_list */
cli_help(param)1123 int cli_help(param)
1124 char *param; /* optional: specific command to get help for */
1125 {
1126    int n=0; /* number of matching items */
1127    int i; /* traversal variable for command table */
1128    int len;
1129 
1130    if (param && (len=strlen(param))>0) /* if a command is specified... */
1131       for (i=0; command_list[i].name; i++) /* walk command table */
1132          if (!strncmp(command_list[i].name,param,len))
1133             {
1134             printf("%s - %s\n",command_list[i].name,command_list[i].help);
1135             n++;
1136             }
1137 
1138    if (!param || !n)
1139       for (i=0; command_list[i].name; i++) /* traverse command table */
1140          printf("%s - %s\n",command_list[i].name,command_list[i].help);
1141 
1142    return(1); /* return 1 unless exit requested, then return 0 */
1143 }
1144 
1145 /* read CLI line from user, translate aliases if any, return fgets status */
cli_read_line(buffer,size)1146 char *cli_read_line(buffer,size)
1147 char *buffer; /* empty input line */
1148 int size;
1149 {
1150    char *result;
1151 
1152    printf("%s",PROMPT);                 /* print prompt */
1153    fflush(stdout);                      /* force prompt to print */
1154    if (result=fgets(buffer,size,stdin)) /* read line safely */
1155       {
1156       buffer[strlen(buffer)-1]='\0';    /* delete final CR */
1157       if (!strcmp(buffer,"?"))           /* translate aliases */
1158          strcpy(buffer,"help");
1159       if (!strcmp(buffer,"exit"))
1160          strcpy(buffer,"quit");
1161       }
1162 
1163    return(result);                      /* return success of fgets */
1164 }
1165 
1166 /* parse CLI input line */
cli_parse_line(buffer)1167 int cli_parse_line(buffer)
1168 char *buffer; /* line of user input */
1169 {
1170    int result=1; /* default return status */
1171    int len; /* length of parsed command */
1172    int i; /* traversal variable for command table */
1173 
1174    if (*buffer=='!') /* check for shell escape */
1175       system((strlen(buffer)>1)?buffer+1:getenv("SHELL"));
1176    else
1177       {
1178       for (i=0; command_list[i].name; i++) /* walk command table */
1179          if (!strncmp(command_list[i].name,buffer,
1180             len=strlen(command_list[i].name)))
1181             { /* if command matches... */
1182             result=(command_list[i].func)
1183                (((int)strlen(buffer)>len)?buffer+len+1:NULL);
1184             break; /* call function and pass remainder of line as parameter */
1185             }
1186 
1187       if (!command_list[i].name) /* if no commands were called... */
1188          printf("Eh?\n"); /* tribute to Canadian diction */
1189       }
1190 
1191    return(result); /* return 1 unless exit requested, then return 0 */
1192 }
1193 
1194 /* read config file if present and process it line by line
1195    - if 'quit' is in file then function returns 0 */
read_config_file(filename,buffer,ignore)1196 int read_config_file(filename,buffer,ignore)
1197 char *filename; /* file name of config file */
1198 char *buffer;   /* temp storage for each line read from file */
1199 int ignore;     /* ignore file not found */
1200 {
1201    int result=1; /* default exit value - proceed with UI */
1202    FILE *fp;
1203 
1204    if (fp=fopen(filename,"r")) /* open config file */
1205       {
1206       printf("Reading configuration from file '%s'\n",filename);
1207       while (fgets(buffer,MAX_LINE,fp) && result) /* read lines until 'quit' */
1208          {
1209          buffer[strlen(buffer)-1]='\0'; /* delete final CR */
1210          result=cli_parse_line(buffer); /* process line as typed in */
1211          }
1212 
1213       fclose(fp);
1214       }
1215    else
1216       if (!ignore)
1217          fprintf(stderr,"Error: cannot read configuration file '%s'\n",
1218             filename);
1219 
1220    return(result);
1221 }
1222 
1223 /* main function - reads config files then enters get line/parse line loop */
main(argc,argv)1224 main(argc,argv)
1225 int argc;
1226 char *argv[];
1227 {
1228    char buffer[MAX_LINE+1]; /* storage for input command line */
1229 
1230    printf("PostMark %s\n",PM_VERSION);
1231    if (read_config_file((argc==2)?argv[1]:".pmrc",buffer,1))
1232       while (cli_read_line(buffer,MAX_LINE) && cli_parse_line(buffer))
1233          ;
1234 }
1235 
1236 /*
1237 
1238                          The "Artistic License"
1239 
1240                                 Preamble
1241 
1242 The intent of this document is to state the conditions under which a
1243 Package may be copied, such that the Copyright Holder maintains some
1244 semblance of artistic control over the development of the package,
1245 while giving the users of the package the right to use and distribute
1246 the Package in a more-or-less customary fashion, plus the right to make
1247 reasonable modifications.
1248 
1249 Definitions:
1250 
1251         "Package" refers to the collection of files distributed by the
1252         Copyright Holder, and derivatives of that collection of files
1253         created through textual modification.
1254 
1255         "Standard Version" refers to such a Package if it has not been
1256         modified, or has been modified in accordance with the wishes
1257         of the Copyright Holder as specified below.
1258 
1259         "Copyright Holder" is whoever is named in the copyright or
1260         copyrights for the package.
1261 
1262         "You" is you, if you're thinking about copying or distributing
1263         this Package.
1264 
1265         "Reasonable copying fee" is whatever you can justify on the
1266         basis of media cost, duplication charges, time of people involved,
1267         and so on.  (You will not be required to justify it to the
1268         Copyright Holder, but only to the computing community at large
1269         as a market that must bear the fee.)
1270 
1271         "Freely Available" means that no fee is charged for the item
1272         itself, though there may be fees involved in handling the item.
1273         It also means that recipients of the item may redistribute it
1274         under the same conditions they received it.
1275 
1276 1. You may make and give away verbatim copies of the source form of the
1277 Standard Version of this Package without restriction, provided that you
1278 duplicate all of the original copyright notices and associated disclaimers.
1279 
1280 2. You may apply bug fixes, portability fixes and other modifications
1281 derived from the Public Domain or from the Copyright Holder.  A Package
1282 modified in such a way shall still be considered the Standard Version.
1283 
1284 3. You may otherwise modify your copy of this Package in any way, provided
1285 that you insert a prominent notice in each changed file stating how and
1286 when you changed that file, and provided that you do at least ONE of the
1287 following:
1288 
1289     a) place your modifications in the Public Domain or otherwise make them
1290     Freely Available, such as by posting said modifications to Usenet or
1291     an equivalent medium, or placing the modifications on a major archive
1292     site such as uunet.uu.net, or by allowing the Copyright Holder to include
1293     your modifications in the Standard Version of the Package.
1294 
1295     b) use the modified Package only within your corporation or organization.
1296 
1297     c) rename any non-standard executables so the names do not conflict
1298     with standard executables, which must also be provided, and provide
1299     a separate manual page for each non-standard executable that clearly
1300     documents how it differs from the Standard Version.
1301 
1302     d) make other distribution arrangements with the Copyright Holder.
1303 
1304 4. You may distribute the programs of this Package in object code or
1305 executable form, provided that you do at least ONE of the following:
1306 
1307     a) distribute a Standard Version of the executables and library files,
1308     together with instructions (in the manual page or equivalent) on where
1309     to get the Standard Version.
1310 
1311     b) accompany the distribution with the machine-readable source of
1312     the Package with your modifications.
1313 
1314     c) give non-standard executables non-standard names, and clearly
1315     document the differences in manual pages (or equivalent), together
1316     with instructions on where to get the Standard Version.
1317 
1318     d) make other distribution arrangements with the Copyright Holder.
1319 
1320 5. You may charge a reasonable copying fee for any distribution of this
1321 Package.  You may charge any fee you choose for support of this
1322 Package.  You may not charge a fee for this Package itself.  However,
1323 you may distribute this Package in aggregate with other (possibly
1324 commercial) programs as part of a larger (possibly commercial) software
1325 distribution provided that you do not advertise this Package as a
1326 product of your own.  You may embed this Package's interpreter within
1327 an executable of yours (by linking); this shall be construed as a mere
1328 form of aggregation, provided that the complete Standard Version of the
1329 interpreter is so embedded.
1330 
1331 6. The scripts and library files supplied as input to or produced as
1332 output from the programs of this Package do not automatically fall
1333 under the copyright of this Package, but belong to whomever generated
1334 them, and may be sold commercially, and may be aggregated with this
1335 Package.  If such scripts or library files are aggregated with this
1336 Package via the so-called "undump" or "unexec" methods of producing a
1337 binary executable image, then distribution of such an image shall
1338 neither be construed as a distribution of this Package nor shall it
1339 fall under the restrictions of Paragraphs 3 and 4, provided that you do
1340 not represent such an executable image as a Standard Version of this
1341 Package.
1342 
1343 7. C subroutines (or comparably compiled subroutines in other
1344 languages) supplied by you and linked into this Package in order to
1345 emulate subroutines and variables of the language defined by this
1346 Package shall not be considered part of this Package, but are the
1347 equivalent of input as in Paragraph 6, provided these subroutines do
1348 not change the language in any way that would cause it to fail the
1349 regression tests for the language.
1350 
1351 8. Aggregation of this Package with a commercial distribution is always
1352 permitted provided that the use of this Package is embedded; that is,
1353 when no overt attempt is made to make this Package's interfaces visible
1354 to the end user of the commercial distribution.  Such use shall not be
1355 construed as a distribution of this Package.
1356 
1357 9. The name of the Copyright Holder may not be used to endorse or promote
1358 products derived from this software without specific prior written permission.
1359 
1360 10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
1361 IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1362 WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1363 
1364                                 The End
1365 
1366 */
1367 
1368 
1369 /* A C-program for MT19937: Integer version (1999/10/28)          */
1370 /*  genrand() generates one pseudorandom unsigned integer (32bit) */
1371 /* which is uniformly distributed among 0 to 2^32-1  for each     */
1372 /* call. sgenrand(seed) sets initial values to the working area   */
1373 /* of 624 words. Before genrand(), sgenrand(seed) must be         */
1374 /* called once. (seed is any 32-bit integer.)                     */
1375 /*   Coded by Takuji Nishimura, considering the suggestions by    */
1376 /* Topher Cooper and Marc Rieffel in July-Aug. 1997.              */
1377 
1378 /* This library is free software; you can redistribute it and/or   */
1379 /* modify it under the terms of the GNU Library General Public     */
1380 /* License as published by the Free Software Foundation; either    */
1381 /* version 2 of the License, or (at your option) any later         */
1382 /* version.                                                        */
1383 /* This library is distributed in the hope that it will be useful, */
1384 /* but WITHOUT ANY WARRANTY; without even the implied warranty of  */
1385 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.            */
1386 /* See the GNU Library General Public License for more details.    */
1387 /* You should have received a copy of the GNU Library General      */
1388 /* Public License along with this library; if not, write to the    */
1389 /* Free Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA   */
1390 /* 02111-1307  USA                                                 */
1391 
1392 /* Copyright (C) 1997, 1999 Makoto Matsumoto and Takuji Nishimura. */
1393 /* Any feedback is very welcome. For any question, comments,       */
1394 /* see http://www.math.keio.ac.jp/matumoto/emt.html or email       */
1395 /* matumoto@math.keio.ac.jp                                        */
1396 
1397 /* REFERENCE                                                       */
1398 /* M. Matsumoto and T. Nishimura,                                  */
1399 /* "Mersenne Twister: A 623-Dimensionally Equidistributed Uniform  */
1400 /* Pseudo-Random Number Generator",                                */
1401 /* ACM Transactions on Modeling and Computer Simulation,           */
1402 /* Vol. 8, No. 1, January 1998, pp 3--30.                          */
1403 
1404 /* Period parameters */
1405 #define N 624
1406 #define M 397
1407 #define MATRIX_A 0x9908b0df   /* constant vector a */
1408 #define UPPER_MASK 0x80000000 /* most significant w-r bits */
1409 #define LOWER_MASK 0x7fffffff /* least significant r bits */
1410 
1411 /* Tempering parameters */
1412 #define TEMPERING_MASK_B 0x9d2c5680
1413 #define TEMPERING_MASK_C 0xefc60000
1414 #define TEMPERING_SHIFT_U(y)  (y >> 11)
1415 #define TEMPERING_SHIFT_S(y)  (y << 7)
1416 #define TEMPERING_SHIFT_T(y)  (y << 15)
1417 #define TEMPERING_SHIFT_L(y)  (y >> 18)
1418 
1419 static unsigned long mt[N]; /* the array for the state vector  */
1420 static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */
1421 
1422 /* Initializing the array with a seed */
1423 void
sgenrand(seed)1424 sgenrand(seed)
1425     unsigned long seed;
1426 {
1427     int i;
1428 
1429     for (i=0;i<N;i++) {
1430          mt[i] = seed & 0xffff0000;
1431          seed = 69069 * seed + 1;
1432          mt[i] |= (seed & 0xffff0000) >> 16;
1433          seed = 69069 * seed + 1;
1434     }
1435     mti = N;
1436 }
1437 
1438 /* Initialization by "sgenrand()" is an example. Theoretically,      */
1439 /* there are 2^19937-1 possible states as an intial state.           */
1440 /* This function allows to choose any of 2^19937-1 ones.             */
1441 /* Essential bits in "seed_array[]" is following 19937 bits:         */
1442 /*  (seed_array[0]&UPPER_MASK), seed_array[1], ..., seed_array[N-1]. */
1443 /* (seed_array[0]&LOWER_MASK) is discarded.                          */
1444 /* Theoretically,                                                    */
1445 /*  (seed_array[0]&UPPER_MASK), seed_array[1], ..., seed_array[N-1]  */
1446 /* can take any values except all zeros.                             */
1447 void
lsgenrand(seed_array)1448 lsgenrand(seed_array)
1449     unsigned long seed_array[];
1450     /* the length of seed_array[] must be at least N */
1451 {
1452     int i;
1453 
1454     for (i=0;i<N;i++)
1455       mt[i] = seed_array[i];
1456     mti=N;
1457 }
1458 
1459 unsigned long
genrand()1460 genrand()
1461 {
1462     unsigned long y;
1463     static unsigned long mag01[2]={0x0, MATRIX_A};
1464     /* mag01[x] = x * MATRIX_A  for x=0,1 */
1465 
1466     if (mti >= N) { /* generate N words at one time */
1467         int kk;
1468 
1469         if (mti == N+1)   /* if sgenrand() has not been called, */
1470             sgenrand(4357); /* a default initial seed is used   */
1471 
1472         for (kk=0;kk<N-M;kk++) {
1473             y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
1474             mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1];
1475         }
1476         for (;kk<N-1;kk++) {
1477             y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
1478             mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1];
1479         }
1480         y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
1481         mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1];
1482 
1483         mti = 0;
1484     }
1485 
1486     y = mt[mti++];
1487     y ^= TEMPERING_SHIFT_U(y);
1488     y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B;
1489     y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C;
1490     y ^= TEMPERING_SHIFT_L(y);
1491 
1492     return y;
1493 }
1494