array.cpp

Go to the documentation of this file.
00001 #ifndef ARRAY_IMPLEMENTATION
00002 #define ARRAY_IMPLEMENTATION
00003 
00004 /*****************************************************************************\
00005 *                                                                             *
00006 *  Name   : array                                                             *
00007 *  Author : Chris Koeritz                                                     *
00008 *                                                                             *
00009 *******************************************************************************
00010 * Copyright (c) 1989-$now By Author.  This program is free software; you can  *
00011 * redistribute it and/or modify it under the terms of the GNU General Public  *
00012 * License as published by the Free Software Foundation; either version 2 of   *
00013 * the License or (at your option) any later version.  This is online at:      *
00014 *     http://www.fsf.org/copyleft/gpl.html                                    *
00015 * Please send any updates to: fred@gruntose.com                               *
00016 \*****************************************************************************/
00017 
00018 // GOALS:
00019 //
00020 // 1) provide a slightly smarter allocation method for C arrays and other
00021 //    contiguous-storage container classes with better speed and reduced memory
00022 //    fragmentation through pre-allocation.  this can reduce memory thrashing
00023 //    when doing appends and inserts that can be granted with previously
00024 //    allocated, but unused, space.
00025 // 2) clean-up bounds failure cases in functions that return a reference by
00026 //    always having at least one bogus element in the array for returns.  this
00027 //    really just requires that we never allow our hidden real length of the
00028 //    array to be zero.
00029 
00030 //hmmm: with bogonic in use here, there is no error handling that
00031 //      requires a blocking factor of 1, is there?  we can go to a zero
00032 //      blocking factor.  or alternatively, we could toss the bogonic and
00033 //      go back to a blocking of 1 by default.
00034 
00035 #include "array.h"
00036 #include "function.h"
00037 #include "guards.h"
00038 
00039 #include <stdio.h>
00040 #include <string.h>
00041 
00042 #undef static_class_name
00043 #define static_class_name() "array"
00044   // used in debugging macros.
00045 
00046 //#define DEBUG_ARRAY
00047   // uncomment for the debugging version.  this is much less efficient because
00048   // it performs random spot checks on the allocator's sanity.
00049 
00050 template <class contents>
00051 array<contents>::array(int num, const contents *init, u_short flags)
00052 : _active_length(0), _real_length(0), _mem_block(NIL), _offset(NIL), _flags(flags)
00053 {
00054   if (_flags > 7) {
00055 #ifdef DEBUG_ARRAY
00056     const char *error_msg = "array constructor: error in parameters!  "
00057         "still passing a block size?  flags is set to %d.";
00058     printf(error_msg, flags);
00059     fprintf(stderr, error_msg, flags);
00060   #ifdef ERRORS_ARE_FATAL
00061       fflush(0);
00062       CAUSE_BREAKPOINT;
00063       throw "deadly_error";
00064   #endif // errors are fatal.
00065 #endif // array debugging is turned on.
00066 
00067     _flags = EXPONE | FLUSH_INVISIBLE;
00068       // drop simple copy, since the caller doesn't know what they're doing.
00069   }
00070 
00071   allocator_reset(num, 1);  // get some space.
00072   retrain(num, init);  // plug in their contents.
00073 }
00074 
00075 template <class contents>
00076 array<contents>::array(const array<contents> &cf)
00077 : _active_length(0), _real_length(0), _mem_block(NIL), _offset(NIL), _flags(cf._flags)
00078 {
00079   allocator_reset(cf._active_length, 1);  // get some space.
00080   operator = (cf);  // assignment operator does the rest.
00081 }
00082 
00083 template <class contents>
00084 array<contents>::~array()
00085 {
00086   _offset = NIL;
00087   if (_mem_block) delete [] _mem_block;
00088   _mem_block = NIL;
00089   _active_length = 0;
00090   _real_length = 0;
00091 }
00092 
00093 template <class contents>
00094 void array<contents>::reset(int num, const contents *init)
00095 { retrain(num, init); }
00096 
00097 template <class contents>
00098 array<contents> &array<contents>::operator =(const array &cf)
00099 {
00100   if (this == &cf) return *this;
00101   _flags = cf._flags;  // copy the flags coming in from the other object.
00102   // prepare the array for retraining...
00103   _offset = _mem_block;  // slide the offset back to the start.
00104   _active_length = 0;  // the length gets reset also.
00105   retrain(cf._active_length, cf.observe());
00106   return *this;
00107 }
00108 
00109 template <class contents>
00110 contents &array<contents>::use(int index)
00111 {
00112   FUNCDEF("use");
00113   bounds_halt(index, 0, this->last(), bogonic<contents>());
00114   return this->access()[index];
00115 }
00116 
00117 template <class contents>
00118 const contents &array<contents>::get(int index) const
00119 {
00120   FUNCDEF("get");
00121   bounds_halt(index, 0, this->last(), bogonic<contents>());
00122   return this->observe()[index];
00123 }
00124 
00125 template <class contents>
00126 array<contents> &array<contents>::concatenate(const array &s1)
00127 {
00128   // check whether there's anything to concatenate.
00129   if (!s1.length()) return *this;
00130   if (this == &s1) {
00131     // make sure they don't concatenate this array to itself.
00132     return concatenate(array<contents>(*this)); 
00133   }
00134   int old_len = this->length();
00135   resize(this->length() + s1.length(), common::NEW_AT_END);
00136   overwrite(old_len, s1);
00137   return *this;
00138 }
00139 
00140 template <class contents>
00141 array<contents> &array<contents>::concatenate(const contents &to_concatenate)
00142 {
00143   resize(this->length() + 1, common::NEW_AT_END);
00144   if (!this->simple())
00145     this->access()[this->last()] = to_concatenate;
00146   else
00147     memcpy(&(this->access()[this->last()]), &to_concatenate, sizeof(contents));
00148   return *this;
00149 }
00150 
00151 template <class contents>
00152 array<contents> &array<contents>::concatenate(const contents *to_concatenate,
00153     int length)
00154 {
00155   if (!length) return *this;  // nothing to do.
00156   const int old_len = this->length();
00157   resize(this->length() + length, common::NEW_AT_END);
00158   if (!this->simple())
00159     for (int i = 0; i < length; i++)
00160       this->access()[old_len + i] = to_concatenate[i];
00161   else
00162     memcpy(&(this->access()[old_len]), to_concatenate,
00163         length * sizeof(contents));
00164   return *this;
00165 }
00166 
00167 template <class contents>
00168 array<contents> array<contents>::concatenation(const array &s1) const
00169 {
00170   // tailor the return array to the new size needed.
00171   array<contents> to_return(this->length() + s1.length(), NIL, s1._flags);
00172   to_return.overwrite(0, *this);  // put the first part into the new array.
00173   to_return.overwrite(this->length(), s1);  // add the second segment.
00174   return to_return;
00175 }
00176 
00177 template <class contents>
00178 array<contents> array<contents>::concatenation(const contents &s1) const
00179 {
00180   array<contents> to_return(this->length() + 1, NIL, _flags);
00181   to_return.overwrite(0, *this);
00182   if (!this->simple())
00183     to_return.access()[to_return.last()] = s1;
00184   else
00185     memcpy(&(to_return.access()[to_return.last()]), &s1, sizeof(contents));
00186   return to_return;
00187 }
00188 
00189 template <class contents>
00190 array<contents> array<contents>::subarray(int start, int end) const
00191 {
00192   bounds_return(start, 0, this->last(), array<contents>(0, NIL, _flags));
00193   bounds_return(end, 0, this->last(), array<contents>(0, NIL, _flags));
00194   if (start > end) return array<contents>(0, NIL, _flags);
00195   return array<contents>(end - start + 1, &(this->observe()[start]), _flags);
00196 }
00197 
00198 template <class contents>
00199 void array<contents>::swap_contents(array &other)
00200 {
00201   if (this == &other) return;  // already swapped then, i suppose.
00202   swap_values(this->_active_length, other._active_length);
00203   swap_values(this->_real_length, other._real_length);
00204   swap_values(this->_offset, other._offset);
00205   swap_values(this->_mem_block, other._mem_block);
00206   swap_values(this->_flags, other._flags);
00207 }
00208 
00209 template <class contents>
00210 outcome array<contents>::shrink()
00211 {
00212   if (!_mem_block) return common::OUT_OF_MEMORY;
00213   if (_active_length == _real_length) return common::OKAY;  // already just right.
00214   array new_holder(*this);
00215     // create a copy of this object that is just the size needed.
00216   swap_contents(new_holder);
00217     // swap this object with the copy, leaving the enlarged version behind
00218     // for destruction.
00219   return common::OKAY;
00220 }
00221 
00222 template <class contents>
00223 outcome array<contents>::stuff(int lengthx, contents *to_stuff) const
00224 {
00225   if (!lengthx || !this->length()) return common::OKAY;
00226   int copy_len = minimum(lengthx, this->length());
00227   if (!this->simple()) {
00228     for (int i = 0; i < copy_len; i++)
00229       to_stuff[i] = this->observe()[i];
00230   } else {
00231     memcpy(to_stuff, this->observe(), copy_len * sizeof(contents));
00232   }
00233   return common::OKAY;
00234 }
00235 
00236 template <class contents>
00237 outcome array<contents>::overwrite(int position,
00238     const array<contents> &write_with, int count)
00239 {
00240   FUNCDEF("overwrite");
00241   if (!count) return common::OKAY;
00242   if ( (this == &write_with) || !this->length() || !write_with.length())
00243     return common::BAD_INPUT;
00244   bounds_halt(position, 0, this->last(), common::OUT_OF_RANGE);
00245   if ( negative(count) || (count > write_with.length()) )
00246     count = write_with.length();
00247   if (position > this->length() - count)
00248     count = this->length() - position;
00249   if (!this->simple()) {
00250     for (int i = position; i < position + count; i++)
00251       this->access()[i] = write_with.observe()[i - position];
00252   } else {
00253     memcpy(&(this->access()[position]), write_with.observe(), 
00254         count * sizeof(contents));
00255   }
00256   return common::OKAY;
00257 }
00258 
00259 template <class contents>
00260 outcome array<contents>::allocator_reset(int initial, int blocking)
00261 {
00262   FUNCDEF("reset");
00263   if (blocking < 1) {
00264 #ifdef DEBUG_ARRAY
00265     continuable_error(static_class_name(), func, "bad block size");
00266 #endif
00267     blocking = 1;
00268   }
00269   if (initial < 0) initial = 0;  // no antimatter arrays.
00270   if (_mem_block) {
00271     // remove old contents.
00272     delete [] _mem_block;
00273     _mem_block = NIL;
00274     _offset = NIL;
00275   }
00276   _active_length = initial;  // reset the length to the reporting size.
00277   _real_length = initial + blocking;  // compute the real length.
00278   if (_real_length) {
00279     try {
00280       _mem_block = new contents[_real_length];
00281       if (!_mem_block) {
00282         out_of_memory_now(static_class_name(), func);
00283         return common::OUT_OF_MEMORY;
00284       }
00285     } catch (...) {
00286       _mem_block = NIL;
00287       out_of_memory_now(static_class_name(), func);
00288       // shouldn't ever reach this spot.
00289       return common::OUT_OF_MEMORY;
00290     }
00291     _offset = _mem_block;  // reset offset to start of array.
00292   }
00293   return common::OKAY;
00294 }
00295 
00296 template <class contents>
00297 void array<contents>::shift_data(shift_directions where)
00298 {
00299   if (where == TO_LEFT) {
00300     // we want to end up with the data jammed up against the left edge.  thus
00301     // we need the offset to be zero bytes from start.
00302     if (_offset == _mem_block)
00303       return;  // offset already at start, we're done.
00304     // well, we need to move the data.
00305     if (simple()) {
00306       memmove(_mem_block, _offset, _active_length * sizeof(contents));
00307     } else {
00308       for (contents *ptr = _offset; ptr < _offset + _active_length; ptr++)
00309         _mem_block[ptr - _offset] = *ptr;
00310     }
00311     _offset = _mem_block;  // we've ensured that this is correct.
00312     if (_flags & FLUSH_INVISIBLE) {
00313       // we need to clean up what might have had contents previously.
00314 //      for (contents *p = _mem_block + _active_length; p < _mem_block + _real_length; p++)
00315 //        *p = contents();
00316     }
00317   } else {
00318     // we want to move the data to the right, so the offset should be the
00319     // difference between the real length and the length.
00320     if (_offset == _mem_block + _real_length - _active_length)
00321       return;  // the offset is already the right size.
00322     if (simple()) {
00323       memmove(&_mem_block[_real_length - _active_length], _offset, _active_length * sizeof(contents));
00324     } else {
00325       for (int i = _real_length - 1; i >= _real_length - _active_length; i--)
00326         _mem_block[i] = _offset[i - _real_length + _active_length];
00327     }
00328     _offset = _mem_block + _real_length - _active_length;  // we've now ensured this.
00329     if (_flags & FLUSH_INVISIBLE) {
00330       // we need to clean up the space on the left where old contents might be.
00331 //      for (contents *p = _mem_block; p < _offset; p++)
00332 //        *p = contents();
00333     }
00334   }
00335 }
00336 
00337 template <class contents>
00338 outcome array<contents>::resize(int new_size, common::how_to_copy way)
00339 {
00340   FUNCDEF("resize");
00341   if (new_size < 0) new_size = 0;  // we stifle this.
00342   if (new_size == _active_length) {
00343     // nothing much to do.
00344     return common::OKAY;
00345   }
00346   // okay, now we at least know that the sizes are different.  we save all the
00347   // old information about the array prior to this resizing.
00348   contents *old_s = _mem_block;  // save the old contents...
00349   const int old_len = _active_length;  // and length.
00350   contents *old_off = _offset;  // and offset.
00351   bool delete_old = false;  // if true, old memory is whacked once it's copied.
00352 
00353 //hmmm: wasn't there a nice realization that we could bail out early in
00354 //      the case where the size is already suffcient?  there seems to be
00355 //      an extraneous copy case here.
00356 //      also it would be nice to have better, more descriptive names for the
00357 //      variables here since they are not lending themselves to understanding
00358 //      the algorithm.
00359 
00360   // we check whether there's easily enough space in the array already.
00361   // if not, then we have some more decisions to make.
00362   if (_real_length - (old_off - old_s) < new_size) {
00363     // well, there's not enough space with the current space and offset.
00364     if (_real_length < new_size) {
00365       // there's really not enough space overall, no fooling.  we now will
00366       // create a new block.
00367       _mem_block = NIL;  // zero out the pointer so reset doesn't delete it.
00368       delete_old = true;
00369       int blocking = 1;
00370       if (exponential()) blocking = new_size + 1;
00371       outcome ret = allocator_reset(new_size, blocking);
00372       if (ret != common::OKAY) {
00373         // failure, but don't forget to whack the old glob.
00374 #ifdef DEBUG_ARRAY
00375         deadly_error(static_class_name(), "resize", "reset failure");
00376 #endif
00377         delete [] old_s;
00378         return ret;
00379       }
00380       // fall out to the copying phase, now that we have some fresh memory.
00381     } else {
00382       // there is enough space if we shift some things around.
00383       const int size_difference = new_size - _active_length;
00384         // we compute how much space has to be found in the array somewhere
00385         // to support the new larger size.
00386       if (way == common::DONT_COPY) {
00387         // simplest case; just reset the offset appropriately so the new_size
00388         // will fit.
00389         _offset = _mem_block;
00390         _active_length = new_size;
00391       } else if (way == common::NEW_AT_BEGINNING) {
00392         // if the new space is at the beginning, there are two cases.  either
00393         // the new size can be accomodated by the current position of the
00394         // data or the data must be shifted to the right.
00395         if (_offset - _mem_block < size_difference) {
00396           // we need to shift the data over to the right since the offset isn't
00397           // big enough for the size increase.
00398           shift_data(TO_RIGHT);  // resets the offset appropriately.
00399         }
00400         // now we know that the amount of space prior to the real data
00401         // is sufficient to hold what new space is needed.  we just need to
00402         // shift the offset back somewhat.
00403         _offset -= size_difference;
00404         _active_length = new_size;
00405       } else {
00406         // better only be three ways to do this; we're now assuming the new
00407         // space should be at the end (NEW_AT_END).
00408         // now that we're here, we know there will be enough space if we shift
00409         // the block to the left, but we DO NEED to do this.  if we didn't need
00410         // to shift the data, then we would find that:
00411         //     _real_length - old_off >= new_size
00412         // which is disallowed by the guardian conditional around this area.
00413         shift_data(TO_LEFT);  // resets the offset for us.
00414         _active_length = new_size;
00415       }
00416       // we have ensured that we had enough space and we have already shifted
00417       // the data around appropriately.  we no longer need to enter the next
00418       // block where we would copy data around if we had to.  it has become
00419       // primarily for cases where either we have to copy data because we
00420       // have new storage to fill or where we are shrinking the array.
00421       return common::OKAY;
00422     }
00423   }
00424 
00425   // the blob of code below is offset invariant.  by the time we reach here,
00426   // the array should be big enough and the offset should be okay.
00427   _active_length = new_size;  // set length to the new reporting size.
00428   if (way != common::DONT_COPY) {
00429     int where = 0;  // offset for storing into new array.
00430     bool do_copy = false;  // if true, then we need to move the data around.
00431     contents *loop_offset_old = old_off;  // offset into original object.
00432     // we should only have to copy the memory in one other case besides our
00433     // inhabiting new memory--when we are asked to resize with the new stuff
00434     // at the beginning of the array.  if the new space is at the end, we're
00435     // already looking proper, but if the new stuff is at the beginning, we
00436     // need to shift existing stuff downwards.
00437     if (way == common::NEW_AT_BEGINNING) {
00438       where = new_size - old_len;  // move up existing junk.
00439       if (where) do_copy = true;  // do copy, since it's not immobile.
00440       if (where < 0) {
00441         // array shrank; we need to do the loop differently for starting
00442         // from the beginning.  we skip to the point in the array that our
00443         // suffix needs to start at.
00444         loop_offset_old -= where;
00445         where = 0;  // reset where so we don't have negative offsets.
00446       }
00447     }
00448     const int size_now = minimum(old_len, _active_length);
00449     if (delete_old || do_copy) {
00450       contents *offset_in_new = _offset + where;
00451       contents *posn_in_old = loop_offset_old;
00452       if (simple()) {
00453         // memmove should take care of intersections.
00454         memmove(offset_in_new, posn_in_old, size_now * sizeof(contents));
00455       } else {
00456         // we need to do the copies using the object's assignment operator.
00457         if (new_size >= old_len) {
00458           for (int i = size_now - 1; i >= 0; i--)
00459             offset_in_new[i] = posn_in_old[i];
00460         } else {
00461           for (int i = 0; i < size_now; i++)
00462             offset_in_new[i] = posn_in_old[i];
00463         }
00464       }
00465 
00466       // we only want to flush the obscured elements when we aren't already
00467       // inhabiting new space.
00468       if ( (_flags & FLUSH_INVISIBLE) && !delete_old) {
00469         // clear out the space that just went out of scope.  we only do this
00470         // for the flushing mode and when we aren't starting from a fresh
00471         // pointer (i.e., delete_old isn't true).
00472         if (new_size < old_len) {
00473 //          for (contents *p = posn_in_old; p < offset_in_new; p++)
00474 //            *p = contents();
00475         }
00476       }
00477     }
00478   }
00479   if (delete_old) delete [] old_s;
00480   return common::OKAY;
00481 }
00482 
00483 template <class contents>
00484 outcome array<contents>::retrain(int new_len, const contents *to_set)
00485 {
00486   FUNCDEF("retrain");
00487   if (new_len < 0) new_len = 0;  // stifle that bad length.
00488 #ifdef DEBUG_ARRAY
00489   if ( (_mem_block >= to_set) && (_mem_block < to_set + new_len) )
00490     deadly_error(static_class_name(), func, "ranges overlap in retrain!");
00491 #endif
00492   outcome ret = resize(new_len, common::DONT_COPY);
00493   if (ret != common::OKAY) return ret;
00494 #ifdef DEBUG_ARRAY
00495   if (new_len != _active_length)
00496     continuable_error(static_class_name(), func, "resize set the wrong length");
00497 #endif
00498   if (to_set) {
00499     if (simple()) 
00500       memcpy(_offset, to_set, _active_length * sizeof(contents));
00501     else
00502       for (int i = 0; i < _active_length; i++)
00503         _offset[i] = to_set[i];
00504   } else {
00505     if (_flags & FLUSH_INVISIBLE) {
00506       // no contents provided, so stuff the space with blanks.
00507 //      for (int i = 0; i < _active_length; i++) _offset[i] = contents();
00508     }
00509   }
00510   if (_flags & FLUSH_INVISIBLE) {
00511 //    for (contents *ptr = _mem_block; ptr < _offset; ptr++)
00512 //      *ptr = contents();
00513 //    for (contents *ptr = _offset + _active_length; ptr < _mem_block + _real_length; ptr++)
00514 //      *ptr = contents();
00515   }
00516   return common::OKAY;
00517 }
00518 
00519 template <class contents>
00520 outcome array<contents>::zap(int position1, int position2)
00521 {
00522   if (position1 > position2) return common::OKAY;
00523   bounds_return(position1, 0, _active_length - 1, common::OUT_OF_RANGE);
00524   bounds_return(position2, 0, _active_length - 1, common::OUT_OF_RANGE);
00525   if (!position1) {
00526     // if they're whacking from the beginning, we just reset the offset.
00527     _offset += position2 + 1;
00528     _active_length -= position2 + 1;
00529     return common::OKAY;
00530   }
00531   const int difference = position2 - position1 + 1;
00532   // copy from just above position2 down into position1.
00533   if (simple()) {
00534     if (_active_length - difference - position1 > 0)
00535       memmove(&_offset[position1], &_offset[position1 + difference],
00536           (_active_length - difference - position1) * sizeof(contents));
00537   } else {
00538     for (int i = position1; i < _active_length - difference; i++)
00539       _offset[i] = _offset[i + difference];
00540   }
00541 
00542   outcome ret = resize(_active_length - difference, common::NEW_AT_END);
00543     // chop down to new size.
00544 #ifdef DEBUG_ARRAY
00545   if (ret != common::OKAY) {
00546     deadly_error(static_class_name(), "zap", "resize failure");
00547     return ret;
00548   }
00549 #endif
00550   return ret;
00551 }
00552 
00553 template <class contents>
00554 outcome array<contents>::insert(int position, int elem_to_add)
00555 {
00556   if (position < 0) return common::OUT_OF_RANGE;
00557   if (position > this->length())
00558     position = this->length();
00559   if (elem_to_add < 0) return common::OUT_OF_RANGE;
00560   common::how_to_copy how = common::NEW_AT_END;
00561   if (position == 0) how = common::NEW_AT_BEGINNING;
00562   resize(this->length() + elem_to_add, how);
00563 
00564   // if the insert wasn't at the front, we have to copy stuff into the new
00565   // locations.
00566   if (how == common::NEW_AT_END) {
00567     const contents simple_default_object = contents();
00568     if (!this->simple()) {
00569       for (int i = this->last(); i >= position + elem_to_add; i--)
00570         this->access()[i] = this->observe()[i - elem_to_add];
00571       for (int j = position; j < position + elem_to_add; j++)
00572         this->access()[j] = simple_default_object;
00573     } else {
00574       memmove(&(this->access()[position + elem_to_add]),
00575           &(this->observe()[position]), (this->length() - position
00576           - elem_to_add) * sizeof(contents));
00577       for (int j = position; j < position + elem_to_add; j++)
00578         memcpy(&this->access()[j], &simple_default_object, sizeof(contents));
00579     }
00580   }
00581   return common::OKAY;
00582 }
00583 
00584 template <class contents>
00585 outcome array<contents>::put(int index, const contents &to_put)
00586 {
00587   FUNCDEF("put");
00588   bounds_halt(index, 0, this->last(), common::OUT_OF_RANGE);
00589   if (!this->simple())
00590     this->access()[index] = to_put;
00591   else
00592     memcpy(&(this->access()[index]), &to_put, sizeof(contents));
00593   return common::OKAY;
00594 }
00595 
00596 template <class contents>
00597 void array<contents>::snarf(array<contents> &new_contents)
00598 {
00599   if (this == &new_contents) return;  // no blasting own feet off.
00600   reset();  // trash our current storage.
00601   swap_contents(new_contents);
00602 }
00603 
00604 #undef static_class_name
00605 
00606 #endif
00607 

Generated on Sat Aug 30 04:31:40 2008 for HOOPLE Libraries by  doxygen 1.5.1