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 id tmp; 95 cnt = [otherArray count]; 96 [self ensureCapacity:count+cnt]; 97 for( i = 0; i < cnt; i++) { 98 tmp = [otherArray objectAtIndex:i]; 99 [self addObject:tmp]; 100 } 101 return; 102} 103 104- (id) objectAtIndex:(NSInteger)anIdx 105{ 106 id obj; 107 if ( anIdx < 0 || anIdx >= count ) { 108 @throw [NSException exceptionWithName:NSRangeException 109 reason:[NSString stringWithFormat:@"Attempt to retrieve objectAtIndex %d past end", anIdx] 110 userInfo:nil]; 111 return nil; 112 } 113 ptrBuffer = [buffer mutableBytes]; 114 obj = ptrBuffer[anIdx]; 115 if ( obj == [NSNull null] ) { 116 obj = nil; 117 } 118 return obj; 119} 120 121- (void) insertObject:(id)anObject atIndex:(NSInteger)anIdx 122{ 123 if ( anObject == nil ) anObject = [NSNull null]; 124 if ( anObject == nil ) { 125 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Attempt to insert nil objectAtIndex" userInfo:nil]; 126 } 127 if ( anIdx < 0 || anIdx > count ) { 128 @throw [NSException exceptionWithName:NSRangeException reason:@"Attempt to insertObjectAtIndex past end" userInfo:nil]; 129 } 130 if ( count == BuffSize ) { 131 [self ensureCapacity:count]; 132 } 133 if ( anIdx < count ) { 134 for (int i = count; i > anIdx; i--) { 135 ptrBuffer[i] = ptrBuffer[i-1]; 136 } 137 } 138 ptrBuffer[anIdx] = [anObject retain]; 139 count++; 140} 141 142- (void) removeObjectAtIndex:(NSInteger)idx; 143{ 144 id tmp; 145 if (idx < 0 || idx >= count) { 146 @throw [NSException exceptionWithName:NSRangeException reason:@"Attempt to insert removeObjectAtIndex past end" userInfo:nil]; 147 } 148 else if (count) { 149 tmp = ptrBuffer[idx]; 150 if ( tmp ) [tmp release]; 151 for (int i = idx; i < count; i++) { 152 ptrBuffer[i] = ptrBuffer[i+1]; 153 } 154 count--; 155 } 156} 157 158- (void) removeLastObject 159{ 160 id tmp; 161 if (count == 0) { 162 @throw [NSException exceptionWithName:NSRangeException reason:@"Attempt to removeLastObject from 0" userInfo:nil]; 163 } 164 count--; 165 tmp = ptrBuffer[count]; 166 if ( tmp ) [tmp release]; 167 ptrBuffer[count] = nil; 168} 169 170- (void)removeAllObjects 171{ 172 id tmp; 173 if (count == 0) { 174 @throw [NSException exceptionWithName:NSRangeException reason:@"Attempt to removeAllObjects from 0" userInfo:nil]; 175 } 176 int i; 177 for ( i = 0; i < BuffSize; i++ ) { 178 if (i < count) { 179 tmp = ptrBuffer[i]; 180 if ( tmp ) [tmp release]; 181 } 182 ptrBuffer[i] = nil; 183 } 184 count = 0; 185} 186 187- (void) replaceObjectAtIndex:(NSInteger)idx withObject:(id)obj 188{ 189 id tmp; 190 if ( obj == nil ) { 191 obj = [NSNull null]; 192 } 193 if ( idx < 0 || idx >= count ) { 194 @throw [NSException exceptionWithName:NSRangeException reason:@"Attempt to replace object past end" userInfo:nil]; 195 } 196 if ( count ) { 197 [obj retain]; 198 tmp = ptrBuffer[idx]; 199 if ( tmp ) [tmp release]; 200 ptrBuffer[idx] = obj; 201 } 202} 203 204- (NSInteger) count 205{ 206 return count; 207} 208 209- (void) setCount:(NSInteger)cnt 210{ 211 count = cnt; 212} 213 214- (NSArray *) allObjects 215{ 216 return [NSArray arrayWithObjects:ptrBuffer count:count]; 217} 218 219- (ArrayIterator *) objectEnumerator 220{ 221 return [ArrayIterator newIterator:[self allObjects]]; 222} 223 224// This is where all the magic happens. 225// You have two choices when implementing this method: 226// 1) Use the stack based array provided by stackbuf. If you do this, then you must respect the value of 'len'. 227// 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. 228// In either case, state->itemsPtr MUST be a valid array (non-nil). This sample takes approach #1, using stackbuf to store results. 229- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len 230{ 231 NSUInteger cnt = 0; 232 // This is the initialization condition, so we'll do one-time setup here. 233 // Ensure that you never set state->state back to 0, or use another method to detect initialization 234 // (such as using one of the values of state->extra). 235 if (state->state == 0) { 236 // We are not tracking mutations, so we'll set state->mutationsPtr to point into one of our extra values, 237 // since these values are not otherwise used by the protocol. 238 // If your class was mutable, you may choose to use an internal variable that is updated when the class is mutated. 239 // state->mutationsPtr MUST NOT be NULL. 240 state->mutationsPtr = &state->extra[0]; 241 } 242 // Now we provide items, which we track with state->state, and determine if we have finished iterating. 243 if (state->state < self.count) { 244 // Set state->itemsPtr to the provided buffer. 245 // Alternate implementations may set state->itemsPtr to an internal C array of objects. 246 // state->itemsPtr MUST NOT be NULL. 247 state->itemsPtr = stackbuf; 248 // Fill in the stack array, either until we've provided all items from the list 249 // or until we've provided as many items as the stack based buffer will hold. 250 while((state->state < self.count) && (cnt < len)) { 251 // For this sample, we generate the contents on the fly. 252 // A real implementation would likely just be copying objects from internal storage. 253 stackbuf[cnt++] = ptrBuffer[state->state++]; 254 } 255 // state->state = ((cnt < len)? cnt : len); 256 } 257 else 258 { 259 // We've already provided all our items, so we signal we are done by returning 0. 260 cnt = 0; 261 } 262 return cnt; 263} 264 265- (NSString *) description 266{ 267 NSMutableString *str; 268 NSInteger idx, cnt; 269 id tmp; 270 cnt = [self count]; 271 str = [NSMutableString stringWithCapacity:30]; 272 [str appendString:@"["]; 273 for (idx = 0; idx < cnt; idx++ ) { 274 tmp = [self objectAtIndex:idx]; 275 [str appendString:((tmp == nil) ? @"nil" : [tmp description])]; 276 } 277 [str appendString:@"]"]; 278 return str; 279} 280 281- (NSString *) toString 282{ 283 return [self description]; 284} 285 286- (void) ensureCapacity:(NSInteger) index 287{ 288 if ((index * sizeof(id)) >= [buffer length]) 289 { 290 NSInteger newSize = ([buffer length] / sizeof(id)) * 2; 291 if (index > newSize) { 292 newSize = index + 1; 293 } 294 BuffSize = newSize; 295 [buffer setLength:(BuffSize * sizeof(id))]; 296 ptrBuffer = [buffer mutableBytes]; 297 } 298} 299 300@end 301