1// 2// AMutableArray.m 3// a_ST4 4// 5// Created by Alan Condit on 3/12/11. 6// Copyright 2011 Alan's MachineWorks. All rights reserved. 7// 8#import "AMutableArray.h" 9#import "ArrayIterator.h" 10 11#define BUFFSIZE 25 12 13@implementation AMutableArray 14 15@synthesize BuffSize; 16@synthesize buffer; 17@synthesize ptrBuffer; 18//@synthesize count; 19 20 21+ (id) newArray 22{ 23 return [[AMutableArray alloc] init]; 24} 25 26+ (id) arrayWithCapacity:(NSInteger)size 27{ 28 return [[AMutableArray alloc] initWithCapacity:size]; 29} 30 31- (id) init 32{ 33 self=[super init]; 34 if ( self != nil ) { 35 BuffSize = BUFFSIZE; 36 buffer = [[NSMutableData dataWithLength:(BuffSize * sizeof(id))] retain]; 37 ptrBuffer = (id *)[buffer mutableBytes]; 38 for( int idx = 0; idx < BuffSize; idx++ ) { 39 ptrBuffer[idx] = nil; 40 } 41 } 42 return self; 43} 44 45- (id) initWithCapacity:(NSInteger)len 46{ 47 self=[super init]; 48 if ( self != nil ) { 49 BuffSize = (len >= BUFFSIZE) ? len : BUFFSIZE; 50 buffer = [[NSMutableData dataWithLength:(BuffSize * sizeof(id))] retain]; 51 ptrBuffer = (id *)[buffer mutableBytes]; 52 for( int idx = 0; idx < BuffSize; idx++ ) { 53 ptrBuffer[idx] = nil; 54 } 55 } 56 return self; 57} 58 59- (void) dealloc 60{ 61#ifdef DEBUG_DEALLOC 62 NSLog( @"called dealloc in AMutableArray" ); 63#endif 64 if ( count ) [self removeAllObjects]; 65 if ( buffer ) [buffer release]; 66 [super dealloc]; 67} 68 69- (id) copyWithZone:(NSZone *)aZone 70{ 71 AMutableArray *copy; 72 73 copy = [[[self class] allocWithZone:aZone] init]; 74 if ( buffer ) { 75 copy.buffer = [buffer copyWithZone:aZone]; 76 } 77 copy.ptrBuffer = [copy.buffer mutableBytes]; 78 copy.count = count; 79 copy.BuffSize = BuffSize; 80 return copy; 81} 82 83- (void) addObject:(id)anObject 84{ 85 if ( anObject == nil ) anObject = [NSNull null]; 86 [anObject retain]; 87 [self ensureCapacity:count]; 88 ptrBuffer[count++] = anObject; 89} 90 91- (void) addObjectsFromArray:(NSArray *)otherArray 92{ 93 NSInteger cnt, i; 94 cnt = [otherArray count]; 95 [self ensureCapacity:count+cnt]; 96 for( i = 0; i < cnt; i++) { 97 [self addObject:[otherArray objectAtIndex:i]]; 98 } 99 return; 100} 101 102- (id) objectAtIndex:(NSInteger)anIdx 103{ 104 id obj; 105 if ( anIdx < 0 || anIdx >= count ) { 106 @throw [NSException exceptionWithName:NSRangeException 107 reason:[NSString stringWithFormat:@"Attempt to retrieve objectAtIndex %d past end", anIdx] 108 userInfo:nil]; 109 return nil; 110 } 111 ptrBuffer = [buffer mutableBytes]; 112 obj = ptrBuffer[anIdx]; 113 if ( obj == [NSNull null] ) { 114 obj = nil; 115 } 116 return obj; 117} 118 119- (void) insertObject:(id)anObject atIndex:(NSInteger)anIdx 120{ 121 if ( anObject == nil ) anObject = [NSNull null]; 122 if ( anObject == nil ) { 123 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Attempt to insert nil objectAtIndex" userInfo:nil]; 124 } 125 if ( anIdx < 0 || anIdx > count ) { 126 @throw [NSException exceptionWithName:NSRangeException reason:@"Attempt to insertObjectAtIndex past end" userInfo:nil]; 127 } 128 if ( count == BuffSize ) { 129 [self ensureCapacity:count]; 130 } 131 if ( anIdx < count ) { 132 for (int i = count; i > anIdx; i--) { 133 ptrBuffer[i] = ptrBuffer[i-1]; 134 } 135 } 136 ptrBuffer[anIdx] = [anObject retain]; 137 count++; 138} 139 140- (void) removeObjectAtIndex:(NSInteger)idx; 141{ 142 id tmp; 143 if (idx < 0 || idx >= count) { 144 @throw [NSException exceptionWithName:NSRangeException reason:@"Attempt to insert removeObjectAtIndex past end" userInfo:nil]; 145 } 146 else if (count) { 147 tmp = ptrBuffer[idx]; 148 if ( tmp ) [tmp release]; 149 for (int i = idx; i < count; i++) { 150 ptrBuffer[i] = ptrBuffer[i+1]; 151 } 152 count--; 153 } 154} 155 156- (void) removeLastObject 157{ 158 id tmp; 159 if (count == 0) { 160 @throw [NSException exceptionWithName:NSRangeException reason:@"Attempt to removeLastObject from 0" userInfo:nil]; 161 } 162 count--; 163 tmp = ptrBuffer[count]; 164 if ( tmp ) [tmp release]; 165 ptrBuffer[count] = nil; 166} 167 168- (void)removeAllObjects 169{ 170 id tmp; 171 if (count == 0) { 172 @throw [NSException exceptionWithName:NSRangeException reason:@"Attempt to removeAllObjects from 0" userInfo:nil]; 173 } 174 int i; 175 for ( i = 0; i < BuffSize; i++ ) { 176 if (i < count) { 177 tmp = ptrBuffer[i]; 178 if ( tmp ) [tmp release]; 179 } 180 ptrBuffer[i] = nil; 181 } 182 count = 0; 183} 184 185- (void) replaceObjectAtIndex:(NSInteger)idx withObject:(id)obj 186{ 187 id tmp; 188 if ( obj == nil ) { 189 obj = [NSNull null]; 190 } 191 if ( idx < 0 || idx >= count ) { 192 @throw [NSException exceptionWithName:NSRangeException reason:@"Attempt to replace object past end" userInfo:nil]; 193 } 194 if ( count ) { 195 [obj retain]; 196 tmp = ptrBuffer[idx]; 197 if ( tmp ) [tmp release]; 198 ptrBuffer[idx] = obj; 199 } 200} 201 202- (NSInteger) count 203{ 204 return count; 205} 206 207- (void) setCount:(NSInteger)cnt 208{ 209 count = cnt; 210} 211 212- (NSArray *) allObjects 213{ 214 return [NSArray arrayWithObjects:ptrBuffer count:count]; 215} 216 217- (ArrayIterator *) objectEnumerator 218{ 219 return [ArrayIterator newIterator:[self allObjects]]; 220} 221 222// This is where all the magic happens. 223// You have two choices when implementing this method: 224// 1) Use the stack based array provided by stackbuf. If you do this, then you must respect the value of 'len'. 225// 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. 226// In either case, state->itemsPtr MUST be a valid array (non-nil). This sample takes approach #1, using stackbuf to store results. 227- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len 228{ 229 NSUInteger cnt = 0; 230 // This is the initialization condition, so we'll do one-time setup here. 231 // Ensure that you never set state->state back to 0, or use another method to detect initialization 232 // (such as using one of the values of state->extra). 233 if (state->state == 0) { 234 // We are not tracking mutations, so we'll set state->mutationsPtr to point into one of our extra values, 235 // since these values are not otherwise used by the protocol. 236 // If your class was mutable, you may choose to use an internal variable that is updated when the class is mutated. 237 // state->mutationsPtr MUST NOT be NULL. 238 state->mutationsPtr = &state->extra[0]; 239 } 240 // Now we provide items, which we track with state->state, and determine if we have finished iterating. 241 if (state->state < self.count) { 242 // Set state->itemsPtr to the provided buffer. 243 // Alternate implementations may set state->itemsPtr to an internal C array of objects. 244 // state->itemsPtr MUST NOT be NULL. 245 state->itemsPtr = stackbuf; 246 // Fill in the stack array, either until we've provided all items from the list 247 // or until we've provided as many items as the stack based buffer will hold. 248 while((state->state < self.count) && (cnt < len)) { 249 // For this sample, we generate the contents on the fly. 250 // A real implementation would likely just be copying objects from internal storage. 251 stackbuf[cnt++] = ptrBuffer[state->state++]; 252 } 253 // state->state = ((cnt < len)? cnt : len); 254 } 255 else 256 { 257 // We've already provided all our items, so we signal we are done by returning 0. 258 cnt = 0; 259 } 260 return cnt; 261} 262 263- (NSString *) description 264{ 265 NSMutableString *str; 266 NSInteger idx, cnt; 267 cnt = [self count]; 268 str = [NSMutableString stringWithCapacity:30]; 269 [str appendString:@"["]; 270 for (idx = 0; idx < cnt; idx++ ) { 271 [str appendString:[[self objectAtIndex:idx] toString]]; 272 } 273 [str appendString:@"]"]; 274 return str; 275} 276 277- (NSString *) toString 278{ 279 return [self description]; 280} 281 282- (void) ensureCapacity:(NSInteger) index 283{ 284 if ((index * sizeof(id)) >= [buffer length]) 285 { 286 NSInteger newSize = ([buffer length] / sizeof(id)) * 2; 287 if (index > newSize) { 288 newSize = index + 1; 289 } 290 BuffSize = newSize; 291 [buffer setLength:(BuffSize * sizeof(id))]; 292 ptrBuffer = [buffer mutableBytes]; 293 } 294} 295 296@end 297