t_allocator.cpp

Go to the documentation of this file.
00001 /*****************************************************************************\
00002 *                                                                             *
00003 *  Name   : test_array_object_allocator                                       *
00004 *  Author : Chris Koeritz                                                     *
00005 *                                                                             *
00006 *******************************************************************************
00007 * Copyright (c) 1993-$now By Author.  This program is free software; you can  *
00008 * redistribute it and/or modify it under the terms of the GNU General Public  *
00009 * License as published by the Free Software Foundation; either version 2 of   *
00010 * the License or (at your option) any later version.  This is online at:      *
00011 *     http://www.fsf.org/copyleft/gpl.html                                    *
00012 * Please send any updates to: fred@gruntose.com                               *
00013 \*****************************************************************************/
00014 
00015 // this class exercises functionality that was formerly in the object_allocator
00016 // base class for array and which is now just part of the array template.
00017 
00018 #include <basis/array.cpp>
00019 #include <basis/byte_array.h>
00020 #include <basis/chaos.h>
00021 #include <basis/function.h>
00022 #include <basis/guards.h>
00023 #include <basis/istring.h>
00024 #include <mechanisms/time_stamp.h>
00025 #include <opsystem/application_shell.h>
00026 #include <loggers/file_logger.h>
00027 #include <data_struct/static_memory_gremlin.h>
00028 #include <textual/string_manipulation.h>
00029 
00030 #define LOG(s) EMERGENCY_LOG(program_wide_logger(), s)
00031 
00032 //#define DUMP_SIZES
00033   // uncomment to see periodic dumps of the object sizes.
00034 
00035 //const int MAX_TEST_CYCLES = 5280;  // a mile of tests...
00036 const int MAX_TEST_CYCLES = 1000;  // only about a fifth a mile.
00037 
00038 const int MAX_SIMULTANEOUS_OBJECTS = 100;
00039 const int MIN_BLOCK = -20;  // negative to test range checking.
00040 const int MAX_BLOCK = 100;
00041 const int MIN_OBJECT = 10;
00042 const int MAX_OBJECT = 1200 - MAX_BLOCK;
00043 
00044 // forward.
00045 class test_object_allocator;
00046 
00047 test_object_allocator *_rooty = NIL;  // replaces global gnarliness.
00048 
00049 class test_object_allocator : public application_shell
00050 {
00051 public:
00052   test_object_allocator() : application_shell(class_name()) { _rooty = this; }
00053 
00054   IMPLEMENT_CLASS_NAME("test_object_allocator");
00055 
00056   virtual int execute();
00057 };
00058 
00059 HOOPLE_MAIN(test_object_allocator, )
00060 
00061 test_object_allocator &root_object() { return *_rooty; }
00062 
00064 
00065 class floopo
00066 {
00067 public:
00068   byte _init;
00069   istring _dreck;
00070 
00071   floopo(byte init = 0)
00072       : _init(init), _dreck(string_manipulation::make_random_name(1, 10)) {}
00073 
00074   bool operator == (const floopo &a) const { return (_init == a._init); }
00075   bool operator != (const floopo &a) const { return (_init != a._init); }
00076 
00077   operator byte() const { return _init; }
00078 };
00079 
00081 
00082 template <class contents>
00083 class bug_finder : public array<contents>, public virtual object_base
00084 {
00085 public:
00086   IMPLEMENT_CLASS_NAME("allocator_bug_finder");
00087 
00088   bug_finder(int length, u_short flags)
00089   : array<contents>(length, NIL, flags) {}
00090 
00091   bug_finder(const bug_finder &copy_from)
00092   : object_base(), array<contents>(copy_from) {}
00093 
00094   virtual ~bug_finder() {}
00095 
00096   bool operator == (const bug_finder &compare) const {
00097     if (this->length() != compare.length()) return false;
00098     return !memcmp(this->observe(), compare.observe(), this->length());
00099   }
00100   inline bool operator != (const bug_finder &compare) const
00101       { return !this->operator ==(compare); }
00102 
00103   bug_finder sub_bug(int start, int end) {
00104     if (end < start)
00105       return bug_finder(0, this->flags());
00106     bounds_return(start, 0, this->length() - 1,
00107         bug_finder(0, this->flags()));
00108     bounds_return(end, 0, this->length() - 1,
00109         bug_finder(0, this->flags()));
00110     bug_finder to_return(end - start + 1, this->flags());
00111     for (int i = start; i <= end; i++)
00112         to_return[i - start] = this->access()[i];
00113     return to_return;
00114   }
00115 
00116   bug_finder operator + (const bug_finder &a2) const {
00117     bug_finder to_return(this->length() + a2.length(), this->flags());
00118     for (int j = 0; j < this->length(); j++) to_return[j] = (*this)[j];
00119     for (int i = this->length(); i < to_return.length(); i++)
00120       to_return[i] = a2[i - this->length()];
00121     return to_return;
00122   }
00123 
00124   contents &operator [] (int index)
00125       { return *(contents *)&(this->access()[index]); }
00126   const contents &operator [] (int index) const
00127       { return *(contents *)&(this->observe()[index]); }
00128 
00129   #define use operator[]
00130   #define get operator[]
00131 
00132   void retrain(int new_size, const contents *to_copy)
00133       { array<contents>::retrain(new_size, (contents *)to_copy); }
00134 
00135   void test_allocation();
00136 };
00137 
00139 
00140 template <class contents>
00141 void bug_finder<contents>::test_allocation()
00142 {
00143   int outcome_size = sizeof(outcome);
00144   if (outcome_size != sizeof(int))
00145     deadly_error(class_name(), "outcome size check", "outcomes are larger than ints");
00146 
00147   // the space that all training for object_allocators comes from.
00148   contents *junk_space = new contents[MAX_OBJECT];
00149   for (int i = 0; i < MAX_OBJECT - 1; i++)
00150     junk_space[i] = contents(root_object().randomizer().inclusive('a', 'z'));
00151   junk_space[MAX_OBJECT - 1] = contents('\0');
00152 
00153   bug_finder<contents> *testers[MAX_SIMULTANEOUS_OBJECTS];
00154   for (int c = 0; c < MAX_SIMULTANEOUS_OBJECTS; c++) {
00155     // set up the initial object_allocator guys.
00156     testers[c] = new bug_finder<contents>(root_object().randomizer().inclusive(MIN_OBJECT,
00157         MAX_OBJECT), this->flags());
00158     if (this->flags() & byte_array::SIMPLE_COPY) {
00159       // copy the randomized junk space into the new object.
00160       memory_assign(testers[c]->access(), junk_space, testers[c]->length());
00161     }
00162   }
00163 
00164   enum actions { do_train, do_size, do_assign, do_access,
00165       do_zap, do_resizer, do_observe, do_swap, do_shrink, do_shift };
00166 
00167   time_stamp next_dump;
00168 
00169   for (int iterations = 0; iterations < MAX_TEST_CYCLES; iterations++) {
00170 #ifdef DUMP_SIZES
00171     if (time_stamp() >= next_dump) {
00172       for (int i = 0; i < MAX_SIMULTANEOUS_OBJECTS; i++) {
00173         printf("%d ", testers[i]->internal_real_length());
00174       }
00175       printf("\n\n");
00176       next_dump = time_stamp(14 * SECOND_ms);
00177     }
00178 #endif
00179     int index = root_object().randomizer().inclusive(0, MAX_SIMULTANEOUS_OBJECTS - 1);
00180     int choice = root_object().randomizer().inclusive(do_train, do_observe);
00181     switch (choice) {
00182       case do_train: {
00183         testers[index]->retrain(root_object().randomizer().inclusive(MIN_OBJECT, MAX_OBJECT),
00184             junk_space);
00185 //hmmm: compare!
00186         break;
00187       }
00188       case do_size: {
00189         bug_finder<contents> old_version = *testers[index];
00190         bool at_front = bool(root_object().randomizer().inclusive(0, 1));
00191         int new_size = root_object().randomizer().inclusive(MIN_OBJECT, MAX_OBJECT);
00192         bool smaller = new_size < old_version.length();
00193         int difference = absolute_value(new_size - old_version.length());
00194         testers[index]->resize(new_size,
00195             at_front? common::NEW_AT_BEGINNING : common::NEW_AT_END);
00196         if (!smaller && difference) {
00197           // neuter the contents in the new section so we can compare.  this
00198           // space is filled however it happens to be constructed for the
00199           // object in question.
00200           if (at_front) {
00201             if (this->flags() & byte_array::SIMPLE_COPY)
00202               memset(testers[index]->access(), 'Q', difference);
00203             else
00204               for (int i = 0; i < difference; i++)
00205                 testers[index]->use(i) = contents('Q');
00206           } else {
00207             if (this->flags() & byte_array::SIMPLE_COPY)
00208               memset(testers[index]->access() + old_version.length(), 'Q',
00209                   difference);
00210             else
00211               for (int i = old_version.length();
00212                   i < old_version.length() + difference; i++)
00213                 testers[index]->use(i) = contents('Q');
00214           }
00215         }
00216         // now compute an equivalent form of what the state should be.
00217         bug_finder<contents> equivalent(0, old_version.flags());
00218         if (at_front) {
00219           if (smaller) {
00220             equivalent = old_version.sub_bug(difference,
00221                 old_version.length() - 1);
00222           } else {
00223             bug_finder<contents> blank(difference, old_version.flags());
00224             if (this->flags() & byte_array::SIMPLE_COPY)
00225               memset(blank.access(), 'Q', blank.length());
00226             else
00227               for (int i = 0; i < blank.length(); i++)
00228                 blank.use(i) = contents('Q');
00229             equivalent = blank + old_version;
00230           }
00231         } else {
00232           if (smaller) {
00233             equivalent = old_version.sub_bug(0, old_version.length()
00234                 - difference - 1);
00235           } else {
00236             bug_finder<contents> blank(difference, old_version.flags());
00237             if (this->flags() & byte_array::SIMPLE_COPY)
00238               memset(blank.access(), 'Q', blank.length());
00239             else
00240               for (int i = 0; i < blank.length(); i++)
00241                 blank.use(i) = contents('Q');
00242             equivalent = old_version + blank;
00243           }
00244         }
00245         if (equivalent.length() != testers[index]->length())
00246           deadly_error(class_name(), __WHERE__, "the resized form had "
00247               "erroneous length");
00248         if (this->flags() & byte_array::SIMPLE_COPY) {
00249           if (memcmp(testers[index]->access(), equivalent.observe(),
00250               equivalent.length())) 
00251             deadly_error(class_name(), __WHERE__, "the resized form had "
00252                 "erroneous contents");
00253         } else {
00254           for (int i = 0; i < equivalent.length(); i++)
00255             if (testers[index]->use(i) != equivalent[i])
00256               deadly_error(class_name(), __WHERE__, "the resized form had "
00257                   "erroneous contents");
00258         }
00259         break;
00260       }
00261       case do_assign: {
00262         int to_assign = root_object().randomizer().inclusive(0, MAX_SIMULTANEOUS_OBJECTS - 1);
00263         *testers[index] = *testers[to_assign];
00264         break;
00265       }
00266       case do_access: {
00267         int start = root_object().randomizer().inclusive(0, testers[index]->length());
00268         int end = root_object().randomizer().inclusive(0, testers[index]->length());
00269         flip_increasing(start, end);
00270         for (int i = start; i < end; i++)
00271           testers[index]->access()[i] = contents(root_object().randomizer().inclusive(1, 255));
00272         break;
00273       }
00274       case do_observe: {
00275         int start = root_object().randomizer().inclusive(0, testers[index]->length());
00276         int end = root_object().randomizer().inclusive(0, testers[index]->length());
00277         flip_increasing(start, end);
00278         int total = 0;
00279         for (int i = start; i < end; i++)
00280           total += testers[index]->observe()[i];
00281         if (total < 20) start = total;  // compiler muffler.  
00282         break;
00283       }
00284       case do_resizer: {
00285         // tests whether the array will reuse space when it should be able to.
00286         bug_finder<contents> &arrh = *testers[index];
00287         // fill with known data.
00288         int i;
00289         for (i = 0; i < arrh.length(); i++)
00290           arrh[i] = contents((i + 23) % 256);
00291         // record the old starting point.
00292         const contents *start = (contents *)arrh.internal_block_start();
00293         int zap_amount = root_object().randomizer().inclusive(1, arrh.length() - 1);
00294         // now take out a chunk from the array at the beginning.
00295         arrh.zap(0, zap_amount - 1);
00296         // test the contents.
00297         for (i = 0; i < arrh.length(); i++)
00298           if (arrh[i] != contents((i + 23 + zap_amount) % 256))
00299             deadly_error(class_name(), __WHERE__, "the resized form "
00300                 "had different contents");
00301         // now add back in the space we ate.
00302         arrh.resize(arrh.length() + zap_amount, common::NEW_AT_END);
00303         // check the pointer; it should not have changed.
00304         if (start != (contents *)arrh.internal_block_start())
00305           deadly_error(class_name(), __WHERE__, "the resized form had "
00306               "a different start address");
00307         // test the contents again.  they should start at zero and have the
00308         // same zap_amount offset.  the stuff past the original point shouldn't
00309         // be tested since we haven't set it.
00310         for (i = 0; i < arrh.length() - zap_amount; i++)
00311           if (arrh[i] != contents((i + 23 + zap_amount) % 256))
00312             deadly_error(class_name(), __WHERE__, "the resized form "
00313                 "had different contents");
00314         // now a test of all values through the zap_amount.
00315         arrh.zap(0, zap_amount - 1);
00316         for (i = 0; i < zap_amount; i++) {
00317           arrh.resize(arrh.length() + 1, common::NEW_AT_END);
00318           if (start != (contents *)arrh.internal_block_start())
00319             deadly_error(class_name(), __WHERE__, "the slowly resized "
00320                 "form had a different start address");
00321         }
00322         // test the contents once more.  they should start at zero and have
00323         // double the zap_amount offset.  the stuff past the original point
00324         // shouldn't be tested since we haven't set it.
00325         for (i = 0; i < arrh.length() - 2 * zap_amount; i++)
00326           if (arrh[i] != contents((i + 23 + 2 * zap_amount) % 256))
00327             deadly_error(class_name(), __WHERE__, "the slowly resized "
00328                 "form had different contents");
00329 
00330         // the tests below should be nearly identical to the ones above, but
00331         // they use the NEW_AT_BEGINNING style of copying.
00332 
00333         // fill with known data.
00334         for (i = 0; i < arrh.length(); i++)
00335           arrh[i] = contents((i + 23) % 256);
00336         // record the old starting point.
00337         start = (contents *)arrh.internal_block_start();
00338         zap_amount = root_object().randomizer().inclusive(1, arrh.length() - 1);
00339         // now take out a chunk from the array at the beginning.
00340         arrh.zap(0, zap_amount - 1);
00341         // test the contents.
00342         for (i = 0; i < arrh.length(); i++)
00343           if (arrh[i] != contents((i + 23 + zap_amount) % 256))
00344             deadly_error(class_name(), __WHERE__, "the resized form "
00345                 "had different contents");
00346         // now add back in the space we ate.
00347         arrh.resize(arrh.length() + zap_amount, common::NEW_AT_BEGINNING);
00348         // check the pointer; it should not have changed.
00349         if (start != (contents *)arrh.internal_block_start())
00350           deadly_error(class_name(), __WHERE__, "the resized form had "
00351               "a different start address");
00352         // test the contents again.  they should start at zap_amount but have
00353         // the same zap_amount offset.  the stuff before the original point
00354         // shouldn't be tested since we haven't set it.
00355         for (i = zap_amount; i < arrh.length(); i++)
00356           if (arrh[i] != contents((i + 23) % 256))
00357             deadly_error(class_name(), __WHERE__, "the resized form "
00358                 "had different contents");
00359         // now a test of all values through the zap_amount.
00360         arrh.zap(0, zap_amount - 1);
00361         for (i = 0; i < zap_amount; i++) {
00362           arrh.resize(arrh.length() + 1, common::NEW_AT_BEGINNING);
00363           if (start != (contents *)arrh.internal_block_start())
00364             deadly_error(class_name(), __WHERE__, "the slowly resized "
00365                 "form had a different start address");
00366         }
00367         // test the contents once more.  the zap_amount offset should stay the
00368         // same since we clobbered the place we added originally, then added
00369         // more space in.  so we skip the first part still.
00370         for (i = zap_amount; i < arrh.length(); i++)
00371           if (arrh[i] != contents((i + 23) % 256))
00372             deadly_error(class_name(), __WHERE__, "the slowly resized "
00373                 "form had different contents");
00374       }
00375       case do_zap: {
00376         int start;
00377         int end;
00378         bool erroneous = false;
00379         int chose = root_object().randomizer().inclusive(1, 100);
00380         if (chose <= 90) {
00381           // there's a ninety percent chance we pick a range that's valid.
00382           start = root_object().randomizer().inclusive(0, testers[index]->length() - 1);
00383           end = root_object().randomizer().inclusive(0, testers[index]->length() - 1);
00384         } else if (chose <= 95) {
00385           // and a 5 percent chance we pick to choose the zero index as our
00386           // start; this gets at the code for fast zapping.
00387           start = 0;
00388           end = root_object().randomizer().inclusive(0, testers[index]->length() - 1);
00389         } else {
00390           // and a 5 percent chance we'll allow picking a bad index.  the
00391           // actual chance of picking a bad one is less.
00392           erroneous = true;
00393           start = root_object().randomizer().inclusive(-2, testers[index]->length() + 3);
00394           end = root_object().randomizer().inclusive(-2, testers[index]->length() + 3);
00395         }
00396         flip_increasing(start, end);
00397         bug_finder<contents> old_version = *testers[index];
00398         testers[index]->zap(start, end);
00399         if (!erroneous) {
00400           bug_finder<contents> old_head = old_version.sub_bug(0, start - 1);
00401           bug_finder<contents> old_tail = old_version.sub_bug(end + 1,
00402               old_version.length() - 1);
00403           bug_finder<contents> equivalent = old_head + old_tail;
00404           if (equivalent.length() != testers[index]->length())
00405             deadly_error(class_name(), __WHERE__, "the zapped form had "
00406                 "erroneous length");
00407           if (this->flags() & byte_array::SIMPLE_COPY) {
00408             if (memcmp(testers[index]->access(), equivalent.observe(),
00409                 equivalent.length())) 
00410               deadly_error(class_name(), __WHERE__, "the zapped form had "
00411                   "erroneous contents");
00412           } else {
00413             for (int i = 0; i < equivalent.length(); i++)
00414               if (testers[index]->get(i) != equivalent.get(i))
00415                 deadly_error(class_name(), __WHERE__, "the zapped form had "
00416                     "erroneous contents");
00417           }
00418         }
00419         break;
00420       }
00421       case do_swap: {
00422         int first = root_object().randomizer().inclusive(0, MAX_SIMULTANEOUS_OBJECTS - 1);
00423         int second = first;
00424         while (first == second) 
00425           second = root_object().randomizer().inclusive(0, MAX_SIMULTANEOUS_OBJECTS - 1);
00426         bug_finder<contents> old_first = *testers[first];
00427         bug_finder<contents> old_second = *testers[second];
00428         testers[first]->swap_contents(*testers[second]);
00429         if (*testers[first] != old_second)
00430           deadly_error(class_name(), __WHERE__, "the old second entry was different "
00431               "from the new first");
00432         if (*testers[second] != old_first)
00433           deadly_error(class_name(), __WHERE__, "the old first entry was different "
00434               "from the new second");
00435         break;
00436       }
00437       case do_shrink: {
00438         int shrinker = root_object().randomizer().inclusive(0, MAX_SIMULTANEOUS_OBJECTS - 1);
00439         bug_finder<contents> old = *testers[shrinker];
00440         testers[shrinker]->shrink();
00441         if (old != *testers[shrinker])
00442           deadly_error(class_name(), __WHERE__, "contents were wrong after shrink");
00443         if (testers[shrinker]->internal_real_length() > old.length() + 1)
00444           deadly_error(class_name(), __WHERE__, "size was too large after shrink");
00445         break;
00446       }
00447       case do_shift: {
00448         int shifty = root_object().randomizer().inclusive(0, MAX_SIMULTANEOUS_OBJECTS - 1);
00449         bug_finder<contents> old = *testers[shifty];
00450         testers[shifty]->shift_data(bug_finder<contents>::TO_LEFT);
00451         if (testers[shifty]->internal_offset() != 0)
00452           deadly_error(class_name(), __WHERE__, "offset is wrong after shift to left");
00453         if (*testers[shifty] != old)
00454           deadly_error(class_name(), __WHERE__, "contents wrong after shift to left");
00455         testers[shifty]->shift_data(bug_finder<contents>::TO_RIGHT);
00456         if (testers[shifty]->internal_offset()
00457             != testers[shifty]->internal_real_length()
00458                  - testers[shifty]->length())
00459           deadly_error(class_name(), __WHERE__, "offset is wrong after shift to right");
00460         if (*testers[shifty] != old)
00461           deadly_error(class_name(), __WHERE__, "contents wrong after shift to right");
00462         break;
00463       }
00464       default: {
00465         deadly_error(class_name(), "action choice", "invalid choice!");
00466         break;
00467       }
00468     }
00469   }
00470 
00471   // clean up.
00472   delete [] junk_space;
00473   for (int d = 0; d < MAX_SIMULTANEOUS_OBJECTS; d++) delete testers[d];
00474 }
00475 
00477 
00478 int test_object_allocator::execute() {
00479   LOG("doing test on non-simple object.\n");
00480   bug_finder<floopo> complex_tester(0, bug_finder<floopo>::EXPONE
00481       | bug_finder<floopo>::FLUSH_INVISIBLE);
00482   complex_tester.test_allocation();
00483   LOG("finished test on non-simple object.\n");
00484 
00485   LOG("doing test on simple object.\n");
00486   bug_finder<char> simple_tester(0, bug_finder<floopo>::SIMPLE_COPY
00487       | bug_finder<floopo>::EXPONE);
00488   simple_tester.test_allocation();
00489   LOG("finished test on simple object.\n");
00490 
00491   guards::alert_message("object_allocator:: works for those "
00492       "functions tested.");
00493   return 0;
00494 }
00495 
00496 

Generated on Fri Nov 21 04:30:04 2008 for HOOPLE Libraries by  doxygen 1.5.1