// // AMutableArray.m // a_ST4 // // Created by Alan Condit on 3/12/11. // Copyright 2011 Alan's MachineWorks. All rights reserved. // #import "AMutableArray.h" #import "ArrayIterator.h" #define BUFFSIZE 25 @implementation AMutableArray @synthesize BuffSize; @synthesize buffer; @synthesize ptrBuffer; //@synthesize count; + (id) newArray { return [[AMutableArray alloc] init]; } + (id) arrayWithCapacity:(NSInteger)size { return [[AMutableArray alloc] initWithCapacity:size]; } - (id) init { self=[super init]; if ( self != nil ) { BuffSize = BUFFSIZE; buffer = [[NSMutableData dataWithLength:(BuffSize * sizeof(id))] retain]; ptrBuffer = (id *)[buffer mutableBytes]; for( int idx = 0; idx < BuffSize; idx++ ) { ptrBuffer[idx] = nil; } } return self; } - (id) initWithCapacity:(NSInteger)len { self=[super init]; if ( self != nil ) { BuffSize = (len >= BUFFSIZE) ? len : BUFFSIZE; buffer = [[NSMutableData dataWithLength:(BuffSize * sizeof(id))] retain]; ptrBuffer = (id *)[buffer mutableBytes]; for( int idx = 0; idx < BuffSize; idx++ ) { ptrBuffer[idx] = nil; } } return self; } - (void) dealloc { #ifdef DEBUG_DEALLOC NSLog( @"called dealloc in AMutableArray" ); #endif if ( count ) [self removeAllObjects]; if ( buffer ) [buffer release]; [super dealloc]; } - (id) copyWithZone:(NSZone *)aZone { AMutableArray *copy; copy = [[[self class] allocWithZone:aZone] init]; if ( buffer ) { copy.buffer = [buffer copyWithZone:aZone]; } copy.ptrBuffer = [copy.buffer mutableBytes]; copy.count = count; copy.BuffSize = BuffSize; return copy; } - (void) addObject:(id)anObject { if ( anObject == nil ) anObject = [NSNull null]; [anObject retain]; [self ensureCapacity:count]; ptrBuffer[count++] = anObject; } - (void) addObjectsFromArray:(NSArray *)otherArray { NSInteger cnt, i; id tmp; cnt = [otherArray count]; [self ensureCapacity:count+cnt]; for( i = 0; i < cnt; i++) { tmp = [otherArray objectAtIndex:i]; [self addObject:tmp]; } return; } - (id) objectAtIndex:(NSInteger)anIdx { id obj; if ( anIdx < 0 || anIdx >= count ) { @throw [NSException exceptionWithName:NSRangeException reason:[NSString stringWithFormat:@"Attempt to retrieve objectAtIndex %d past end", anIdx] userInfo:nil]; return nil; } ptrBuffer = [buffer mutableBytes]; obj = ptrBuffer[anIdx]; if ( obj == [NSNull null] ) { obj = nil; } return obj; } - (void) insertObject:(id)anObject atIndex:(NSInteger)anIdx { if ( anObject == nil ) anObject = [NSNull null]; if ( anObject == nil ) { @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Attempt to insert nil objectAtIndex" userInfo:nil]; } if ( anIdx < 0 || anIdx > count ) { @throw [NSException exceptionWithName:NSRangeException reason:@"Attempt to insertObjectAtIndex past end" userInfo:nil]; } if ( count == BuffSize ) { [self ensureCapacity:count]; } if ( anIdx < count ) { for (int i = count; i > anIdx; i--) { ptrBuffer[i] = ptrBuffer[i-1]; } } ptrBuffer[anIdx] = [anObject retain]; count++; } - (void) removeObjectAtIndex:(NSInteger)idx; { id tmp; if (idx < 0 || idx >= count) { @throw [NSException exceptionWithName:NSRangeException reason:@"Attempt to insert removeObjectAtIndex past end" userInfo:nil]; } else if (count) { tmp = ptrBuffer[idx]; if ( tmp ) [tmp release]; for (int i = idx; i < count; i++) { ptrBuffer[i] = ptrBuffer[i+1]; } count--; } } - (void) removeLastObject { id tmp; if (count == 0) { @throw [NSException exceptionWithName:NSRangeException reason:@"Attempt to removeLastObject from 0" userInfo:nil]; } count--; tmp = ptrBuffer[count]; if ( tmp ) [tmp release]; ptrBuffer[count] = nil; } - (void)removeAllObjects { id tmp; if (count == 0) { @throw [NSException exceptionWithName:NSRangeException reason:@"Attempt to removeAllObjects from 0" userInfo:nil]; } int i; for ( i = 0; i < BuffSize; i++ ) { if (i < count) { tmp = ptrBuffer[i]; if ( tmp ) [tmp release]; } ptrBuffer[i] = nil; } count = 0; } - (void) replaceObjectAtIndex:(NSInteger)idx withObject:(id)obj { id tmp; if ( obj == nil ) { obj = [NSNull null]; } if ( idx < 0 || idx >= count ) { @throw [NSException exceptionWithName:NSRangeException reason:@"Attempt to replace object past end" userInfo:nil]; } if ( count ) { [obj retain]; tmp = ptrBuffer[idx]; if ( tmp ) [tmp release]; ptrBuffer[idx] = obj; } } - (NSInteger) count { return count; } - (void) setCount:(NSInteger)cnt { count = cnt; } - (NSArray *) allObjects { return [NSArray arrayWithObjects:ptrBuffer count:count]; } - (ArrayIterator *) objectEnumerator { return [ArrayIterator newIterator:[self allObjects]]; } // This is where all the magic happens. // You have two choices when implementing this method: // 1) Use the stack based array provided by stackbuf. If you do this, then you must respect the value of 'len'. // 2) Return your own array of objects. If you do this, return the full length of the array returned until you run out of objects, then return 0. For example, a linked-array implementation may return each array in order until you iterate through all arrays. // In either case, state->itemsPtr MUST be a valid array (non-nil). This sample takes approach #1, using stackbuf to store results. - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len { NSUInteger cnt = 0; // This is the initialization condition, so we'll do one-time setup here. // Ensure that you never set state->state back to 0, or use another method to detect initialization // (such as using one of the values of state->extra). if (state->state == 0) { // We are not tracking mutations, so we'll set state->mutationsPtr to point into one of our extra values, // since these values are not otherwise used by the protocol. // If your class was mutable, you may choose to use an internal variable that is updated when the class is mutated. // state->mutationsPtr MUST NOT be NULL. state->mutationsPtr = &state->extra[0]; } // Now we provide items, which we track with state->state, and determine if we have finished iterating. if (state->state < self.count) { // Set state->itemsPtr to the provided buffer. // Alternate implementations may set state->itemsPtr to an internal C array of objects. // state->itemsPtr MUST NOT be NULL. state->itemsPtr = stackbuf; // Fill in the stack array, either until we've provided all items from the list // or until we've provided as many items as the stack based buffer will hold. while((state->state < self.count) && (cnt < len)) { // For this sample, we generate the contents on the fly. // A real implementation would likely just be copying objects from internal storage. stackbuf[cnt++] = ptrBuffer[state->state++]; } // state->state = ((cnt < len)? cnt : len); } else { // We've already provided all our items, so we signal we are done by returning 0. cnt = 0; } return cnt; } - (NSString *) description { NSMutableString *str; NSInteger idx, cnt; id tmp; cnt = [self count]; str = [NSMutableString stringWithCapacity:30]; [str appendString:@"["]; for (idx = 0; idx < cnt; idx++ ) { tmp = [self objectAtIndex:idx]; [str appendString:((tmp == nil) ? @"nil" : [tmp description])]; } [str appendString:@"]"]; return str; } - (NSString *) toString { return [self description]; } - (void) ensureCapacity:(NSInteger) index { if ((index * sizeof(id)) >= [buffer length]) { NSInteger newSize = ([buffer length] / sizeof(id)) * 2; if (index > newSize) { newSize = index + 1; } BuffSize = newSize; [buffer setLength:(BuffSize * sizeof(id))]; ptrBuffer = [buffer mutableBytes]; } } @end