diff --git a/ChangeLog b/ChangeLog index 1f61598e9..f53ceb31e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2004-06-25 Richard Frith-Macdonald + + * Source/NSIndexSet.m: Rewrite range removal to fix bug reported + by Fred Kiefer. + * Testing/nsindexset.m: Add test for deletion of range which has + partial overlap with two others. + 2004-06-24 Richard Frith-Macdonald * Source/NSIndexSet.m: Remove some redundant code for clarity. diff --git a/Source/NSIndexSet.m b/Source/NSIndexSet.m index 099dc7f4d..9d8214c84 100644 --- a/Source/NSIndexSet.m +++ b/Source/NSIndexSet.m @@ -710,6 +710,7 @@ static unsigned posForIndex(GSIArray array, unsigned index) - (void) removeIndexesInRange: (NSRange)aRange { unsigned pos; + NSRange r; if (NSNotFound - aRange.length < aRange.location) { @@ -717,95 +718,103 @@ static unsigned posForIndex(GSIArray array, unsigned index) format: @"[%@-%@]: Bad range", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; } - if (aRange.length == 0 || _array == 0 || GSIArrayCount(_array) == 0) + if (aRange.length == 0 || _array == 0 + || (pos = posForIndex(_array, aRange.location)) >= GSIArrayCount(_array)) { return; // Already empty } - pos = posForIndex(_array, aRange.location); + + r = GSIArrayItemAtIndex(_array, pos).ext; + if (r.location <= aRange.location) + { + if (r.location == aRange.location) + { + if (NSMaxRange(r) <= NSMaxRange(aRange)) + { + /* + * Found range is entirely within range to remove, + * leaving next range to check at current position. + */ + GSIArrayRemoveItemAtIndex(_array, pos); + } + else + { + /* + * Range to remove is entirely within found range and + * overlaps the start of the found range ... shrink it + * and trhen we are finished. + */ + r.location += aRange.length; + r.length -= aRange.length; + GSIArraySetItemAtIndex(_array, (GSIArrayItem)r, pos); + return; + } + } + else + { + if (NSMaxRange(r) <= NSMaxRange(aRange)) + { + /* + * Range to remove overlaps the end of the found range. + * May also overlap next range ... so shorten found + * range and move on. + */ + r.length = aRange.location - r.location; + GSIArraySetItemAtIndex(_array, (GSIArrayItem)r, pos); + pos++; + } + else + { + NSRange next = r; + + /* + * Range to remove is entirely within found range and + * overlaps the middle of the found range ... split it. + * Then we are finished. + */ + next.location = NSMaxRange(aRange); + next.length = NSMaxRange(r) - next.location; + r.length = aRange.location - r.location; + GSIArraySetItemAtIndex(_array, (GSIArrayItem)r, pos); + pos++; + GSIArrayInsertItem(_array, (GSIArrayItem)next, pos); + return; + } + } + } /* - * Remove any ranges contained entirely in the one to be removed. + * At this point we are guaranteed that, if there is a range at pos, + * it does not start before aRange.location */ while (pos < GSIArrayCount(_array)) { NSRange r = GSIArrayItemAtIndex(_array, pos).ext; - if (r.location < aRange.location || NSMaxRange(r) > NSMaxRange(aRange)) - { - break; - } - GSIArrayRemoveItemAtIndex(_array, pos); - } - - if (pos < GSIArrayCount(_array)) - { - NSRange r = GSIArrayItemAtIndex(_array, pos).ext; - - if (r.location <= aRange.location) + if (NSMaxRange(r) <= NSMaxRange(aRange)) { /* - * The existing range might overlap or mcontain the range to remove. + * Found range is entirely within range to remove ... + * delete it. */ - if (NSMaxRange(r) >= NSMaxRange(aRange)) + GSIArrayRemoveItemAtIndex(_array, pos); + } + else + { + if (r.location < NSMaxRange(aRange)) { /* - * Range to remove is contained in the range we found ... + * Range to remove overlaps start of found range ... + * shorten it. */ - if (r.location == aRange.location) - { - /* - * Remove from start of range. - */ - r.length -= aRange.length; - r.location += aRange.length; - GSIArraySetItemAtIndex(_array, (GSIArrayItem)r, pos); - } - else if (NSMaxRange(r) == NSMaxRange(aRange)) - { - /* - * Remove from end of range. - */ - r.length -= aRange.length; - GSIArraySetItemAtIndex(_array, (GSIArrayItem)r, pos); - } - else - { - NSRange t; - unsigned p; - - /* - * Split the range. - */ - p = NSMaxRange(aRange); - t = NSMakeRange(p, NSMaxRange(r) - p); - GSIArrayInsertItem(_array, (GSIArrayItem)t, pos+1); - r.length = aRange.location - r.location; - GSIArraySetItemAtIndex(_array, (GSIArrayItem)r, pos); - } - } - else if (NSMaxRange(r) >= aRange.location) - { - /* - * The range to remove overlaps the one we found. - */ - r.length = aRange.location - r.location; + r.length = NSMaxRange(r) - NSMaxRange(aRange); + r.location = NSMaxRange(aRange); GSIArraySetItemAtIndex(_array, (GSIArrayItem)r, pos); - - if (++pos < GSIArrayCount(_array)) - { - NSRange r = GSIArrayItemAtIndex(_array, pos).ext; - - if (r.location < NSMaxRange(aRange)) - { - /* - * and also overlaps the following range. - */ - r.length -= NSMaxRange(aRange) - r.location; - r.location = NSMaxRange(aRange); - GSIArraySetItemAtIndex(_array, (GSIArrayItem)r, pos); - } - } } + /* + * Found range extends beyond range to remove ... finished. + */ + return; } } } diff --git a/Testing/nsindexset.m b/Testing/nsindexset.m index b11535432..6c172e182 100644 --- a/Testing/nsindexset.m +++ b/Testing/nsindexset.m @@ -6,6 +6,7 @@ main () NSAutoreleasePool *arp = [NSAutoreleasePool new]; NSIndexSet *s; NSMutableIndexSet *m; + NSMutableIndexSet *o; unsigned int buf[2]; NSRange r; @@ -179,6 +180,30 @@ main () [m removeIndexesInRange: NSMakeRange(0, 11)]; printf(" %s\n", [m isEqual: [NSIndexSet indexSet]] == YES ? "passed" : "failed"); + o = [NSMutableIndexSet indexSet]; + [m addIndex: 3]; + [m addIndex: 4]; + [m addIndex: 6]; + [m addIndex: 7]; + [o addIndex: 3]; + [o addIndex: 7]; + printf("Can remove range 4-6 from mutable set containing 3,4,6,7 ..."); + [m removeIndexesInRange: NSMakeRange(4, 3)]; + printf(" %s\n", [m isEqual: o] == YES ? "passed" : "failed"); + + [m addIndex: 3]; + [m addIndex: 4]; + [m addIndex: 6]; + [m addIndex: 7]; + [m addIndex: 8]; + [m addIndex: 9]; + [o addIndex: 3]; + [o removeIndex: 7]; + [o addIndex: 9]; + printf("Can remove range 4-8 from mutable set containing 3,4,6,7,8,9 ..."); + [m removeIndexesInRange: NSMakeRange(4, 5)]; + printf(" %s\n", [m isEqual: o] == YES ? "passed" : "failed"); + // NSLog(@"%@", m); [arp release]; exit (0);