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