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