From: AMERICA@PRL.PHILIPS.NL
Newsgroups: comp.sys.mac.oop.macapp3
Subject: bugs in CArrayIterator
Date: 16 Dec 92 11:15:00 GMT
Organization: AppleLink Gateway

Dear Colleagues,
 
There is a bug in CArrayIterator::DeleteElementAt, which causes improper
behavior when a whole range of elements is deleted at a time (theCount > 1)
and the lower bound, the upper bound, or the current index is in that range.
(I found this bug when halfway through the iteration I deleted all array
elements and suddenly found out that the current index had become negative.)
 
Here is my version of the method, which fixes the problem:
 
void CArrayIterator::DeleteElementAt(ArrayIndex theIndex, ArrayIndex theCount)
{
        // tuck the endpoints of the iteration in to match
        if (theIndex < fLowBound)
                fLowBound = Max (fLowBound - theCount, theIndex);
                // This is equivalent to:
                //      if (theIndex + theCount <= fLowBound)
                //              fLowBound -= theCount;
                //              // whole deleted range is below fLowBound
                //      else
                //              fLowBound = theIndex;
                //              // new index of the first element not deleted
 
        if (theIndex <= fHighBound)
                fHighBound = Max (fHighBound - theCount, theIndex - 1);
                // Similar: theIndex - 1 is the last element not deleted.
 
        // Now bend the fCurrentIndex to account for the deletion.
        // In case the current item is deleted, we give fCurrentIndex such a
        // value that AFTER calling the method advance it will point to the n
ext
        // element to be processed.
        if (fIterateForward) {
            if (theIndex <= fCurrentIndex)
                fCurrentIndex = Max (fCurrentIndex - theCount, theIndex - 1);
        }
        else {  // Iterating backwards
            if (theIndex < fCurrentIndex)
                fCurrentIndex = Max (fCurrentIndex - theCount, theIndex);
        }
 
        // hand off control to the next link until you hit the last link in t
he circular chain
        if (fNextLink != fDynamicArray->fIteratorPtr)
                fNextLink->DeleteElementAt(theIndex, theCount);
} // CArrayIterator::DeleteElementAt
 
 
There is another bug in CArrayIterator::IArrayIterator, which turns up if you
specify a lower bound that is higher than the upper bound. In this case, the
iteration should be empty, but instead the element at the upper bound will be
processed. The following version of the method corrects the problem:
 
void CArrayIterator::IArrayIterator(TDynamicArray* itsDynamicArray,
                                    ArrayIndex itsLowBound,
                                    ArrayIndex itsHighBound,
                                    Boolean itsForward)
{
        fNextLink = this;
        fPreviousLink = this;
        fIterateForward = itsForward;
        fDynamicArray = itsDynamicArray;
 
        // Trim the iteration bounds to the array bounds, but allow empty ran
ge.
        fHighBound= MinMax(kEmptyIndex,itsHighBound,itsDynamicArray->GetSize(
));
        fLowBound = MinMax(1, itsLowBound, fHighBound + 1);
 
        if (fLowBound > fHighBound) {   // empty iteration
                fLowBound = kEmptyIndex;
                fHighBound = kEmptyIndex;
        }
 
        // set the initial value of the current index based on the direction
of the iteration.
        else if (fIterateForward)
                fCurrentIndex = fLowBound;
        else
                fCurrentIndex = fHighBound;
 
        // Make sure that the fCurrentIndex counter and Iteration direction f
lag from
        // an Iterator are used so that the fCurrentIndex counter can be "ben
t" if anyone
        // else deletes or inserts elements while the iteration is in progres
s. Pretty slick, eh?
 
        // link me in to the list of iterations in progress
        fDynamicArray->fIteratorPtr = this->AppendToList(fDynamicArray->fIter
atorPtr);
 
        // Setup the FailInfo to catch any failures while this object is in s
cope
        // giving control to the HandleFailure method
        CatchFailures(this->fFailInfo, (CatchFailuresType) & CArrayIterator::
HandleFailure, this);
} // CArrayIterator::IArrayIterator
 
 
Pierre America
Philips Research Laboratories
P. O. Box 80.000
5600 JA Eindhoven
The Netherlands
 
Internet: america@prl.philips.nl