// // NSIndexPathSet.m // mySTEP // // Created by Dr. H. Nikolaus Schaller on Tue Nov 22 2005. // Copyright (c) 2005 DSITRI. // // This file is part of the mySTEP Library and is provided // under the terms of the GNU Library General Public License. // // CODE NOT TESTED #import "Foundation/NSIndexPath.h" #import "Foundation/NSIndexSet.h" #import "NSPrivate.h" static NSIndexPath *_root; @implementation NSIndexPath - (NSString *) description; { if(_parent) return [NSString stringWithFormat:@"%@.%u", [_parent description], _index]; else return [NSString stringWithFormat:@"%u", _index]; // first } + (NSIndexPath *) indexPathWithIndex:(unsigned) idx; { return [self indexPathWithIndexes:&idx length:1]; } + (NSIndexPath *) indexPathWithIndexes:(unsigned *) idx length:(unsigned) len; { return [[[self alloc] initWithIndexes:idx length:len] autorelease]; } - (BOOL) isEqual:(id) obj; { return obj == self; } - (NSComparisonResult) compare:(NSIndexPath *) obj; { unsigned int i; unsigned int objLength=obj->_length; if(obj == self) return NSOrderedSame; // must be the same for(i=0; i<_length && i < objLength; i++) { unsigned my=[self indexAtPosition:i]; unsigned other=[obj indexAtPosition:i]; if(my < other) return NSOrderedAscending; if(my > other) return NSOrderedDescending; } if(_length < objLength) return NSOrderedAscending; if(_length > objLength) return NSOrderedDescending; return NSOrderedSame; // all the same } - (void) getIndexes:(unsigned *) idx; { if(_parent) [_parent getIndexes:idx]; // fill prefix part idx[_length]=_index; } - (unsigned) indexAtPosition:(unsigned) pos; { if(pos > _length) return NSNotFound; while(pos < _length) self=_parent; // go back return _index; } - (NSIndexPath *) indexPathByAddingIndex:(unsigned) idx; { NSEnumerator *e=[_children objectEnumerator]; // optimize by using NSMapTable! NSIndexPath *child; // FIXME: this search could be optimized by a NSMapTable mapping child indexes to objects (if they exist) while((child=[e nextObject])) { if(child->_index == idx) return child; // already stored } if(!_children) _children=[[NSMutableArray alloc] initWithCapacity:10]; // guess could be better estimated child=[[isa alloc] init]; // allocate a fresh node child->_parent=self; child->_length=_length+1; // one level down child->_index=idx; // FIXME: should be a NSMapTable [_children addObject:child]; return [child autorelease]; } - (NSIndexPath *) indexPathByRemovingLastIndex; { return _parent; } - (unsigned) length; { return _length; } - (id) initWithIndex:(unsigned) index; { return [self initWithIndexes:&index length:1]; } - (id) initWithIndexes:(unsigned *) idx length:(unsigned) len; { if(!_root) _root=self; // first call: make us the root object else [self release]; self=_root; while(len-- > 0) self=[self indexPathByAddingIndex:*idx++]; // walk through tree creating subnodes if needed return [self retain]; } - (void) dealloc; { [_children release]; [super dealloc]; } - (id) copyWithZone:(NSZone *) zone; { return [self retain]; } // not really copied - allows to use us as a key for NSDictionary - (void) encodeWithCoder:(NSCoder *) coder; { // encode length // encode indexes NIMP; } - (id) initWithCoder:(NSCoder *) coder; { // decode length // crate temp buffer // decode indexes // return [self initWithIndexes:&index length:length]; return NIMP; } @end @implementation NSIndexSet + (id) indexSet; { return [[self new] autorelease]; } + (id) indexSetWithIndex:(unsigned int) value; { return [[[self alloc] initWithIndex:value] autorelease]; } + (id) indexSetWithIndexesInRange:(NSRange) range; { return [[[self alloc] initWithIndexesInRange:range] autorelease]; } - (id) copyWithZone:(NSZone *) zone; { return [self retain]; } - (id) mutableCopyWithZone:(NSZone *) zone; { return [[NSMutableIndexSet allocWithZone:zone] initWithIndexSet:self]; } - (id) init; { if((self=[super init])) { // no special initialization - _nranges==0 } return self; } - (void) dealloc; { if(_indexRanges) objc_free(_indexRanges); [super dealloc]; } - (NSString *) description; { NSString *r=nil; unsigned int i; if(_nranges == 0) return @""; // empty indexset for(i=0; i<_nranges; i++) { if(r) r=[r stringByAppendingFormat:_indexRanges[i].length==1?@"%@,%u":@"%@, %u-%u", r, _indexRanges[i].location, NSMaxRange(_indexRanges[i])-1]; else r=[NSString stringWithFormat:_indexRanges[i].length==1?@"%u":@"%u-%u", _indexRanges[i].location, NSMaxRange(_indexRanges[i])-1]; } return r; } - (id) initWithIndex:(unsigned int) value; { return [self initWithIndexesInRange:NSMakeRange(value, 1)]; } - (id) initWithIndexesInRange:(NSRange) range; { if((self=[super init])) { _nranges=1; _indexRanges=(NSRange *) objc_malloc(sizeof(_indexRanges[0])); // allocate one element _indexRanges[0]=range; // store } return self; } - (id) initWithIndexSet:(NSIndexSet *) other; { if((self=[super init])) { _nranges=other->_nranges; _indexRanges=(NSRange *) objc_malloc(_nranges*sizeof(_indexRanges[0])); // allocate one element memcpy(_indexRanges, other->_indexRanges, _nranges*sizeof(_indexRanges[0])); // copy } return self; } - (void) encodeWithCoder:(NSCoder *) coder; { [coder encodeValueOfObjCType:@encode(unsigned int) at:&_nranges]; [coder encodeArrayOfObjCType:@encode(NSRange) count:_nranges at:_indexRanges]; } - (id) initWithCoder:(NSCoder *) coder; { if((self=[super init])) { [coder decodeValueOfObjCType:@encode(unsigned int) at:&_nranges]; _indexRanges=(NSRange *) objc_malloc(_nranges*sizeof(_indexRanges[0])); // allocate elements [coder decodeArrayOfObjCType:@encode(NSRange) count:_nranges at:_indexRanges]; } return self; } - (BOOL) isEqual:(id) indexSet; { return [indexSet isKindOfClass:isa] && [self isEqualToIndexSet:indexSet]; } - (BOOL) isEqualToIndexSet:(NSIndexSet *) other; { // segments must be identical since we don't allow overlapping subranges adjacent segments unsigned int i; if(_nranges != other->_nranges) return NO; for(i=0; i<_nranges; i++) { if(!NSEqualRanges(_indexRanges[i], other->_indexRanges[i])) return NO; } return YES; } - (BOOL) containsIndex:(unsigned int) value; { unsigned int i; for(i=0; i<_nranges; i++) { if(NSLocationInRange(value, _indexRanges[i])) return YES; } return NO; } - (BOOL) containsIndexes:(NSIndexSet *) other; { // contains ALL indexes of the other set NIMP; return NO; } - (BOOL) containsIndexesInRange:(NSRange) range; { // there must be a segment that completely overlaps with range (there can't be two because they should have been merged!) unsigned int i; // we could even search faster by splitting the total set of ranges in halves because they are sorted for(i=0; i<_nranges; i++) { if(NSEqualRanges(NSIntersectionRange(range, _indexRanges[i]), range)) return YES; } return NO; } - (BOOL) intersectsIndexesInRange:(NSRange) range; { // if any index is in the set unsigned int i; for(i=0; i<_nranges; i++) { if(NSIntersectionRange(range, _indexRanges[i]).length != 0) return YES; } return NO; } - (unsigned int) count; { unsigned int i; if(!_count1) { // value should be cached! _count1=1; // one more... for(i=0; i<_nranges; i++) _count1+=_indexRanges[i].length; // sum up } return _count1-1; } - (unsigned int) hash { // hashing must be fast! if(!_count1) [self count]; // recache return _count1; // should be a good indicator... } - (unsigned int) firstIndex; { if(_nranges == 0) return NSNotFound; return _indexRanges[0].location; } - (unsigned int) lastIndex; { if(_nranges == 0) return NSNotFound; return NSMaxRange(_indexRanges[_nranges-1])-1; // last index } - (unsigned int) indexGreaterThanIndex:(unsigned int) value; { unsigned int i=0; while(i<_nranges) { if(_indexRanges[i].location > value) return _indexRanges[i].location; // range segment beyond if(NSLocationInRange(value+1, _indexRanges[i])) return value+1; // next index falls into this subrange i++; } return NSNotFound; } - (unsigned int) indexGreaterThanOrEqualToIndex:(unsigned int) value; { unsigned int i=0; while(i<_nranges) { if(_indexRanges[i].location > value) return _indexRanges[i].location; // range segment is beyond if(NSLocationInRange(value, _indexRanges[i])) return value; // falls into this subrange i++; } return NSNotFound; } - (unsigned int) indexLessThanIndex:(unsigned int) value; { unsigned int i=_nranges; while(i-- > 0) { if(NSMaxRange(_indexRanges[i]) <= value) return NSMaxRange(_indexRanges[i])-1; // range segment before if(NSLocationInRange(value-1, _indexRanges[i])) return value-1; // previous index falls into this subrange } return NSNotFound; } - (unsigned int) indexLessThanOrEqualToIndex:(unsigned int) value; { unsigned int i=_nranges; while(i-- > 0) { if(NSMaxRange(_indexRanges[i]) <= value) return NSMaxRange(_indexRanges[i])-1; // range segment before if(NSLocationInRange(value, _indexRanges[i])) return value; //index falls into this subrange } return NSNotFound; } - (unsigned int) getIndexes:(unsigned int *) buffer maxCount:(unsigned int) cnt inIndexRange:(NSRangePointer) indexRange; { unsigned int i; unsigned c0=cnt; if(!indexRange) { // unlimited for(i=0; i<_nranges && cnt > 0; i++) { unsigned int val=_indexRanges[i].location; unsigned int last=NSMaxRange(_indexRanges[i]); while(val < last && cnt > 0) *buffer++=val++, cnt--; } } else { for(i=0; i<_nranges && NSMaxRange(_indexRanges[i]) < indexRange->location; i++) ; // find first relevant block for(; i<_nranges && cnt > 0; i++) { // extract next index block unsigned int val=_indexRanges[i].location; unsigned int last=NSMaxRange(_indexRanges[i]); if(val < indexRange->location) val=indexRange->location; // don't start before requested range if(last > NSMaxRange(*indexRange)) last=NSMaxRange(*indexRange); // limit to end of requested range if(last < val) break; // already done and nothing more to add while(val < last && cnt > 0) *buffer++=val++, cnt--; } // update indexRange } return c0-cnt; // number of entries copied } - (NSUInteger) countOfIndexesInRange:(NSRange) value { unsigned int i=0; NSUInteger count=0; while(i<_nranges) { NSRange isect=NSIntersectionRange(value, _indexRanges[i]); count+=isect.length; i++; } return count; } @end @implementation NSMutableIndexSet - (id) initWithIndex:(unsigned int) value; { self=[super initWithIndex:value]; if(self) _capacity=_nranges; // that is what we currently have allocated return self; } - (id) initWithIndexesInRange:(NSRange) range; { self=[super initWithIndexesInRange:range]; if(self) _capacity=_nranges; // that is what we currently have allocated return self; } - (id) initWithIndexSet:(NSIndexSet *) other; { self=[super initWithIndexSet:other]; if(self) _capacity=_nranges; // that is what we currently have allocated return self; } - (id) initWithCoder:(NSCoder *) coder; { self=[super initWithCoder:coder]; if(self) _capacity=_nranges; // that is what we currently have allocated return self; } - (id) copyWithZone:(NSZone *) zone; { // immutable copy of mutable object return [[NSIndexSet allocWithZone:zone] initWithIndexSet:self]; } - (id) mutableCopyWithZone:(NSZone *) zone; { return [[NSMutableIndexSet allocWithZone:zone] initWithIndexSet:self]; } - (void) _addIndexesInRanges:(NSRangePointer) ranges count:(unsigned) count; { unsigned int i=0, j; for(j=0; j_indexRanges count:other->_nranges]; } - (void) addIndexesInRange:(NSRange) range; { [self _addIndexesInRanges:&range count:1]; } - (void) _removeIndexesInRanges:(NSRangePointer) ranges count:(unsigned) count; { unsigned int i=0, j; for(j=0; j split (i.e. delete (3-5) from (1-7) -> (1-2,6-7) if(_nranges == _capacity) { #if 0 NSLog(@"increase capacity (%d)", _capacity); #endif _indexRanges=(NSRange *) objc_realloc(_indexRanges, sizeof(_indexRanges[0])*(_capacity+=3)); // make more room #if 0 NSLog(@"increased capacity (%d)", _capacity); #endif } memmove(&_indexRanges[i+1], &_indexRanges[i], sizeof(_indexRanges[0])*(_nranges-i)); // FIXME: _indexRanges[i+1].location=NSMaxRange(range); // second subrange _indexRanges[i+1].length=NSMaxRange(_indexRanges[i])-_indexRanges[i+1].location; // second subrange _indexRanges[i].length=range.location-_indexRanges[i].location; // first subrange _nranges++; // we now have one more } else _indexRanges[i]=inter; // reduce to intersection _count1=0; // recache } else i++; // no intersection } } #if 0 NSLog(@"result: %@", self); #endif } - (void) removeIndex:(unsigned int) value; { NSRange range=NSMakeRange(value, 1); [self _removeIndexesInRanges:&range count:1]; } - (void) removeIndexes:(NSIndexSet *) other; { // remove all segments [self _removeIndexesInRanges:other->_indexRanges count:other->_nranges]; } - (void) removeIndexesInRange:(NSRange) range; { [self _removeIndexesInRanges:&range count:1]; } - (void) removeAllIndexes; { _nranges=0; } - (void) shiftIndexesStartingAtIndex:(unsigned int) index by:(int) delta; { if(delta < 0) [self removeIndexesInRange:NSMakeRange(index-delta, delta)]; // ensure they don't exist // find block which contains starting index // if starting index is not _firstIndex, split into two ranges so that we can really shift // add delta to all firstIndex values until end _count1=0; // recache NIMP; } @end