Array.imp.h

Go to the documentation of this file.
00001 //*****************************************************************************/
00002 // Copyright (c) 1998-2006 Autodesk, Inc.
00003 // All rights reserved.
00004 // 
00005 // These coded instructions, statements, and computer programs contain
00006 // unpublished proprietary information written by Autodesk, Inc., and are
00007 // protected by Federal copyright law. They may not be disclosed to third
00008 // parties or copied or duplicated in any form, in whole or in part, without
00009 // the prior written consent of Autodesk, Inc.
00010 //*****************************************************************************/
00011 /*==============================================================================
00012 
00013   file:     Array.imp.h
00014   author:   Daniel Levesque
00015   created:  27 March 2006
00016   description:
00017     Array container.
00018 
00019 ==============================================================================*/
00020 
00021 template <class T> Array<T>::Array()
00022 : mpArray(NULL),
00023   mReservedLen(0),
00024   mUsedLen(0),
00025   mGrowLen(kDefaultGrowthLength)
00026 {
00027     if(mGrowLen < 1) {
00028         // Growth length needs to be at least 1.
00029         mGrowLen = 1;
00030     }
00031 }
00032 
00033 template <class T> Array<T>::Array(size_t usedLength, const T& defaultVal, size_t growLength)
00034 : mpArray(NULL),
00035   mReservedLen(0),
00036   mUsedLen(0),
00037   mGrowLen(growLength)
00038 {
00039     if(mGrowLen < 1) {
00040         // Growth length needs to be at least 1.
00041         mGrowLen = 1;
00042     }
00043 
00044     // Re-size the array if a non-zero length was specified.
00045     if(usedLength > 0) {
00046         mpArray = ArrayAllocate(usedLength);
00047         if (mpArray == NULL) {
00048             handleOutOfMemory();
00049         }
00050         else {
00051 
00052             // Initialize the new elements
00053             ArrayConstruct(mpArray, usedLength, defaultVal);
00054 
00055             mReservedLen = usedLength;
00056             mUsedLen = usedLength;
00057         }
00058     }
00059 }
00060 
00061 // This is the usual copy constructor with the caveat that,
00062 // if the system can not allocate enough memory to satisfy the
00063 // request then it is assumed that the entire system is in a
00064 // dangerously low memory situation, and there is no alternative
00065 // but to have the system gracefully abort (i.e., prompting the
00066 // users to save files, and/or free up more memory, or what-have-you).
00067 //
00068 template <class T> Array<T>::Array(const Array<T>& src)
00069 : mpArray(NULL),
00070   mReservedLen(src.mReservedLen),
00071   mUsedLen(src.mUsedLen),
00072   mGrowLen(src.mGrowLen)
00073 {
00074     if (mReservedLen > 0) {
00075         mpArray = ArrayAllocate(mReservedLen);
00076         if (mpArray == NULL) {
00077             handleOutOfMemory();
00078             mReservedLen = 0;
00079             mUsedLen = 0;
00080         }
00081         else {
00082             ArrayCopyConstruct(mpArray, src.mpArray, mUsedLen);
00083         }
00084     }
00085 }
00086 
00087 template <class T> Array<T>::~Array()
00088 {
00089     if (mpArray != NULL) {
00090         ArrayDestruct(mpArray, mUsedLen);
00091         ArrayDeAllocate(mpArray);
00092     }
00093 }
00094 
00095 // The assignment operator.  The assignment operator copies
00096 // the data from the source array to this array.  If the
00097 // source array contains more elements that this array has
00098 // space to store, then this array is grown to meet the demand.
00099 // Otherwise, the reserved length of this array does not change.
00100 // After this operation is completed the used lengths of the
00101 // two arrays will be equal.  The grow length of this array is
00102 // not affected by this operation.
00103 //
00104 template <class T> Array<T>& Array<T>::operator = (const Array<T>& src)
00105 {
00106     if (this != &src) {
00107         // Re-allocate the buffer if necessary
00108         if (mReservedLen < src.mUsedLen) {
00109             // Destroy the existing list
00110             if (mpArray != NULL) {
00111                 ArrayDestruct(mpArray, mUsedLen);
00112                 ArrayDeAllocate(mpArray);
00113             }
00114             // Allocate a new buffer
00115             mReservedLen = src.mUsedLen;
00116             mpArray = ArrayAllocate(mReservedLen);
00117             if (mpArray == NULL) { // ...so this only happens if failure.
00118                 handleOutOfMemory();
00119                 mReservedLen = 0;
00120                 mUsedLen = 0;
00121                 return *this;
00122             }
00123             // Copy the list
00124             mUsedLen = src.mUsedLen;
00125             ArrayCopyConstruct(mpArray, src.mpArray, mUsedLen);
00126         }
00127         else if(mUsedLen < src.mUsedLen) {
00128             // The entire destination list is to be overwritten
00129             ArrayCopy(mpArray, src.mpArray, mUsedLen);
00130             // Remaining elements need to be added to the list
00131             ArrayCopyConstruct(mpArray + mUsedLen, src.mpArray + mUsedLen, src.mUsedLen - mUsedLen);
00132             mUsedLen = src.mUsedLen;
00133         }
00134         else if(mUsedLen > src.mUsedLen) {
00135             // Copy the entire source list.
00136             ArrayCopy(mpArray, src.mpArray, src.mUsedLen);
00137             // Truncate unused elements in the destination list.
00138             ArrayDestruct(mpArray + src.mUsedLen, mUsedLen - src.mUsedLen);
00139             mUsedLen = src.mUsedLen;
00140         }
00141         else {
00142             // Lists are of identical size; simply copy the entire contents
00143             ArrayCopy(mpArray, src.mpArray, mUsedLen);
00144         }
00145     }
00146     return *this;
00147 }
00148 
00149 
00150 // The equal to operator.  The equal to operator compares
00151 // the data in two arrays.  If the used length of the
00152 // two arrays are the same and the corresponding entries of
00153 // the two arrays are equal, true is returned. Otherwise,
00154 // false is returned.
00155 //
00156 template <class T> bool Array<T>::operator == (const Array<T>& cpr) const
00157 {
00158     if (mUsedLen == cpr.mUsedLen)
00159     {
00160         for (size_t i = 0; i < mUsedLen; i++)
00161             if (mpArray[i] != cpr.mpArray[i])
00162                 return false;
00163         return true;
00164     }
00165     return false;
00166 }
00167 
00168 // Sets all the elements within the used length of the array,
00169 // (that is, elements 0..length()-1), to `value'.
00170 //
00171 template <class T> Array<T>& Array<T>::setAll(const T& value)
00172 {
00173     for (size_t i = 0; i < mUsedLen; i++) {
00174         mpArray[i] = value;
00175     }
00176     return *this;
00177 }
00178 
00179 // Appends the `otherArray' to the end of this array.  The used length of
00180 // this array will increase by the length of the `otherArray'.  If the
00181 // reserved length is not long enough it will increase by the amount
00182 // necessary to fit the newly added elements (with the usual caveat about
00183 // insufficient memory).
00184 //
00185 template <class T> Array<T>& Array<T>::append(const Array<T>& otherArray)
00186 {
00187     size_t otherLen = otherArray.length();
00188     if (otherLen == 0) {
00189         return *this;
00190     }
00191     size_t newLen = mUsedLen + otherLen;
00192     if (newLen > mReservedLen) {
00193         setLengthReserved(newLen);
00194     }
00195 
00196     ArrayCopyConstruct(mpArray + mUsedLen, otherArray.mpArray, otherLen);
00197 
00198     mUsedLen = newLen;
00199     return *this;
00200 }
00201 
00202 // Inserts `value' at `index'.  The value formerly at `index'
00203 // gets moved to `index+1',  `index+1 gets moved to `index+2' and so on.
00204 // Note that insertAt(length(), value) is equivalent to append(value).
00205 // The used length of the array will increase by one.  If the reserved
00206 // length is not long enough it will increase by the grow length (with the
00207 // usual caveat about insufficient memory).
00208 //
00209 template <class T> Array<T>& Array<T>::insertAt(size_t index, const T& value)
00210 {
00211     DbgAssert(index >= 0 && index <= mUsedLen);
00212 
00213     if (mUsedLen >= mReservedLen) {
00214         size_t growth = (mUsedLen * sizeof(T)) < kArrayGrowthThreshold ?
00215             (mUsedLen / 2) : kArrayGrowthThreshold / sizeof(T);
00216         setLengthReserved(mUsedLen + __max(growth, mGrowLen));
00217     }
00218 
00219     if (index != mUsedLen) {
00220 
00221         // Initialize the new member of the array
00222         ArrayCopyConstruct(mpArray + mUsedLen, mpArray + mUsedLen - 1, 1);
00223 
00224         // Copy the remainder of the list that needs to be shifted
00225         for(size_t i = mUsedLen - 1; i > index; --i) {
00226             mpArray[i] = mpArray[i-1];
00227         }
00228 
00229         // Now copy the new element into the array
00230         mpArray[index] = value;
00231     }
00232     else {
00233         // Add the new value to the end of the list
00234         ArrayCopyConstruct(mpArray + mUsedLen, &value, 1);
00235     }
00236 
00237     mUsedLen++;
00238     return *this;
00239 }
00240 
00241 template <class T> Array<T>& Array<T>::insertAt(size_t index, const T* values, size_t count)
00242 {
00243     DbgAssert(index >= 0 && index <= mUsedLen);
00244 
00245     if(index <= mUsedLen) {
00246 
00247         size_t lastInsertIndex = index + count - 1;
00248 
00249         // Increase the allocated memory if necessary
00250         size_t newUsedLen = mUsedLen + count;
00251         if(newUsedLen > mReservedLen) {
00252 
00253             // Allocate a new buffer
00254             T* newArray = ArrayAllocate(newUsedLen);
00255             if(newArray == NULL) {
00256                 // Can't insert the new element since the allocation failed.
00257                 handleOutOfMemory();
00258                 return *this;
00259             }
00260 
00261             // Copy existing elements located to the left of the insertion range
00262             ArrayCopyConstruct(newArray, mpArray, index);
00263 
00264             // Copy the inserted elements
00265             ArrayCopyConstruct(newArray + index, values, count);
00266 
00267             // Copy existing elements located to the right of the insertion range
00268             if(index < mUsedLen) {
00269                 ArrayCopyConstruct(newArray + index + count, mpArray + index, mUsedLen - index);
00270             }
00271 
00272             // Destroy the old array
00273             ArrayDestruct(mpArray, mUsedLen);
00274             ArrayDeAllocate(mpArray);
00275 
00276             mpArray = newArray;
00277             mUsedLen = newUsedLen;
00278             mReservedLen = newUsedLen;
00279         }
00280         else {
00281             if(index < mUsedLen) {
00282                 // Shift elements that get moved beyond the current limit of the array
00283                 ArrayCopyConstruct(mpArray + mUsedLen, mpArray + mUsedLen - count, count);
00284 
00285                 // Shift elements that stay inside the current limits of the array
00286                 if((index + count) < mUsedLen) {
00287                     ArrayCopyOverlap(mpArray + index + count, mpArray + index, mUsedLen - index - count);
00288                 }
00289 
00290                 // Copy new elements that get inserted within the current size of the array
00291                 if(lastInsertIndex < mUsedLen) {
00292                     ArrayCopy(mpArray + index, values, count);
00293                 }
00294                 else {
00295                     ArrayCopy(mpArray + index, values, mUsedLen - index);
00296                 }
00297             }
00298 
00299             // Copy new elements that get inserted beyond the current size of the array
00300             if(lastInsertIndex >= mUsedLen) {
00301                 size_t numElementsInserted = (mUsedLen - index);
00302                 DbgAssert(numElementsInserted < count);
00303                 ArrayCopyConstruct(mpArray + mUsedLen, values + numElementsInserted, count - numElementsInserted);
00304             }
00305 
00306             mUsedLen += count;
00307         }
00308     }
00309 
00310     return *this;
00311 }
00312 
00313 // Removes the element at `index'.  The used length will
00314 // decrease by one.  `index' MUST BE within bounds.
00315 //
00316 template <class T> Array<T>& Array<T>::removeAt(size_t index)
00317 {
00318     DbgAssert(isValidIndex(index));
00319 
00320     if(index < mUsedLen) {
00321         // Shift array elements to the left if needed.
00322         //
00323         if (index < mUsedLen - 1) {
00324             for(size_t i = index; i < mUsedLen - 1; ++i) {
00325                 mpArray[i] = mpArray[i+1];
00326             }
00327         }
00328 
00329         // Destroy the last element of the array
00330         ArrayDestruct(mpArray + mUsedLen - 1, 1);
00331 
00332         mUsedLen--;
00333     }
00334 
00335     return *this;
00336 }
00337 
00338 // Removes all elements starting with 'startIndex' and ending with 'endIndex'
00339 // The used length will decrease by number of removed elements.
00340 //
00341 template <class T> Array<T>& Array<T>::removeSubArray(size_t startIndex, size_t endIndex)
00342 {
00343     DbgAssert(isValidIndex(startIndex));
00344     DbgAssert(startIndex <= endIndex);
00345 
00346     if(startIndex < mUsedLen) {
00347 
00348         if(endIndex >= mUsedLen) {
00349             endIndex = mUsedLen - 1;
00350         }
00351 
00352         size_t numToRemove = endIndex - startIndex + 1;
00353 
00354         // Shift all elements that reside on the right of the sub-array to be removed
00355         for(size_t i = endIndex + 1; i < mUsedLen; ++i) {
00356             mpArray[i - numToRemove] = mpArray[i];
00357         }
00358 
00359         // Truncate the array
00360         ArrayDestruct(mpArray + mUsedLen - numToRemove, numToRemove);
00361 
00362         mUsedLen -= numToRemove;
00363     }
00364 
00365     return *this;
00366 }
00367 
00368 // Returns true if and only if the array contains `value' from
00369 // index `start' onwards.  Returns, in `index', the first location
00370 // that contains `value'.  The search begins at position `start'.
00371 // `start' is supplied with a default value of `0', i.e., the
00372 // beginning of the array.
00373 //
00374 template <class T> bool Array<T>::find(const T& value, size_t& index, size_t start) const
00375 {
00376     const size_t nFoundAt = this->findFrom(value, start);
00377     if (nFoundAt == -1)
00378         return false;
00379     index = nFoundAt;
00380     return true;
00381 }
00382 
00383 template <class T> size_t Array<T>::find(const T& value) const
00384 {
00385     return this->findFrom(value, 0);   // search from the beginning
00386 }
00387 
00388 template <class T> size_t Array<T>::findFrom(const T& value, size_t start) const
00389 {
00390     for (size_t i = start; i < this->mUsedLen; i++) {
00391         if (mpArray[i] == value)
00392             return i;
00393     }
00394     return (size_t)-1;
00395 }
00396 
00397 // Allows you to set the used length of the array.
00398 // If you try to set the used length to be greater than
00399 // the reserved length, then the array is grown to a
00400 // reasonable size (thus increasing both the used length
00401 // AND the reserved length).
00402 // Also, the reserved length will grow in growth length
00403 // steps.
00404 template <class T> Array<T>& Array<T>::setLengthUsed(size_t n, const T& defaultVal)
00405 {
00406     DbgAssert(n >= 0);
00407     if (n > mReservedLen) {
00408 
00409         size_t growth = (mReservedLen * sizeof(T)) < kArrayGrowthThreshold ?
00410             (mReservedLen / 2) : kArrayGrowthThreshold / sizeof(T);
00411 
00412         size_t minSize = mReservedLen + __max(growth, mGrowLen);
00413         if ( n > minSize)
00414             minSize = n;
00415         setLengthReserved(minSize);
00416     }
00417 
00418     if(n > mUsedLen) {
00419         // Initialize the new elements
00420         ArrayConstruct(mpArray + mUsedLen, n - mUsedLen, defaultVal);
00421     }
00422     else {
00423         // Destroy the elements to be removed
00424         ArrayDestruct(mpArray + n, mUsedLen - n);
00425     }
00426 
00427     mUsedLen = n;
00428     return *this;
00429 }
00430 
00431 // Allows you to set the reserved length of the array.
00432 // If you set the reserved length to be less than
00433 // the used length, then the used length is reset
00434 // to match the new reserved length.  A reserved length
00435 // of zero is valid.
00436 //
00437 template <class T> Array<T>& Array<T>::setLengthReserved(size_t n)
00438 {
00439     DbgAssert(n >= 0);
00440 
00441     if(n != mReservedLen) {
00442 
00443         if(n == 0) {
00444             if(mReservedLen > 0) {
00445                 ArrayDestruct(mpArray, mUsedLen);
00446                 ArrayDeAllocate(mpArray);
00447                 mpArray = NULL;
00448                 mUsedLen = 0;
00449                 mReservedLen = 0;
00450             }
00451         }
00452         else if(mReservedLen == 0) {
00453             mpArray = ArrayAllocate(n);
00454             if(mpArray == NULL) {
00455                 // Failure to allocate memory; can't increase the reserved length.
00456                 handleOutOfMemory();
00457                 return *this;
00458             }
00459             mReservedLen = n;
00460         }
00461         else {
00462             T* oldArray = mpArray;
00463             size_t oldUsedLen = mUsedLen;
00464 
00465             // Allocate the new array
00466             mpArray = ArrayAllocate(n);
00467             if(mpArray == NULL) {
00468                 // Failure to allocate memory; can't change the reserved length.
00469                 handleOutOfMemory();
00470                 mpArray = oldArray;
00471                 return *this;
00472             }
00473 
00474             // Copy the old array to the new one.
00475             if(n < mUsedLen) {
00476                 // The old members don't all fit in the new array
00477                 ArrayCopyConstruct(mpArray, oldArray, n);
00478                 mUsedLen = n;
00479             }
00480             else {
00481                 ArrayCopyConstruct(mpArray, oldArray, mUsedLen);
00482             }
00483             mReservedLen = n;
00484 
00485             // Destroy the old array
00486             ArrayDestruct(oldArray, oldUsedLen);
00487             ArrayDeAllocate(oldArray);
00488         }
00489     }
00490 
00491     return *this;
00492 }
00493 
00494 // Reverses the order of the array.  That is if you have two
00495 // arrays, `a' and `b', then if you assign `a = b' then call
00496 // `a.reverse()' then a[0] == b[n], a[1] == b[n-1],... a[n] == b[0].
00497 //
00498 template <class T> Array<T>& Array<T>::reverse()
00499 {
00500     size_t halfUsedLen = mUsedLen/2;
00501     for (size_t i = 0; i < halfUsedLen; i++) {
00502         T tmp = mpArray[i];
00503         mpArray[i] = mpArray[mUsedLen - 1 - i];
00504         mpArray[mUsedLen - 1 - i] = tmp;
00505     }
00506     return *this;
00507 }
00508 
00509 // Swaps the elements in `i1' and `i2'.
00510 //
00511 template <class T> Array<T>& Array<T>::swap(size_t i1, size_t i2)
00512 {
00513     DbgAssert(isValidIndex(i1));
00514     DbgAssert(isValidIndex(i2));
00515 
00516     if (i1 == i2) return *this;
00517 
00518     T tmp = mpArray[i1];
00519     mpArray[i1] = mpArray[i2];
00520     mpArray[i2] = tmp;
00521     return *this;
00522 }
00523 
00524 // Returns true if and only if `value' was removed from the array from
00525 // position `start' onwards.  Only the first occurrence of `value'
00526 // is removed.  Calling this function is equivalent to doing a "find(),
00527 // then "removeAt()".
00528 //
00529 template <class T> bool Array<T>::remove(const T& value, size_t start)
00530 {
00531     const size_t i = this->findFrom(value, start);
00532     if (i == -1)
00533         return false;
00534     this->removeAt(i);
00535     return true;
00536 }
00537 
00538 #pragma warning(push)
00539 #pragma warning(disable:4127)
00540 template <class T> void Array<T>::sort(CompareFnc cmp) {
00541 
00542     if(mUsedLen > 1) {
00543         // Use the standard C function of the type doesn't have a copy operator
00544         // (meaning that memcpy() is safe)
00545         /*  From MSDN: The compiler's support for type traits allows library writers to 
00546             determine various characteristics of a type at compile time. 
00547             All type traits return false if the specified conditions are not met. 
00548             Returns true if the CLR or native type has a copy assignment operator. */
00549         if(!__has_assign(T)) {
00550             qsort(mpArray, mUsedLen, sizeof(T), cmp);
00551         }
00552         else {
00553             quickSortRecursive(mpArray, 0, mUsedLen - 1, cmp);
00554         }
00555     }
00556 }
00557 #pragma warning(pop)
00558 
00559 template <class T> size_t Array<T>::quickSortPartition(T* data, size_t first, size_t last, CompareFnc cmp)
00560 {
00561     const T& pivot = data[last]; // use the last item as the pivot
00562     size_t left = first; // sort from the first item
00563     size_t right = last - 1; // sort to the item excluding the pivot
00564 
00565     do {
00566         while ((left < last) && (cmp(&(data[left]), &pivot) <= 0))
00567         {
00568             ++left;
00569         }
00570         while ((right > first) && (cmp(&(data[right]), &pivot) >= 0))
00571         {
00572             --right;
00573         }
00574         if (left < right) {
00575             T swapValue = data[left];
00576             data[left] = data[right];
00577             data[right] = swapValue;
00578         }
00579     } while (left < right);
00580 
00581     if (cmp(&data[left], &pivot) > 0)
00582     {
00583         T swapValue = data[left];
00584         data[left] = data[last];
00585         data[last] = swapValue;
00586     }
00587 
00588     return left;
00589 }
00590 
00591 template <class T> void Array<T>::quickSortRecursive(T* data, size_t first, size_t last, CompareFnc cmp)
00592 {
00593     if (first < last)
00594     {
00595         size_t pivot_position = quickSortPartition(data, first, last, cmp);
00596 
00597         // Protect against overflow. Normally the "if (first < last)" test would
00598         // guard against this, but size_t is unsigned, meaning "right - 1" can result
00599         // in a test of -1 > 0 when right is 0, which is an invalid unsigned inequality.
00600         if (pivot_position > 0)
00601         {
00602             quickSortRecursive(data, first, pivot_position - 1, cmp);
00603         }
00604         quickSortRecursive(data, pivot_position + 1, last, cmp);
00605     }
00606 }