modified: Makefile
[GalaxyCodeBases.git] / c_cpp / readsdedump / prbtest.c
blob730c91cb38d15c6041705e551dbb33764ac4a237
1 /* Produced by texiweb from libavl.w. */
3 /* libavl - library for manipulation of binary trees.
4 Copyright (C) 1998-2002, 2004 Free Software Foundation, Inc.
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
11 This program is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 See the GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 02111-1307, USA.
21 The author may be contacted at <blp@gnu.org> on the Internet, or
22 write to Ben Pfaff, Stanford University, Computer Science Dept., 353
23 Serra Mall, Stanford CA 94305, USA.
26 #include <assert.h>
27 #include <limits.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <time.h>
33 #include "prbtest.h"
35 /* Insertion order. */
36 enum insert_order
38 INS_RANDOM, /* Random order. */
39 INS_ASCENDING, /* Ascending order. */
40 INS_DESCENDING, /* Descending order. */
41 INS_BALANCED, /* Balanced tree order. */
42 INS_ZIGZAG, /* Zig-zag order. */
43 INS_ASCENDING_SHIFTED, /* Ascending from middle, then beginning. */
44 INS_CUSTOM, /* Custom order. */
46 INS_CNT /* Number of insertion orders. */
49 /* Deletion order. */
50 enum delete_order
52 DEL_RANDOM, /* Random order. */
53 DEL_REVERSE, /* Reverse of insertion order. */
54 DEL_SAME, /* Same as insertion order. */
55 DEL_CUSTOM, /* Custom order. */
57 DEL_CNT /* Number of deletion orders. */
60 /* Memory tracking policy. */
61 enum mt_policy
63 MT_TRACK, /* Track allocation for leak detection. */
64 MT_NO_TRACK, /* No leak detection. */
65 MT_FAIL_COUNT, /* Fail allocations after a while. */
66 MT_FAIL_PERCENT, /* Fail allocations randomly. */
67 MT_SUBALLOC /* Suballocate from larger blocks. */
70 /* A single command-line option. */
71 struct option
73 const char *long_name; /* Long name (|"--name"|). */
74 int short_name; /* Short name (|"-n"|); value returned. */
75 int has_arg; /* Has a required argument? */
78 /* Test to perform. */
79 enum test
81 TST_CORRECTNESS, /* Default tests. */
82 TST_OVERFLOW, /* Stack overflow test. */
83 TST_NULL /* No test, just overhead. */
86 /* Program options. */
87 struct test_options
89 enum test test; /* Test to perform. */
90 enum insert_order insert_order; /* Insertion order. */
91 enum delete_order delete_order; /* Deletion order. */
93 enum mt_policy alloc_policy; /* Allocation policy. */
94 int alloc_arg[2]; /* Policy arguments. */
95 int alloc_incr; /* Amount to increment |alloc_arg| each iteration. */
97 int node_cnt; /* Number of nodes in tree. */
98 int iter_cnt; /* Number of runs. */
100 int seed_given; /* Seed provided on command line? */
101 unsigned seed; /* Random number seed. */
103 int verbosity; /* Verbosity level, 0=default. */
104 int nonstop; /* Don't stop after one error? */
107 /* Program name. */
108 char *pgm_name;
110 /* Utility functions. */
112 /* Comparison function for pointers to |int|s.
113 |param| is not used. */
115 compare_ints (const void *pa, const void *pb, void *param)
117 const int *a = pa;
118 const int *b = pb;
120 if (*a < *b)
121 return -1;
122 else if (*a > *b)
123 return +1;
124 else
125 return 0;
128 /* Prints |message| on |stderr|, which is formatted as for |printf()|,
129 and terminates the program unsuccessfully. */
130 static void
131 fail (const char *message, ...)
133 va_list args;
135 fprintf (stderr, "%s: ", pgm_name);
137 va_start (args, message);
138 vfprintf (stderr, message, args);
139 va_end (args);
141 putchar ('\n');
143 exit (EXIT_FAILURE);
146 /* Allocates and returns a pointer to |size| bytes of memory.
147 Aborts if allocation fails. */
148 static void *
149 xmalloc (size_t size)
151 void *block = malloc (size);
152 if (block == NULL && size != 0)
153 fail ("out of memory");
154 return block;
157 /* Memory tracking allocator. */
159 /* A memory block. */
160 struct block
162 struct block *next; /* Next in linked list. */
164 int idx; /* Allocation order index number. */
165 size_t size; /* Size in bytes. */
166 size_t used; /* MT_SUBALLOC: amount used so far. */
167 void *content; /* Allocated region. */
170 /* Indexes into |arg[]| within |struct mt_allocator|. */
171 enum mt_arg_index
173 MT_COUNT = 0, /* |MT_FAIL_COUNT|: Remaining successful allocations. */
174 MT_PERCENT = 0, /* |MT_FAIL_PERCENT|: Failure percentage. */
175 MT_BLOCK_SIZE = 0, /* |MT_SUBALLOC|: Size of block to suballocate. */
176 MT_ALIGN = 1 /* |MT_SUBALLOC|: Alignment of suballocated blocks. */
179 /* Memory tracking allocator. */
180 struct mt_allocator
182 struct libavl_allocator allocator; /* Allocator. Must be first member. */
184 /* Settings. */
185 enum mt_policy policy; /* Allocation policy. */
186 int arg[2]; /* Policy arguments. */
187 int verbosity; /* Message verbosity level. */
189 /* Current state. */
190 struct block *head, *tail; /* Head and tail of block list. */
191 int alloc_idx; /* Number of allocations so far. */
192 int block_cnt; /* Number of still-allocated blocks. */
195 static void *mt_allocate (struct libavl_allocator *, size_t);
196 static void mt_free (struct libavl_allocator *, void *);
198 /* Initializes the memory manager for use
199 with allocation policy |policy| and policy arguments |arg[]|,
200 at verbosity level |verbosity|, where 0 is a ``normal'' value. */
201 struct mt_allocator *
202 mt_create (enum mt_policy policy, int arg[2], int verbosity)
204 struct mt_allocator *mt = xmalloc (sizeof *mt);
206 mt->allocator.libavl_malloc = mt_allocate;
207 mt->allocator.libavl_free = mt_free;
209 mt->policy = policy;
210 mt->arg[0] = arg[0];
211 mt->arg[1] = arg[1];
212 mt->verbosity = verbosity;
214 mt->head = mt->tail = NULL;
215 mt->alloc_idx = 0;
216 mt->block_cnt = 0;
218 return mt;
221 /* Frees and destroys memory tracker |mt|,
222 reporting any memory leaks. */
223 void
224 mt_destroy (struct mt_allocator *mt)
226 assert (mt != NULL);
228 if (mt->block_cnt == 0)
230 if (mt->policy != MT_NO_TRACK && mt->verbosity >= 1)
231 printf (" No memory leaks.\n");
233 else
235 struct block *iter, *next;
237 if (mt->policy != MT_SUBALLOC)
238 printf (" Memory leaks detected:\n");
239 for (iter = mt->head; iter != NULL; iter = next)
241 if (mt->policy != MT_SUBALLOC)
242 printf (" block #%d: %lu bytes\n",
243 iter->idx, (unsigned long) iter->size);
245 next = iter->next;
246 free (iter->content);
247 free (iter);
251 free (mt);
254 /* Returns the |struct libavl_allocator| associated with |mt|. */
255 void *
256 mt_allocator (struct mt_allocator *mt)
258 return &mt->allocator;
261 /* Creates a new |struct block| containing |size| bytes of content
262 and returns a pointer to content. */
263 static void *
264 new_block (struct mt_allocator *mt, size_t size)
266 struct block *new;
268 /* Allocate and initialize new |struct block|. */
269 new = xmalloc (sizeof *new);
270 new->next = NULL;
271 new->idx = mt->alloc_idx++;
272 new->size = size;
273 new->used = 0;
274 new->content = xmalloc (size);
276 /* Add block to linked list. */
277 if (mt->head == NULL)
278 mt->head = new;
279 else
280 mt->tail->next = new;
281 mt->tail = new;
283 /* Alert user. */
284 if (mt->verbosity >= 3)
285 printf (" block #%d: allocated %lu bytes\n",
286 new->idx, (unsigned long) size);
288 /* Finish up and return. */
289 mt->block_cnt++;
290 return new->content;
293 /* Prints a message about a rejected allocation if appropriate. */
294 static void
295 reject_request (struct mt_allocator *mt, size_t size)
297 if (mt->verbosity >= 2)
298 printf (" block #%d: rejected request for %lu bytes\n",
299 mt->alloc_idx++, (unsigned long) size);
302 /* Allocates and returns a block of |size| bytes. */
303 static void *
304 mt_allocate (struct libavl_allocator *allocator, size_t size)
306 struct mt_allocator *mt = (struct mt_allocator *) allocator;
308 /* Special case. */
309 if (size == 0)
310 return NULL;
312 switch (mt->policy)
314 case MT_TRACK:
315 return new_block (mt, size);
317 case MT_NO_TRACK:
318 return xmalloc (size);
320 case MT_FAIL_COUNT:
321 if (mt->arg[MT_COUNT] == 0)
323 reject_request (mt, size);
324 return NULL;
326 mt->arg[MT_COUNT]--;
327 return new_block (mt, size);
329 case MT_FAIL_PERCENT:
330 if (rand () / (RAND_MAX / 100 + 1) < mt->arg[MT_PERCENT])
332 reject_request (mt, size);
333 return NULL;
335 else
336 return new_block (mt, size);
338 case MT_SUBALLOC:
339 if (mt->tail == NULL
340 || mt->tail->used + size > (size_t) mt->arg[MT_BLOCK_SIZE])
341 new_block (mt, mt->arg[MT_BLOCK_SIZE]);
342 if (mt->tail->used + size <= (size_t) mt->arg[MT_BLOCK_SIZE])
344 void *p = (char *) mt->tail->content + mt->tail->used;
345 size = ((size + mt->arg[MT_ALIGN] - 1)
346 / mt->arg[MT_ALIGN] * mt->arg[MT_ALIGN]);
347 mt->tail->used += size;
348 if (mt->verbosity >= 3)
349 printf (" block #%d: suballocated %lu bytes\n",
350 mt->tail->idx, (unsigned long) size);
351 return p;
353 else
354 fail ("blocksize %lu too small for %lu-byte allocation",
355 (unsigned long) mt->tail->size, (unsigned long) size);
357 default:
358 assert (0);
362 /* Releases |block| previously returned by |mt_allocate()|. */
363 static void
364 mt_free (struct libavl_allocator *allocator, void *block)
366 struct mt_allocator *mt = (struct mt_allocator *) allocator;
367 struct block *iter, *prev;
369 /* Special cases. */
370 if (block == NULL || mt->policy == MT_NO_TRACK)
372 free (block);
373 return;
375 if (mt->policy == MT_SUBALLOC)
376 return;
378 /* Search for |block| within the list of allocated blocks. */
379 for (prev = NULL, iter = mt->head; iter; prev = iter, iter = iter->next)
381 if (iter->content == block)
383 /* Block found. Remove it from the list. */
384 struct block *next = iter->next;
386 if (prev == NULL)
387 mt->head = next;
388 else
389 prev->next = next;
390 if (next == NULL)
391 mt->tail = prev;
393 /* Alert user. */
394 if (mt->verbosity >= 4)
395 printf (" block #%d: freed %lu bytes\n",
396 iter->idx, (unsigned long) iter->size);
398 /* Free block. */
399 free (iter->content);
400 free (iter);
402 /* Finish up and return. */
403 mt->block_cnt--;
404 return;
408 /* Block not in list. */
409 printf (" attempt to free unknown block %p (already freed?)\n", block);
412 /* Option parsing state. */
413 struct option_state
415 const struct option *options; /* List of options. */
416 char **arg_next; /* Remaining arguments. */
417 char *short_next; /* When non-null, unparsed short options. */
420 /* Creates and returns a command-line options parser.
421 |args| is a null-terminated array of command-line arguments, not
422 including program name. */
423 static struct option_state *
424 option_init (const struct option *options, char **args)
426 struct option_state *state;
428 assert (options != NULL && args != NULL);
430 state = xmalloc (sizeof *state);
431 state->options = options;
432 state->arg_next = args;
433 state->short_next = NULL;
435 return state;
438 /* Parses a short option whose single-character name is pointed to by
439 |state->short_next|. Advances past the option so that the next one
440 will be parsed in the next call to |option_get()|. Sets |*argp| to
441 the option's argument, if any. Returns the option's short name. */
442 static int
443 handle_short_option (struct option_state *state, char **argp)
445 const struct option *o;
447 assert (state != NULL
448 && state->short_next != NULL && *state->short_next != '\0'
449 && state->options != NULL);
451 /* Find option in |o|. */
452 for (o = state->options; ; o++)
453 if (o->long_name == NULL)
454 fail ("unknown option `-%c'; use --help for help", *state->short_next);
455 else if (o->short_name == *state->short_next)
456 break;
457 state->short_next++;
459 /* Handle argument. */
460 if (o->has_arg)
462 if (*state->arg_next == NULL || **state->arg_next == '-')
463 fail ("`-%c' requires an argument; use --help for help");
465 *argp = *state->arg_next++;
468 return o->short_name;
471 /* Parses a long option whose command-line argument is pointed to by
472 |*state->arg_next|. Advances past the option so that the next one
473 will be parsed in the next call to |option_get()|. Sets |*argp| to
474 the option's argument, if any. Returns the option's identifier. */
475 static int
476 handle_long_option (struct option_state *state, char **argp)
478 const struct option *o; /* Iterator on options. */
479 char name[16]; /* Option name. */
480 const char *arg; /* Option argument. */
482 assert (state != NULL
483 && state->arg_next != NULL && *state->arg_next != NULL
484 && state->options != NULL
485 && argp != NULL);
487 /* Copy the option name into |name|
488 and put a pointer to its argument, or |NULL| if none, into |arg|. */
490 const char *p = *state->arg_next + 2;
491 const char *q = p + strcspn (p, "=");
492 size_t name_len = q - p;
494 if (name_len > (sizeof name) - 1)
495 name_len = (sizeof name) - 1;
496 memcpy (name, p, name_len);
497 name[name_len] = '\0';
499 arg = (*q == '=') ? q + 1 : NULL;
502 /* Find option in |o|. */
503 for (o = state->options; ; o++)
504 if (o->long_name == NULL)
505 fail ("unknown option --%s; use --help for help", name);
506 else if (!strcmp (name, o->long_name))
507 break;
509 /* Make sure option has an argument if it should. */
510 if ((arg != NULL) != (o->has_arg != 0))
512 if (arg != NULL)
513 fail ("--%s can't take an argument; use --help for help", name);
514 else
515 fail ("--%s requires an argument; use --help for help", name);
518 /* Advance and return. */
519 state->arg_next++;
520 *argp = (char *) arg;
521 return o->short_name;
524 /* Retrieves the next option in the state vector |state|.
525 Returns the option's identifier, or -1 if out of options.
526 Stores the option's argument, or |NULL| if none, into |*argp|. */
527 static int
528 option_get (struct option_state *state, char **argp)
530 assert (state != NULL && argp != NULL);
532 /* No argument by default. */
533 *argp = NULL;
535 /* Deal with left-over short options. */
536 if (state->short_next != NULL)
538 if (*state->short_next != '\0')
539 return handle_short_option (state, argp);
540 else
541 state->short_next = NULL;
544 /* Out of options? */
545 if (*state->arg_next == NULL)
547 free (state);
548 return -1;
551 /* Non-option arguments not supported. */
552 if ((*state->arg_next)[0] != '-')
553 fail ("non-option arguments encountered; use --help for help");
554 if ((*state->arg_next)[1] == '\0')
555 fail ("unknown option `-'; use --help for help");
557 /* Handle the option. */
558 if ((*state->arg_next)[1] == '-')
559 return handle_long_option (state, argp);
560 else
562 state->short_next = *state->arg_next + 1;
563 state->arg_next++;
564 return handle_short_option (state, argp);
568 /* Command line parser. */
570 /* If |a| is a prefix for |b| or vice versa, returns the length of the
571 match.
572 Otherwise, returns 0. */
573 size_t
574 match_len (const char *a, const char *b)
576 size_t cnt;
578 for (cnt = 0; *a == *b && *a != '\0'; a++, b++)
579 cnt++;
581 return (*a != *b && *a != '\0' && *b != '\0') ? 0 : cnt;
584 /* |s| should point to a decimal representation of an integer.
585 Returns the value of |s|, if successful, or 0 on failure. */
586 static int
587 stoi (const char *s)
589 long x = strtol (s, NULL, 10);
590 return x >= INT_MIN && x <= INT_MAX ? x : 0;
593 /* Print helpful syntax message and exit. */
594 static void
595 usage (void)
597 static const char *help[] =
599 "bst-test, unit test for GNU libavl.\n\n",
600 "Usage: %s [OPTION]...\n\n",
601 "In the option descriptions below, CAPITAL denote arguments.\n",
602 "If a long option shows an argument as mandatory, then it is\n",
603 "mandatory for the equivalent short option also. See the GNU\n",
604 "libavl manual for more information.\n\n",
605 "-t, --test=TEST Sets test to perform. TEST is one of:\n",
606 " correctness insert/delete/... (default)\n",
607 " overflow stack overflow test\n",
608 " benchmark benchmark test\n",
609 " null no test\n",
610 "-s, --size=TREE-SIZE Sets tree size in nodes (default 16).\n",
611 "-r, --repeat=COUNT Repeats operation COUNT times (default 16).\n",
612 "-i, --insert=ORDER Sets the insertion order. ORDER is one of:\n",
613 " random random permutation (default)\n",
614 " ascending ascending order 0...n-1\n",
615 " descending descending order n-1...0\n",
616 " balanced balanced tree order\n",
617 " zigzag zig-zag tree\n",
618 " asc-shifted n/2...n-1, 0...n/2-1\n",
619 " custom custom, read from stdin\n",
620 "-d, --delete=ORDER Sets the deletion order. ORDER is one of:\n",
621 " random random permutation (default)\n",
622 " reverse reverse order of insertion\n",
623 " same same as insertion order\n",
624 " custom custom, read from stdin\n",
625 "-a, --alloc=POLICY Sets allocation policy. POLICY is one of:\n",
626 " track track memory leaks (default)\n",
627 " no-track turn off leak detection\n",
628 " fail-CNT fail after CNT allocations\n",
629 " fail%%PCT fail random PCT%% of allocations\n",
630 " sub-B,A divide B-byte blocks in A-byte units\n",
631 " (Ignored for `benchmark' test.)\n",
632 "-A, --incr=INC Fail policies: arg increment per repetition.\n",
633 "-S, --seed=SEED Sets initial number seed to SEED.\n",
634 " (default based on system time)\n",
635 "-n, --nonstop Don't stop after a single error.\n",
636 "-q, --quiet Turns down verbosity level.\n",
637 "-v, --verbose Turns up verbosity level.\n",
638 "-h, --help Displays this help screen.\n",
639 "-V, --version Reports version and copyright information.\n",
640 NULL,
643 const char **p;
644 for (p = help; *p != NULL; p++)
645 printf (*p, pgm_name);
647 exit (EXIT_SUCCESS);
650 /* Parses command-line arguments from null-terminated array |args|.
651 Sets up |options| appropriately to correspond. */
652 static void
653 parse_command_line (char **args, struct test_options *options)
655 static const struct option option_tab[] =
657 {"test", 't', 1},
658 {"insert", 'i', 1},
659 {"delete", 'd', 1},
660 {"alloc", 'a', 1},
661 {"incr", 'A', 1},
662 {"size", 's', 1},
663 {"repeat", 'r', 1},
664 {"operation", 'o', 1},
665 {"seed", 'S', 1},
666 {"nonstop", 'n', 0},
667 {"quiet", 'q', 0},
668 {"verbose", 'v', 0},
669 {"help", 'h', 0},
670 {"version", 'V', 0},
671 {NULL, 0, 0},
674 struct option_state *state;
676 /* Default options. */
677 options->test = TST_CORRECTNESS;
678 options->insert_order = INS_RANDOM;
679 options->delete_order = DEL_RANDOM;
680 options->alloc_policy = MT_TRACK;
681 options->alloc_arg[0] = 0;
682 options->alloc_arg[1] = 0;
683 options->alloc_incr = 0;
684 options->node_cnt = 15;
685 options->iter_cnt = 15;
686 options->seed_given = 0;
687 options->verbosity = 0;
688 options->nonstop = 0;
690 if (*args == NULL)
691 return;
693 state = option_init (option_tab, args + 1);
694 for (;;)
696 char *arg;
697 int id = option_get (state, &arg);
698 if (id == -1)
699 break;
701 switch (id)
703 case 't':
704 if (match_len (arg, "correctness") >= 3)
705 options->test = TST_CORRECTNESS;
706 else if (match_len (arg, "overflow") >= 3)
707 options->test = TST_OVERFLOW;
708 else if (match_len (arg, "null") >= 3)
709 options->test = TST_NULL;
710 else
711 fail ("unknown test \"%s\"", arg);
712 break;
714 case 'i':
716 static const char *orders[INS_CNT] =
718 "random", "ascending", "descending",
719 "balanced", "zigzag", "asc-shifted", "custom",
722 const char **iter;
724 assert (sizeof orders / sizeof *orders == INS_CNT);
725 for (iter = orders; ; iter++)
726 if (iter >= orders + INS_CNT)
727 fail ("unknown order \"%s\"", arg);
728 else if (match_len (*iter, arg) >= 3)
730 options->insert_order = iter - orders;
731 break;
734 break;
736 case 'd':
738 static const char *orders[DEL_CNT] =
740 "random", "reverse", "same", "custom",
743 const char **iter;
745 assert (sizeof orders / sizeof *orders == DEL_CNT);
746 for (iter = orders; ; iter++)
747 if (iter >= orders + DEL_CNT)
748 fail ("unknown order \"%s\"", arg);
749 else if (match_len (*iter, arg) >= 3)
751 options->delete_order = iter - orders;
752 break;
755 break;
757 case 'a':
758 if (match_len (arg, "track") >= 3)
759 options->alloc_policy = MT_TRACK;
760 else if (match_len (arg, "no-track") >= 3)
761 options->alloc_policy = MT_NO_TRACK;
762 else if (!strncmp (arg, "fail", 3))
764 const char *p = arg + strcspn (arg, "-%");
765 if (*p == '-')
766 options->alloc_policy = MT_FAIL_COUNT;
767 else if (*p == '%')
768 options->alloc_policy = MT_FAIL_PERCENT;
769 else
770 fail ("invalid allocation policy \"%s\"", arg);
772 options->alloc_arg[0] = stoi (p + 1);
774 else if (!strncmp (arg, "suballoc", 3))
776 const char *p = strchr (arg, '-');
777 const char *q = strchr (arg, ',');
778 if (p == NULL || q == NULL)
779 fail ("invalid allocation policy \"%s\"", arg);
781 options->alloc_policy = MT_SUBALLOC;
782 options->alloc_arg[0] = stoi (p + 1);
783 options->alloc_arg[1] = stoi (q + 1);
784 if (options->alloc_arg[MT_BLOCK_SIZE] < 32)
785 fail ("block size too small");
786 else if (options->alloc_arg[MT_ALIGN]
787 > options->alloc_arg[MT_BLOCK_SIZE])
788 fail ("alignment cannot be greater than block size");
789 else if (options->alloc_arg[MT_ALIGN] < 1)
790 fail ("alignment must be at least 1");
792 break;
794 case 'A':
795 options->alloc_incr = stoi (arg);
796 break;
798 case 's':
799 options->node_cnt = stoi (arg);
800 if (options->node_cnt < 1)
801 fail ("bad tree size \"%s\"", arg);
802 break;
804 case 'r':
805 options->iter_cnt = stoi (arg);
806 if (options->iter_cnt < 1)
807 fail ("bad repeat count \"%s\"", arg);
808 break;
810 case 'S':
811 options->seed_given = 1;
812 options->seed = strtoul (arg, NULL, 0);
813 break;
815 case 'n':
816 options->nonstop = 1;
817 break;
819 case 'q':
820 options->verbosity--;
821 break;
823 case 'v':
824 options->verbosity++;
825 break;
827 case 'h':
828 usage ();
829 break;
831 case 'V':
832 fputs ("GNU libavl 2.0.2\n"
833 "Copyright (C) 1998-2002, 2004 "
834 "Free Software Foundation, Inc.\n"
835 "This program comes with NO WARRANTY, not even for\n"
836 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
837 "You may redistribute copies under the terms of the\n"
838 "GNU General Public License. For more information on\n"
839 "these matters, see the file named COPYING.\n",
840 stdout);
841 exit (EXIT_SUCCESS);
843 default:
844 assert (0);
849 /* Fills the |n| elements of |array[]| with a random permutation of the
850 integers between |0| and |n - 1|. */
851 static void
852 permuted_integers (int array[], size_t n)
854 size_t i;
856 for (i = 0; i < n; i++)
857 array[i] = i;
859 for (i = 0; i < n; i++)
861 size_t j = i + (unsigned) rand () / (RAND_MAX / (n - i) + 1);
862 int t = array[j];
863 array[j] = array[i];
864 array[i] = t;
868 /* Generates a list of integers that produce a balanced tree when
869 inserted in order into a binary tree in the usual way.
870 |min| and |max| inclusively bound the values to be inserted.
871 Output is deposited starting at |*array|. */
872 static void
873 gen_balanced_tree (int min, int max, int **array)
875 int i;
877 if (min > max)
878 return;
880 i = (min + max + 1) / 2;
881 *(*array)++ = i;
882 gen_balanced_tree (min, i - 1, array);
883 gen_balanced_tree (i + 1, max, array);
887 /* Generates a permutation of the integers |0| to |n - 1| into
888 |insert[]| according to |insert_order|. */
889 static void
890 gen_insertions (size_t n, enum insert_order insert_order, int insert[])
892 size_t i;
894 switch (insert_order)
896 case INS_RANDOM:
897 permuted_integers (insert, n);
898 break;
900 case INS_ASCENDING:
901 for (i = 0; i < n; i++)
902 insert[i] = i;
903 break;
905 case INS_DESCENDING:
906 for (i = 0; i < n; i++)
907 insert[i] = n - i - 1;
908 break;
910 case INS_BALANCED:
911 gen_balanced_tree (0, n - 1, &insert);
912 break;
914 case INS_ZIGZAG:
915 for (i = 0; i < n; i++)
916 if (i % 2 == 0)
917 insert[i] = i / 2;
918 else
919 insert[i] = n - i / 2 - 1;
920 break;
922 case INS_ASCENDING_SHIFTED:
923 for (i = 0; i < n; i++)
925 insert[i] = i + n / 2;
926 if ((size_t) insert[i] >= n)
927 insert[i] -= n;
929 break;
931 case INS_CUSTOM:
932 for (i = 0; i < n; i++)
933 if (scanf ("%d", &insert[i]) == 0)
934 fail ("error reading insertion order from stdin");
935 break;
937 default:
938 assert (0);
942 /* Generates a permutation of the integers |0| to |n - 1| into
943 |delete[]| according to |delete_order| and |insert[]|. */
944 static void
945 gen_deletions (size_t n, enum delete_order delete_order,
946 const int *insert, int *delete)
948 size_t i;
950 switch (delete_order)
952 case DEL_RANDOM:
953 permuted_integers (delete, n);
954 break;
956 case DEL_REVERSE:
957 for (i = 0; i < n; i++)
958 delete[i] = insert[n - i - 1];
959 break;
961 case DEL_SAME:
962 for (i = 0; i < n; i++)
963 delete[i] = insert[i];
964 break;
966 case DEL_CUSTOM:
967 for (i = 0; i < n; i++)
968 if (scanf ("%d", &delete[i]) == 0)
969 fail ("error reading deletion order from stdin");
970 break;
972 default:
973 assert (0);
977 /* Choose and return an initial random seed based on the current time.
978 Based on code by Lawrence Kirby <fred@genesis.demon.co.uk>. */
979 unsigned
980 time_seed (void)
982 time_t timeval; /* Current time. */
983 unsigned char *ptr; /* Type punned pointed into timeval. */
984 unsigned seed; /* Generated seed. */
985 size_t i;
987 timeval = time (NULL);
988 ptr = (unsigned char *) &timeval;
990 seed = 0;
991 for (i = 0; i < sizeof timeval; i++)
992 seed = seed * (UCHAR_MAX + 2u) + ptr[i];
994 return seed;
998 main (int argc, char *argv[])
1000 struct test_options opts; /* Command-line options. */
1001 int *insert, *delete; /* Insertion and deletion orders. */
1002 int success; /* Everything okay so far? */
1004 /* Initialize |pgm_name|, using |argv[0]| if sensible. */
1005 pgm_name = argv[0] != NULL && argv[0][0] != '\0' ? argv[0] : "bst-test";
1007 /* Parse command line into |options|. */
1008 parse_command_line (argv, &opts);
1010 if (opts.verbosity >= 0)
1011 fputs ("bst-test for GNU libavl 2.0.2; use --help to get help.\n", stdout);
1013 if (!opts.seed_given)
1014 opts.seed = time_seed () % 32768u;
1016 insert = xmalloc (sizeof *insert * opts.node_cnt);
1017 delete = xmalloc (sizeof *delete * opts.node_cnt);
1019 /* Run the tests. */
1020 success = 1;
1021 while (opts.iter_cnt--)
1023 struct mt_allocator *alloc;
1025 if (opts.verbosity >= 0)
1027 printf ("Testing seed=%u", opts.seed);
1028 if (opts.alloc_incr)
1029 printf (", alloc arg=%d", opts.alloc_arg[0]);
1030 printf ("...\n");
1031 fflush (stdout);
1034 /* Generate insertion and deletion order.
1035 Seed them separately to ensure deletion order is
1036 independent of insertion order. */
1037 srand (opts.seed);
1038 gen_insertions (opts.node_cnt, opts.insert_order, insert);
1040 srand (++opts.seed);
1041 gen_deletions (opts.node_cnt, opts.delete_order, insert, delete);
1043 if (opts.verbosity >= 1)
1045 int i;
1047 printf (" Insertion order:");
1048 for (i = 0; i < opts.node_cnt; i++)
1049 printf (" %d", insert[i]);
1050 printf (".\n");
1052 if (opts.test == TST_CORRECTNESS)
1054 printf ("Deletion order:");
1055 for (i = 0; i < opts.node_cnt; i++)
1056 printf (" %d", delete[i]);
1057 printf (".\n");
1061 alloc = mt_create (opts.alloc_policy, opts.alloc_arg, opts.verbosity);
1064 int okay;
1065 struct libavl_allocator *a = mt_allocator (alloc);
1067 switch (opts.test)
1069 case TST_CORRECTNESS:
1070 okay = test_correctness (a, insert, delete, opts.node_cnt,
1071 opts.verbosity);
1072 break;
1074 case TST_OVERFLOW:
1075 okay = test_overflow (a, insert, opts.node_cnt, opts.verbosity);
1076 break;
1078 case TST_NULL:
1079 okay = 1;
1080 break;
1082 default:
1083 assert (0);
1086 if (okay)
1088 if (opts.verbosity >= 1)
1089 printf (" No errors.\n");
1091 else
1093 success = 0;
1094 printf (" Error!\n");
1098 mt_destroy (alloc);
1099 opts.alloc_arg[0] += opts.alloc_incr;
1101 if (!success && !opts.nonstop)
1102 break;
1105 free (delete);
1106 free (insert);
1108 return success ? EXIT_SUCCESS : EXIT_FAILURE;