libavl/test.c
00001 /* Produced by texiweb from libavl.w. */
00002 
00003 /* libavl - library for manipulation of binary trees.
00004    Copyright (C) 1998-2002, 2004 Free Software Foundation, Inc.
00005 
00006    This program is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU General Public License as
00008    published by the Free Software Foundation; either version 2 of the
00009    License, or (at your option) any later version.
00010 
00011    This program is distributed in the hope that it will be useful, but
00012    WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00014    See the GNU General Public License for more details.
00015 
00016    You should have received a copy of the GNU General Public License
00017    along with this program; if not, write to the Free Software
00018    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
00019    02111-1307, USA.
00020 
00021    The author may be contacted at <blp@gnu.org> on the Internet, or
00022    write to Ben Pfaff, Stanford University, Computer Science Dept., 353
00023    Serra Mall, Stanford CA 94305, USA.
00024 */
00025 
00026 #include <assert.h>
00027 #include <limits.h>
00028 #include <stdarg.h>
00029 #include <stdio.h>
00030 #include <stdlib.h>
00031 #include <string.h>
00032 #include <time.h>
00033 #include "test.h"
00034 
00035 /* Insertion order. */
00036 enum insert_order
00037   {
00038     INS_RANDOM,                        /* Random order. */
00039     INS_ASCENDING,                /* Ascending order. */
00040     INS_DESCENDING,                /* Descending order. */
00041     INS_BALANCED,                /* Balanced tree order. */
00042     INS_ZIGZAG,                        /* Zig-zag order. */
00043     INS_ASCENDING_SHIFTED,      /* Ascending from middle, then beginning. */
00044     INS_CUSTOM,                        /* Custom order. */
00045 
00046     INS_CNT                     /* Number of insertion orders. */
00047   };
00048 
00049 /* Deletion order. */
00050 enum delete_order
00051   {
00052     DEL_RANDOM,                        /* Random order. */
00053     DEL_REVERSE,                /* Reverse of insertion order. */
00054     DEL_SAME,                        /* Same as insertion order. */
00055     DEL_CUSTOM,                        /* Custom order. */
00056 
00057     DEL_CNT                     /* Number of deletion orders. */
00058   };
00059 
00060 /* Memory tracking policy. */
00061 enum mt_policy
00062   {
00063     MT_TRACK,                        /* Track allocation for leak detection. */
00064     MT_NO_TRACK,                /* No leak detection. */
00065     MT_FAIL_COUNT,        /* Fail allocations after a while. */
00066     MT_FAIL_PERCENT,                /* Fail allocations randomly. */
00067     MT_SUBALLOC                 /* Suballocate from larger blocks. */
00068   };
00069 
00070 /* A single command-line option. */
00071 struct option
00072   {
00073     const char *long_name;        /* Long name (|"--name"|). */
00074     int short_name;                /* Short name (|"-n"|); value returned. */
00075     int has_arg;                /* Has a required argument? */
00076   };
00077 
00078 /* Test to perform. */
00079 enum test
00080   {
00081     TST_CORRECTNESS,                /* Default tests. */
00082     TST_OVERFLOW,                /* Stack overflow test. */
00083     TST_NULL                    /* No test, just overhead. */
00084   };
00085 
00086 /* Program options. */
00087 struct test_options
00088   {
00089     enum test test;                     /* Test to perform. */
00090     enum insert_order insert_order;     /* Insertion order. */
00091     enum delete_order delete_order;     /* Deletion order. */
00092 
00093     enum mt_policy alloc_policy;        /* Allocation policy. */
00094     int alloc_arg[2];                   /* Policy arguments. */
00095     int alloc_incr; /* Amount to increment |alloc_arg| each iteration. */
00096 
00097     int node_cnt;                       /* Number of nodes in tree. */
00098     int iter_cnt;                       /* Number of runs. */
00099 
00100     int seed_given;                     /* Seed provided on command line? */
00101     unsigned seed;                      /* Random number seed. */
00102 
00103     int verbosity;                      /* Verbosity level, 0=default. */
00104     int nonstop;                        /* Don't stop after one error? */
00105   };
00106 
00107 /* Program name. */
00108 char *pgm_name;
00109 
00110 /* Utility functions. */
00111 
00112 /* Comparison function for pointers to |int|s.
00113    |param| is not used. */
00114 int
00115 compare_ints (const void *pa, const void *pb, void *param)
00116 {
00117   const int *a = pa;
00118   const int *b = pb;
00119 
00120   if (*a < *b)
00121     return -1;
00122   else if (*a > *b)
00123     return +1;
00124   else
00125     return 0;
00126 }
00127 
00128 /* Prints |message| on |stderr|, which is formatted as for |printf()|,
00129    and terminates the program unsuccessfully. */
00130 static void
00131 fail (const char *message, ...)
00132 {
00133   va_list args;
00134 
00135   fprintf (stderr, "%s: ", pgm_name);
00136 
00137   va_start (args, message);
00138   vfprintf (stderr, message, args);
00139   va_end (args);
00140 
00141   putchar ('\n');
00142 
00143   exit (EXIT_FAILURE);
00144 }
00145 
00146 /* Allocates and returns a pointer to |size| bytes of memory.
00147    Aborts if allocation fails. */
00148 static void *
00149 xmalloc (size_t size)
00150 {
00151   void *block = malloc (size);
00152   if (block == NULL && size != 0)
00153     fail ("out of memory");
00154   return block;
00155 }
00156 
00157 /* Memory tracking allocator. */
00158 
00159 /* A memory block. */
00160 struct block
00161   {
00162     struct block *next;                 /* Next in linked list. */
00163 
00164     int idx;                            /* Allocation order index number. */
00165     size_t size;                        /* Size in bytes. */
00166     size_t used;                        /* MT_SUBALLOC: amount used so far. */
00167     void *content;                      /* Allocated region. */
00168   };
00169 
00170 /* Indexes into |arg[]| within |struct mt_allocator|. */
00171 enum mt_arg_index
00172   {
00173     MT_COUNT = 0,      /* |MT_FAIL_COUNT|: Remaining successful allocations. */
00174     MT_PERCENT = 0,    /* |MT_FAIL_PERCENT|: Failure percentage. */
00175     MT_BLOCK_SIZE = 0, /* |MT_SUBALLOC|: Size of block to suballocate. */
00176     MT_ALIGN = 1       /* |MT_SUBALLOC|: Alignment of suballocated blocks. */
00177   };
00178 
00179 /* Memory tracking allocator. */
00180 struct mt_allocator
00181   {
00182     struct libavl_allocator allocator;  /* Allocator.  Must be first member. */
00183 
00184     /* Settings. */
00185     enum mt_policy policy;              /* Allocation policy. */
00186     int arg[2];                         /* Policy arguments. */
00187     int verbosity;                      /* Message verbosity level. */
00188 
00189     /* Current state. */
00190     struct block *head, *tail;          /* Head and tail of block list. */
00191     int alloc_idx;                      /* Number of allocations so far. */
00192     int block_cnt;                      /* Number of still-allocated blocks. */
00193   };
00194 
00195 static void *mt_allocate (struct libavl_allocator *, size_t);
00196 static void mt_free (struct libavl_allocator *, void *);
00197 
00198 /* Initializes the memory manager for use
00199    with allocation policy |policy| and policy arguments |arg[]|,
00200    at verbosity level |verbosity|, where 0 is a ``normal'' value. */
00201 struct mt_allocator *
00202 mt_create (enum mt_policy policy, int arg[2], int verbosity)
00203 {
00204   struct mt_allocator *mt = xmalloc (sizeof *mt);
00205 
00206   mt->allocator.libavl_malloc = mt_allocate;
00207   mt->allocator.libavl_free = mt_free;
00208 
00209   mt->policy = policy;
00210   mt->arg[0] = arg[0];
00211   mt->arg[1] = arg[1];
00212   mt->verbosity = verbosity;
00213 
00214   mt->head = mt->tail = NULL;
00215   mt->alloc_idx = 0;
00216   mt->block_cnt = 0;
00217 
00218   return mt;
00219 }
00220 
00221 /* Frees and destroys memory tracker |mt|,
00222    reporting any memory leaks. */
00223 void
00224 mt_destroy (struct mt_allocator *mt)
00225 {
00226   assert (mt != NULL);
00227 
00228   if (mt->block_cnt == 0)
00229     {
00230       if (mt->policy != MT_NO_TRACK && mt->verbosity >= 1)
00231         printf ("  No memory leaks.\n");
00232     }
00233   else
00234     {
00235       struct block *iter, *next;
00236 
00237       if (mt->policy != MT_SUBALLOC)
00238         printf ("  Memory leaks detected:\n");
00239       for (iter = mt->head; iter != NULL; iter = next)
00240         {
00241           if (mt->policy != MT_SUBALLOC)
00242             printf ("    block #%d: %lu bytes\n",
00243                     iter->idx, (unsigned long) iter->size);
00244 
00245           next = iter->next;
00246           free (iter->content);
00247           free (iter);
00248         }
00249     }
00250 
00251   free (mt);
00252 }
00253 
00254 /* Returns the |struct libavl_allocator| associated with |mt|. */
00255 void *
00256 mt_allocator (struct mt_allocator *mt)
00257 {
00258   return &mt->allocator;
00259 }
00260 
00261 /* Creates a new |struct block| containing |size| bytes of content
00262    and returns a pointer to content. */
00263 static void *
00264 new_block (struct mt_allocator *mt, size_t size)
00265 {
00266   struct block *new;
00267 
00268   /* Allocate and initialize new |struct block|. */
00269   new = xmalloc (sizeof *new);
00270   new->next = NULL;
00271   new->idx = mt->alloc_idx++;
00272   new->size = size;
00273   new->used = 0;
00274   new->content = xmalloc (size);
00275 
00276   /* Add block to linked list. */
00277   if (mt->head == NULL)
00278     mt->head = new;
00279   else
00280     mt->tail->next = new;
00281   mt->tail = new;
00282 
00283   /* Alert user. */
00284   if (mt->verbosity >= 3)
00285     printf ("    block #%d: allocated %lu bytes\n",
00286             new->idx, (unsigned long) size);
00287 
00288   /* Finish up and return. */
00289   mt->block_cnt++;
00290   return new->content;
00291 }
00292 
00293 /* Prints a message about a rejected allocation if appropriate. */
00294 static void
00295 reject_request (struct mt_allocator *mt, size_t size)
00296 {
00297   if (mt->verbosity >= 2)
00298     printf ("    block #%d: rejected request for %lu bytes\n",
00299             mt->alloc_idx++, (unsigned long) size);
00300 }
00301 
00302 /* Allocates and returns a block of |size| bytes. */
00303 static void *
00304 mt_allocate (struct libavl_allocator *allocator, size_t size)
00305 {
00306   struct mt_allocator *mt = (struct mt_allocator *) allocator;
00307 
00308   /* Special case. */
00309   if (size == 0)
00310     return NULL;
00311 
00312   switch (mt->policy)
00313     {
00314     case MT_TRACK:
00315       return new_block (mt, size);
00316 
00317     case MT_NO_TRACK:
00318       return xmalloc (size);
00319 
00320     case MT_FAIL_COUNT:
00321       if (mt->arg[MT_COUNT] == 0)
00322         {
00323           reject_request (mt, size);
00324           return NULL;
00325         }
00326       mt->arg[MT_COUNT]--;
00327       return new_block (mt, size);
00328 
00329     case MT_FAIL_PERCENT:
00330       if (rand () / (RAND_MAX / 100 + 1) < mt->arg[MT_PERCENT])
00331         {
00332           reject_request (mt, size);
00333           return NULL;
00334         }
00335       else
00336         return new_block (mt, size);
00337 
00338     case MT_SUBALLOC:
00339       if (mt->tail == NULL
00340           || mt->tail->used + size > (size_t) mt->arg[MT_BLOCK_SIZE])
00341         new_block (mt, mt->arg[MT_BLOCK_SIZE]);
00342       if (mt->tail->used + size <= (size_t) mt->arg[MT_BLOCK_SIZE])
00343         {
00344           void *p = (char *) mt->tail->content + mt->tail->used;
00345           size = ((size + mt->arg[MT_ALIGN] - 1)
00346                   / mt->arg[MT_ALIGN] * mt->arg[MT_ALIGN]);
00347           mt->tail->used += size;
00348           if (mt->verbosity >= 3)
00349             printf ("    block #%d: suballocated %lu bytes\n",
00350                     mt->tail->idx, (unsigned long) size);
00351           return p;
00352         }
00353       else
00354         fail ("blocksize %lu too small for %lu-byte allocation",
00355               (unsigned long) mt->tail->size, (unsigned long) size);
00356 
00357     default:
00358       assert (0);
00359     }
00360 }
00361 
00362 /* Releases |block| previously returned by |mt_allocate()|. */
00363 static void
00364 mt_free (struct libavl_allocator *allocator, void *block)
00365 {
00366   struct mt_allocator *mt = (struct mt_allocator *) allocator;
00367   struct block *iter, *prev;
00368 
00369   /* Special cases. */
00370   if (block == NULL || mt->policy == MT_NO_TRACK)
00371     {
00372       free (block);
00373       return;
00374     }
00375   if (mt->policy == MT_SUBALLOC)
00376     return;
00377 
00378   /* Search for |block| within the list of allocated blocks. */
00379   for (prev = NULL, iter = mt->head; iter; prev = iter, iter = iter->next)
00380     {
00381       if (iter->content == block)
00382         {
00383           /* Block found.  Remove it from the list. */
00384           struct block *next = iter->next;
00385 
00386           if (prev == NULL)
00387             mt->head = next;
00388           else
00389             prev->next = next;
00390           if (next == NULL)
00391             mt->tail = prev;
00392 
00393           /* Alert user. */
00394           if (mt->verbosity >= 4)
00395             printf ("    block #%d: freed %lu bytes\n",
00396                     iter->idx, (unsigned long) iter->size);
00397 
00398           /* Free block. */
00399           free (iter->content);
00400           free (iter);
00401 
00402           /* Finish up and return. */
00403           mt->block_cnt--;
00404           return;
00405         }
00406     }
00407 
00408   /* Block not in list. */
00409   printf ("    attempt to free unknown block %p (already freed?)\n", block);
00410 }
00411 
00412 /* Option parsing state. */
00413 struct option_state
00414   {
00415     const struct option *options; /* List of options. */
00416     char **arg_next;            /* Remaining arguments. */
00417     char *short_next;           /* When non-null, unparsed short options. */
00418   };
00419 
00420 /* Creates and returns a command-line options parser.
00421    |args| is a null-terminated array of command-line arguments, not
00422    including program name. */
00423 static struct option_state *
00424 option_init (const struct option *options, char **args)
00425 {
00426   struct option_state *state;
00427 
00428   assert (options != NULL && args != NULL);
00429 
00430   state = xmalloc (sizeof *state);
00431   state->options = options;
00432   state->arg_next = args;
00433   state->short_next = NULL;
00434 
00435   return state;
00436 }
00437 
00438 /* Parses a short option whose single-character name is pointed to by
00439    |state->short_next|.  Advances past the option so that the next one
00440    will be parsed in the next call to |option_get()|.  Sets |*argp| to
00441    the option's argument, if any.  Returns the option's short name. */
00442 static int
00443 handle_short_option (struct option_state *state, char **argp)
00444 {
00445   const struct option *o;
00446 
00447   assert (state != NULL
00448           && state->short_next != NULL && *state->short_next != '\0'
00449           && state->options != NULL);
00450 
00451   /* Find option in |o|. */
00452   for (o = state->options; ; o++)
00453     if (o->long_name == NULL)
00454       fail ("unknown option `-%c'; use --help for help", *state->short_next);
00455     else if (o->short_name == *state->short_next)
00456       break;
00457   state->short_next++;
00458 
00459   /* Handle argument. */
00460   if (o->has_arg)
00461     {
00462       if (*state->arg_next == NULL || **state->arg_next == '-')
00463         fail ("`-%c' requires an argument; use --help for help");
00464 
00465       *argp = *state->arg_next++;
00466     }
00467 
00468   return o->short_name;
00469 }
00470 
00471 /* Parses a long option whose command-line argument is pointed to by
00472    |*state->arg_next|.  Advances past the option so that the next one
00473    will be parsed in the next call to |option_get()|.  Sets |*argp| to
00474    the option's argument, if any.  Returns the option's identifier. */
00475 static int
00476 handle_long_option (struct option_state *state, char **argp)
00477 {
00478   const struct option *o;        /* Iterator on options. */
00479   char name[16];                /* Option name. */
00480   const char *arg;                /* Option argument. */
00481 
00482   assert (state != NULL
00483           && state->arg_next != NULL && *state->arg_next != NULL
00484           && state->options != NULL
00485           && argp != NULL);
00486 
00487   /* Copy the option name into |name|
00488      and put a pointer to its argument, or |NULL| if none, into |arg|. */
00489   {
00490     const char *p = *state->arg_next + 2;
00491     const char *q = p + strcspn (p, "=");
00492     size_t name_len = q - p;
00493 
00494     if (name_len > (sizeof name) - 1)
00495       name_len = (sizeof name) - 1;
00496     memcpy (name, p, name_len);
00497     name[name_len] = '\0';
00498 
00499     arg = (*q == '=') ? q + 1 : NULL;
00500   }
00501 
00502   /* Find option in |o|. */
00503   for (o = state->options; ; o++)
00504     if (o->long_name == NULL)
00505       fail ("unknown option --%s; use --help for help", name);
00506     else if (!strcmp (name, o->long_name))
00507       break;
00508 
00509   /* Make sure option has an argument if it should. */
00510   if ((arg != NULL) != (o->has_arg != 0))
00511     {
00512       if (arg != NULL)
00513         fail ("--%s can't take an argument; use --help for help", name);
00514       else
00515         fail ("--%s requires an argument; use --help for help", name);
00516     }
00517 
00518   /* Advance and return. */
00519   state->arg_next++;
00520   *argp = (char *) arg;
00521   return o->short_name;
00522 }
00523 
00524 /* Retrieves the next option in the state vector |state|.
00525    Returns the option's identifier, or -1 if out of options.
00526    Stores the option's argument, or |NULL| if none, into |*argp|. */
00527 static int
00528 option_get (struct option_state *state, char **argp)
00529 {
00530   assert (state != NULL && argp != NULL);
00531 
00532   /* No argument by default. */
00533   *argp = NULL;
00534 
00535   /* Deal with left-over short options. */
00536   if (state->short_next != NULL)
00537     {
00538       if (*state->short_next != '\0')
00539         return handle_short_option (state, argp);
00540       else
00541         state->short_next = NULL;
00542     }
00543 
00544   /* Out of options? */
00545   if (*state->arg_next == NULL)
00546     {
00547       free (state);
00548       return -1;
00549     }
00550 
00551   /* Non-option arguments not supported. */
00552   if ((*state->arg_next)[0] != '-')
00553     fail ("non-option arguments encountered; use --help for help");
00554   if ((*state->arg_next)[1] == '\0')
00555     fail ("unknown option `-'; use --help for help");
00556 
00557   /* Handle the option. */
00558   if ((*state->arg_next)[1] == '-')
00559     return handle_long_option (state, argp);
00560   else
00561     {
00562       state->short_next = *state->arg_next + 1;
00563       state->arg_next++;
00564       return handle_short_option (state, argp);
00565     }
00566 }
00567 
00568 /* Command line parser. */
00569 
00570 /* If |a| is a prefix for |b| or vice versa, returns the length of the
00571    match.
00572    Otherwise, returns 0. */
00573 size_t
00574 match_len (const char *a, const char *b)
00575 {
00576   size_t cnt;
00577 
00578   for (cnt = 0; *a == *b && *a != '\0'; a++, b++)
00579     cnt++;
00580 
00581   return (*a != *b && *a != '\0' && *b != '\0') ? 0 : cnt;
00582 }
00583 
00584 /* |s| should point to a decimal representation of an integer.
00585    Returns the value of |s|, if successful, or 0 on failure. */
00586 static int
00587 stoi (const char *s)
00588 {
00589   long x = strtol (s, NULL, 10);
00590   return x >= INT_MIN && x <= INT_MAX ? x : 0;
00591 }
00592 
00593 /* Print helpful syntax message and exit. */
00594 static void
00595 usage (void)
00596 {
00597   static const char *help[] =
00598     {
00599       "bst-test, unit test for GNU libavl.\n\n",
00600       "Usage: %s [OPTION]...\n\n",
00601       "In the option descriptions below, CAPITAL denote arguments.\n",
00602       "If a long option shows an argument as mandatory, then it is\n",
00603       "mandatory for the equivalent short option also.  See the GNU\n",
00604       "libavl manual for more information.\n\n",
00605       "-t, --test=TEST     Sets test to perform.  TEST is one of:\n",
00606       "                      correctness insert/delete/... (default)\n",
00607       "                      overflow    stack overflow test\n",
00608       "                      benchmark   benchmark test\n",
00609       "                      null        no test\n",
00610       "-s, --size=TREE-SIZE  Sets tree size in nodes (default 16).\n",
00611       "-r, --repeat=COUNT  Repeats operation COUNT times (default 16).\n",
00612       "-i, --insert=ORDER  Sets the insertion order.  ORDER is one of:\n",
00613       "                      random      random permutation (default)\n",
00614       "                      ascending   ascending order 0...n-1\n",
00615       "                      descending  descending order n-1...0\n",
00616       "                      balanced    balanced tree order\n",
00617       "                      zigzag      zig-zag tree\n",
00618       "                      asc-shifted n/2...n-1, 0...n/2-1\n",
00619       "                      custom      custom, read from stdin\n",
00620       "-d, --delete=ORDER  Sets the deletion order.  ORDER is one of:\n",
00621       "                      random   random permutation (default)\n",
00622       "                      reverse  reverse order of insertion\n",
00623       "                      same     same as insertion order\n",
00624       "                      custom   custom, read from stdin\n",
00625       "-a, --alloc=POLICY  Sets allocation policy.  POLICY is one of:\n",
00626       "                      track     track memory leaks (default)\n",
00627       "                      no-track  turn off leak detection\n",
00628       "                      fail-CNT  fail after CNT allocations\n",
00629       "                      fail%%PCT  fail random PCT%% of allocations\n",
00630       "                      sub-B,A   divide B-byte blocks in A-byte units\n",
00631       "                    (Ignored for `benchmark' test.)\n",
00632       "-A, --incr=INC      Fail policies: arg increment per repetition.\n",
00633       "-S, --seed=SEED     Sets initial number seed to SEED.\n",
00634       "                    (default based on system time)\n",
00635       "-n, --nonstop       Don't stop after a single error.\n",
00636       "-q, --quiet         Turns down verbosity level.\n",
00637       "-v, --verbose       Turns up verbosity level.\n",
00638       "-h, --help          Displays this help screen.\n",
00639       "-V, --version       Reports version and copyright information.\n",
00640       NULL,
00641     };
00642 
00643   const char **p;
00644   for (p = help; *p != NULL; p++)
00645     printf (*p, pgm_name);
00646 
00647   exit (EXIT_SUCCESS);
00648 }
00649 
00650 /* Parses command-line arguments from null-terminated array |args|.
00651    Sets up |options| appropriately to correspond. */
00652 static void
00653 parse_command_line (char **args, struct test_options *options)
00654 {
00655   static const struct option option_tab[] =
00656     {
00657       {"test", 't', 1},
00658       {"insert", 'i', 1},
00659       {"delete", 'd', 1},
00660       {"alloc", 'a', 1},
00661       {"incr", 'A', 1},
00662       {"size", 's', 1},
00663       {"repeat", 'r', 1},
00664       {"operation", 'o', 1},
00665       {"seed", 'S', 1},
00666       {"nonstop", 'n', 0},
00667       {"quiet", 'q', 0},
00668       {"verbose", 'v', 0},
00669       {"help", 'h', 0},
00670       {"version", 'V', 0},
00671       {NULL, 0, 0},
00672     };
00673 
00674   struct option_state *state;
00675 
00676   /* Default options. */
00677   options->test = TST_CORRECTNESS;
00678   options->insert_order = INS_RANDOM;
00679   options->delete_order = DEL_RANDOM;
00680   options->alloc_policy = MT_TRACK;
00681   options->alloc_arg[0] = 0;
00682   options->alloc_arg[1] = 0;
00683   options->alloc_incr = 0;
00684   options->node_cnt = 15;
00685   options->iter_cnt = 15;
00686   options->seed_given = 0;
00687   options->verbosity = 0;
00688   options->nonstop = 0;
00689 
00690   if (*args == NULL)
00691     return;
00692 
00693   state = option_init (option_tab, args + 1);
00694   for (;;)
00695     {
00696       char *arg;
00697       int id = option_get (state, &arg);
00698       if (id == -1)
00699         break;
00700 
00701       switch (id)
00702         {
00703         case 't':
00704           if (match_len (arg, "correctness") >= 3)
00705             options->test = TST_CORRECTNESS;
00706           else if (match_len (arg, "overflow") >= 3)
00707             options->test = TST_OVERFLOW;
00708           else if (match_len (arg, "null") >= 3)
00709             options->test = TST_NULL;
00710           else
00711             fail ("unknown test \"%s\"", arg);
00712           break;
00713 
00714         case 'i':
00715           {
00716             static const char *orders[INS_CNT] =
00717               {
00718                 "random", "ascending", "descending",
00719                 "balanced", "zigzag", "asc-shifted", "custom",
00720               };
00721 
00722             const char **iter;
00723 
00724             assert (sizeof orders / sizeof *orders == INS_CNT);
00725             for (iter = orders; ; iter++)
00726               if (iter >= orders + INS_CNT)
00727                 fail ("unknown order \"%s\"", arg);
00728               else if (match_len (*iter, arg) >= 3)
00729                 {
00730                   options->insert_order = iter - orders;
00731                   break;
00732                 }
00733           }
00734           break;
00735 
00736         case 'd':
00737           {
00738             static const char *orders[DEL_CNT] =
00739               {
00740                 "random", "reverse", "same", "custom",
00741               };
00742 
00743             const char **iter;
00744 
00745             assert (sizeof orders / sizeof *orders == DEL_CNT);
00746             for (iter = orders; ; iter++)
00747               if (iter >= orders + DEL_CNT)
00748                 fail ("unknown order \"%s\"", arg);
00749               else if (match_len (*iter, arg) >= 3)
00750                 {
00751                   options->delete_order = iter - orders;
00752                   break;
00753                 }
00754           }
00755           break;
00756 
00757         case 'a':
00758           if (match_len (arg, "track") >= 3)
00759             options->alloc_policy = MT_TRACK;
00760           else if (match_len (arg, "no-track") >= 3)
00761             options->alloc_policy = MT_NO_TRACK;
00762           else if (!strncmp (arg, "fail", 3))
00763             {
00764               const char *p = arg + strcspn (arg, "-%");
00765               if (*p == '-')
00766                 options->alloc_policy = MT_FAIL_COUNT;
00767               else if (*p == '%')
00768                 options->alloc_policy = MT_FAIL_PERCENT;
00769               else
00770                 fail ("invalid allocation policy \"%s\"", arg);
00771 
00772               options->alloc_arg[0] = stoi (p + 1);
00773             }
00774           else if (!strncmp (arg, "suballoc", 3))
00775             {
00776               const char *p = strchr (arg, '-');
00777               const char *q = strchr (arg, ',');
00778               if (p == NULL || q == NULL)
00779                 fail ("invalid allocation policy \"%s\"", arg);
00780 
00781               options->alloc_policy = MT_SUBALLOC;
00782               options->alloc_arg[0] = stoi (p + 1);
00783               options->alloc_arg[1] = stoi (q + 1);
00784               if (options->alloc_arg[MT_BLOCK_SIZE] < 32)
00785                 fail ("block size too small");
00786               else if (options->alloc_arg[MT_ALIGN]
00787                        > options->alloc_arg[MT_BLOCK_SIZE])
00788                 fail ("alignment cannot be greater than block size");
00789               else if (options->alloc_arg[MT_ALIGN] < 1)
00790                 fail ("alignment must be at least 1");
00791             }
00792           break;
00793 
00794         case 'A':
00795           options->alloc_incr = stoi (arg);
00796           break;
00797 
00798         case 's':
00799           options->node_cnt = stoi (arg);
00800           if (options->node_cnt < 1)
00801             fail ("bad tree size \"%s\"", arg);
00802           break;
00803 
00804         case 'r':
00805           options->iter_cnt = stoi (arg);
00806           if (options->iter_cnt < 1)
00807             fail ("bad repeat count \"%s\"", arg);
00808           break;
00809 
00810         case 'S':
00811           options->seed_given = 1;
00812           options->seed = strtoul (arg, NULL, 0);
00813           break;
00814 
00815         case 'n':
00816           options->nonstop = 1;
00817           break;
00818 
00819         case 'q':
00820           options->verbosity--;
00821           break;
00822 
00823         case 'v':
00824           options->verbosity++;
00825           break;
00826 
00827         case 'h':
00828           usage ();
00829           break;
00830 
00831         case 'V':
00832           fputs ("GNU libavl 2.0.2\n"
00833                  "Copyright (C) 1998-2002, 2004 "
00834                  "Free Software Foundation, Inc.\n"
00835                  "This program comes with NO WARRANTY, not even for\n"
00836                  "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
00837                  "You may redistribute copies under the terms of the\n"
00838                  "GNU General Public License.  For more information on\n"
00839                  "these matters, see the file named COPYING.\n",
00840                  stdout);
00841           exit (EXIT_SUCCESS);
00842 
00843         default:
00844           assert (0);
00845         }
00846     }
00847 }
00848 
00849 /* Fills the |n| elements of |array[]| with a random permutation of the
00850    integers between |0| and |n - 1|. */
00851 static void
00852 permuted_integers (int array[], size_t n)
00853 {
00854   size_t i;
00855 
00856   for (i = 0; i < n; i++)
00857     array[i] = i;
00858 
00859   for (i = 0; i < n; i++)
00860     {
00861       size_t j = i + (unsigned) rand () / (RAND_MAX / (n - i) + 1);
00862       int t = array[j];
00863       array[j] = array[i];
00864       array[i] = t;
00865     }
00866 }
00867 
00868 /* Generates a list of integers that produce a balanced tree when
00869    inserted in order into a binary tree in the usual way.
00870    |min| and |max| inclusively bound the values to be inserted.
00871    Output is deposited starting at |*array|. */
00872 static void
00873 gen_balanced_tree (int min, int max, int **array)
00874 {
00875   int i;
00876 
00877   if (min > max)
00878     return;
00879 
00880   i = (min + max + 1) / 2;
00881   *(*array)++ = i;
00882   gen_balanced_tree (min, i - 1, array);
00883   gen_balanced_tree (i + 1, max, array);
00884 }
00885 
00886 
00887 /* Generates a permutation of the integers |0| to |n - 1| into
00888    |insert[]| according to |insert_order|. */
00889 static void
00890 gen_insertions (size_t n, enum insert_order insert_order, int insert[])
00891 {
00892   size_t i;
00893 
00894   switch (insert_order)
00895     {
00896     case INS_RANDOM:
00897       permuted_integers (insert, n);
00898       break;
00899 
00900     case INS_ASCENDING:
00901       for (i = 0; i < n; i++)
00902         insert[i] = i;
00903       break;
00904 
00905     case INS_DESCENDING:
00906       for (i = 0; i < n; i++)
00907         insert[i] = n - i - 1;
00908       break;
00909 
00910     case INS_BALANCED:
00911       gen_balanced_tree (0, n - 1, &insert);
00912       break;
00913 
00914     case INS_ZIGZAG:
00915       for (i = 0; i < n; i++)
00916         if (i % 2 == 0)
00917           insert[i] = i / 2;
00918         else
00919           insert[i] = n - i / 2 - 1;
00920       break;
00921 
00922     case INS_ASCENDING_SHIFTED:
00923       for (i = 0; i < n; i++)
00924         {
00925            insert[i] = i + n / 2;
00926            if ((size_t) insert[i] >= n)
00927              insert[i] -= n;
00928         }
00929       break;
00930 
00931     case INS_CUSTOM:
00932       for (i = 0; i < n; i++)
00933         if (scanf ("%d", &insert[i]) == 0)
00934           fail ("error reading insertion order from stdin");
00935       break;
00936 
00937     default:
00938       assert (0);
00939     }
00940 }
00941 
00942 /* Generates a permutation of the integers |0| to |n - 1| into
00943    |delete[]| according to |delete_order| and |insert[]|. */
00944 static void
00945 gen_deletions (size_t n, enum delete_order delete_order,
00946                const int *insert, int *delete)
00947 {
00948   size_t i;
00949 
00950   switch (delete_order)
00951     {
00952     case DEL_RANDOM:
00953       permuted_integers (delete, n);
00954       break;
00955 
00956     case DEL_REVERSE:
00957       for (i = 0; i < n; i++)
00958         delete[i] = insert[n - i - 1];
00959       break;
00960 
00961     case DEL_SAME:
00962       for (i = 0; i < n; i++)
00963         delete[i] = insert[i];
00964       break;
00965 
00966     case DEL_CUSTOM:
00967       for (i = 0; i < n; i++)
00968         if (scanf ("%d", &delete[i]) == 0)
00969           fail ("error reading deletion order from stdin");
00970       break;
00971 
00972     default:
00973       assert (0);
00974     }
00975 }
00976 
00977 /* Choose and return an initial random seed based on the current time.
00978    Based on code by Lawrence Kirby <fred@genesis.demon.co.uk>. */
00979 unsigned
00980 time_seed (void)
00981 {
00982   time_t timeval;        /* Current time. */
00983   unsigned char *ptr;        /* Type punned pointed into timeval. */
00984   unsigned seed;        /* Generated seed. */
00985   size_t i;
00986 
00987   timeval = time (NULL);
00988   ptr = (unsigned char *) &timeval;
00989 
00990   seed = 0;
00991   for (i = 0; i < sizeof timeval; i++)
00992     seed = seed * (UCHAR_MAX + 2u) + ptr[i];
00993 
00994   return seed;
00995 }
00996 
00997 int
00998 main (int argc, char *argv[])
00999 {
01000   struct test_options opts;        /* Command-line options. */
01001   int *insert, *delete;                /* Insertion and deletion orders. */
01002   int success;                  /* Everything okay so far? */
01003 
01004   /* Initialize |pgm_name|, using |argv[0]| if sensible. */
01005   pgm_name = argv[0] != NULL && argv[0][0] != '\0' ? argv[0] : "bst-test";
01006 
01007   /* Parse command line into |options|. */
01008   parse_command_line (argv, &opts);
01009 
01010   if (opts.verbosity >= 0)
01011     fputs ("bst-test for GNU libavl 2.0.2; use --help to get help.\n", stdout);
01012 
01013   if (!opts.seed_given)
01014     opts.seed = time_seed () % 32768u;
01015 
01016   insert = xmalloc (sizeof *insert * opts.node_cnt);
01017   delete = xmalloc (sizeof *delete * opts.node_cnt);
01018 
01019   /* Run the tests. */
01020   success = 1;
01021   while (opts.iter_cnt--)
01022     {
01023       struct mt_allocator *alloc;
01024 
01025       if (opts.verbosity >= 0)
01026         {
01027           printf ("Testing seed=%u", opts.seed);
01028           if (opts.alloc_incr)
01029             printf (", alloc arg=%d", opts.alloc_arg[0]);
01030           printf ("...\n");
01031           fflush (stdout);
01032         }
01033 
01034       /* Generate insertion and deletion order.
01035          Seed them separately to ensure deletion order is
01036          independent of insertion order. */
01037       srand (opts.seed);
01038       gen_insertions (opts.node_cnt, opts.insert_order, insert);
01039 
01040       srand (++opts.seed);
01041       gen_deletions (opts.node_cnt, opts.delete_order, insert, delete);
01042 
01043       if (opts.verbosity >= 1)
01044         {
01045           int i;
01046 
01047           printf ("  Insertion order:");
01048           for (i = 0; i < opts.node_cnt; i++)
01049             printf (" %d", insert[i]);
01050           printf (".\n");
01051 
01052           if (opts.test == TST_CORRECTNESS)
01053             {
01054               printf ("Deletion order:");
01055               for (i = 0; i < opts.node_cnt; i++)
01056                 printf (" %d", delete[i]);
01057               printf (".\n");
01058             }
01059         }
01060 
01061       alloc = mt_create (opts.alloc_policy, opts.alloc_arg, opts.verbosity);
01062 
01063       {
01064         int okay;
01065         struct libavl_allocator *a = mt_allocator (alloc);
01066 
01067         switch (opts.test)
01068           {
01069           case TST_CORRECTNESS:
01070             okay = test_correctness (a, insert, delete, opts.node_cnt,
01071                                      opts.verbosity);
01072             break;
01073 
01074           case TST_OVERFLOW:
01075             okay = test_overflow (a, insert, opts.node_cnt, opts.verbosity);
01076             break;
01077 
01078           case TST_NULL:
01079             okay = 1;
01080             break;
01081 
01082           default:
01083             assert (0);
01084           }
01085 
01086         if (okay)
01087           {
01088             if (opts.verbosity >= 1)
01089               printf ("  No errors.\n");
01090           }
01091         else
01092           {
01093             success = 0;
01094             printf ("  Error!\n");
01095           }
01096       }
01097 
01098       mt_destroy (alloc);
01099       opts.alloc_arg[0] += opts.alloc_incr;
01100 
01101       if (!success && !opts.nonstop)
01102         break;
01103     }
01104 
01105   free (delete);
01106   free (insert);
01107 
01108   return success ? EXIT_SUCCESS : EXIT_FAILURE;
01109 }
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Defines