]> err.no Git - sope/blob - libFoundation/Foundation/NSConcreteArray.m
fixed some NGMail framework build issue
[sope] / libFoundation / Foundation / NSConcreteArray.m
1 /* 
2    NSConcreteArray.m
3
4    Copyright (C) 1995, 1996 Ovidiu Predescu and Mircea Oancea.
5    All rights reserved.
6
7    Author: Mircea Oancea <mircea@jupiter.elcom.pub.ro>
8
9    This file is part of libFoundation.
10
11    Permission to use, copy, modify, and distribute this software and its
12    documentation for any purpose and without fee is hereby granted, provided
13    that the above copyright notice appear in all copies and that both that
14    copyright notice and this permission notice appear in supporting
15    documentation.
16
17    We disclaim all warranties with regard to this software, including all
18    implied warranties of merchantability and fitness, in no event shall
19    we be liable for any special, indirect or consequential damages or any
20    damages whatsoever resulting from loss of use, data or profits, whether in
21    an action of contract, negligence or other tortious action, arising out of
22    or in connection with the use or performance of this software.
23 */
24
25 #include <stdio.h>
26
27 #include <Foundation/common.h>
28 #include <Foundation/NSArray.h>
29 #include <Foundation/NSString.h>
30 #include <Foundation/NSException.h>
31 #include <Foundation/exceptions/GeneralExceptions.h>
32
33 #include <extensions/objc-runtime.h>
34
35 #include "NSConcreteArray.h"
36
37 #define DO_NOT_USE_ZONE 1
38
39 static NSConcreteEmptyArray *sharedEmptyArray = nil;
40
41 /*
42  * NSConcreteArray class
43  */
44
45 @implementation NSConcreteArray
46
47 + (void)initialize
48 {
49     if (sharedEmptyArray == nil)
50         sharedEmptyArray = [[NSConcreteEmptyArray alloc] init];
51 }
52
53 - (id)init
54 {
55     RELEASE(self);
56     return RETAIN(sharedEmptyArray);
57 }
58
59 - (id)initWithObjects:(id *)objects count:(unsigned int)count
60 {
61     unsigned i;
62
63     if (count == 0) {
64         RELEASE(self);
65         return RETAIN(sharedEmptyArray);
66     }
67     else if (count == 1) {
68         RELEASE(self);
69         return [[NSConcreteSingleObjectArray alloc]
70                                              initWithObjects:objects count:1];
71     }
72     
73 #if DO_NOT_USE_ZONE
74     self->items      = calloc(count, sizeof(id));
75 #else
76     self->items      = NSZoneCalloc([self zone], sizeof(id), count);
77 #endif
78     self->itemsCount = count;
79     for (i = 0; i < count; i++) {
80         if (!(self->items[i] = RETAIN(objects[i]))) {
81             [[[InvalidArgumentException alloc] 
82                     initWithReason:@"Nil object to be added in array"] raise];
83         }
84     }
85     return self;
86 }
87
88 - (id)initWithArray:(NSArray *)anotherArray
89 {
90     unsigned i, count = [anotherArray count];
91
92     if (count == 0) {
93         RELEASE(self);
94         return RETAIN(sharedEmptyArray);
95     }
96     else if (count == 1) {
97         id item = [anotherArray objectAtIndex:0];
98         
99         RELEASE(self);
100         return [[NSConcreteSingleObjectArray alloc]
101                                              initWithObjects:&item count:1];
102     }
103     
104 #if DO_NOT_USE_ZONE
105     self->items = calloc(count, sizeof(id));
106 #else
107     self->items = NSZoneCalloc([self zone], sizeof(id), count);
108 #endif
109     self->itemsCount = count;
110     for (i = 0; i < itemsCount; i++)
111         self->items[i] = RETAIN([anotherArray objectAtIndex:i]);
112     return self;
113 }
114
115 - (void)dealloc
116 {
117     register signed int index; // Note: this limits the size of the array
118     
119     if ((index = self->itemsCount) > 0) {
120         /* 
121            Not using static cache because the array content is only similiar
122            inside a single collection, not across collections (arrays being
123            used in various contexts).
124            Hitrate of the cache is about 75% (including initial miss).
125         */
126         register Class LastClass  = Nil;
127         register IMP   objRelease = NULL;
128         
129         for (index--; index >= 0; index--) {
130             // TODO: cache RELEASE IMP
131 #if LIB_FOUNDATION_BOEHM_GC
132             self->items[index] = nil;
133 #else
134             register id obj = self->items[index];
135             
136             if (*(id *)obj != LastClass) {
137                 LastClass = *(id *)obj;
138                 objRelease = method_get_imp(object_is_instance(obj)
139                   ? class_get_instance_method(LastClass, @selector(release))
140                   : class_get_class_method(LastClass, @selector(release)));
141             }
142             
143             objRelease(obj, NULL /* dangerous? */);
144         }
145 #endif
146     }
147     
148 #if DO_NOT_USE_ZONE
149     if (self->items) free(self->items);
150 #else
151     lfFree(self->items);
152 #endif
153     [super dealloc];
154 }
155
156 /* Querying the Array */
157
158 - (id)objectAtIndex:(unsigned int)index
159 {
160     if (index >= self->itemsCount) {
161         [[[RangeException alloc] 
162                 initWithReason:@"objectAtIndex: in NSArray" 
163                 size:self->itemsCount index:index] raise];
164     }
165     return self->items[index];
166 }
167
168 - (unsigned int)count
169 {
170     return self->itemsCount;
171 }
172
173 - (unsigned int)indexOfObjectIdenticalTo:(id)anObject
174 {
175     unsigned i;
176     
177     for (i = 0; i < self->itemsCount; i++) {
178         if (items[i] == anObject)
179                 return i;
180     }
181     return NSNotFound;
182 }
183
184 @end /* NSConcreteArray */
185
186 /*
187  * NSConcreteEmptyArray class
188  */
189
190 @implementation NSConcreteEmptyArray
191
192 - (id)init
193 {
194     return self;
195 }
196 - (id)initWithObjects:(id *)objects count:(unsigned int)count
197 {
198     if (count > 0) {
199         RELEASE(self);
200         self = [[NSConcreteArray alloc] initWithObjects:objects count:count];
201     }
202     return self;
203 }
204 - (id)initWithArray:(NSArray *)anotherArray
205 {
206     if ([anotherArray count] > 0) {
207         id tmp;
208         tmp = [[NSConcreteArray alloc] initWithArray:anotherArray];
209         RELEASE(self);
210         return tmp;
211     }
212     return self;
213 }
214
215 - (id)objectAtIndex:(unsigned int)index
216 {
217     [[[RangeException alloc] 
218               initWithReason:@"objectAtIndex: in NSArray" size:0 index:index] raise];
219     return nil;
220 }
221 - (unsigned int)count
222 {
223     return 0;
224 }
225 - (unsigned int)indexOfObjectIdenticalTo:(id)anObject
226 {
227     return NSNotFound;
228 }
229
230 @end /* NSConcreteEmptyArray */
231
232 /*
233  * NSConcreteSingleObjectArray class
234  */
235
236 @implementation NSConcreteSingleObjectArray
237
238 + (void)initialize {
239     // force setup of shared emtpy
240     if (sharedEmptyArray == nil)
241         sharedEmptyArray = [[NSConcreteEmptyArray alloc] init];
242 }
243
244 - (id)init
245 {
246     RELEASE(self);
247     return RETAIN(sharedEmptyArray);
248 }
249 - (id)initWithObjects:(id *)objects count:(unsigned int)count
250 {
251     if (count == 0) {
252         RELEASE(self);
253         return RETAIN(sharedEmptyArray);
254     }
255     if (count > 1) {
256         RELEASE(self);
257         self = [[NSConcreteArray allocWithZone:[self zone]]
258                                  initWithObjects:objects count:count];
259     }
260     
261     if ((self->item = objects[0]) == nil) {
262         [[[InvalidArgumentException alloc] 
263                     initWithReason:@"Nil object to be added in array"] raise];
264     }
265     self->item = RETAIN(self->item);
266     return self;
267 }
268 - (id)initWithArray:(NSArray *)anotherArray
269 {
270     unsigned count = [anotherArray count];
271     
272     if (count == 0) {
273         RELEASE(self);
274         return RETAIN(sharedEmptyArray);
275     }
276     if (count > 1) {
277         RELEASE(self);
278         self = [[NSConcreteArray alloc] initWithArray:anotherArray];
279     }
280     
281     if ((self->item = [anotherArray objectAtIndex:0]) == nil) {
282         [[[InvalidArgumentException alloc] 
283                     initWithReason:@"Nil object to be added in array"] raise];
284     }
285     self->item = RETAIN(self->item);
286     return self;
287 }
288
289 - (void)dealloc
290 {
291     RELEASE(self->item);
292     [super dealloc];
293 }
294
295 // query array
296
297 - (id)objectAtIndex:(unsigned int)index
298 {
299     if (index > 0) {
300       [[[RangeException alloc] 
301                 initWithReason:@"objectAtIndex: in NSArray" size:1 index:index] raise];
302     }
303     return self->item;
304 }
305 - (unsigned int)count
306 {
307     return 1;
308 }
309 - (unsigned int)indexOfObjectIdenticalTo:(id)anObject
310 {
311     return (self->item == anObject) ? 0 : NSNotFound;
312 }
313
314 @end /* NSConcreteSingleObjectArray */
315
316 /*
317  * NSConcreteMutableArray class
318  */
319
320 @implementation NSConcreteMutableArray
321
322 + (void)initialize
323 {
324     static BOOL initialized = NO;
325     if(!initialized) {
326         initialized = YES;
327         class_add_behavior(self, [NSConcreteArray class]);
328     }
329 }
330
331 - (id)init
332 {
333 #if DO_NOT_USE_ZONE
334     self->items = calloc(1, sizeof(id));
335 #else
336     self->items = NSZoneCalloc([self zone], 1, sizeof(id));
337 #endif
338     self->maxItems   = 1;
339     self->itemsCount = 0;
340     return self;
341 }
342
343 - (id)initWithCapacity:(unsigned int)aNumItems
344 {
345     self->maxItems = aNumItems ? aNumItems : 16;
346 #if DO_NOT_USE_ZONE
347     self->items = calloc(self->maxItems, sizeof(id));
348 #else
349     self->items = NSZoneCalloc([self zone], sizeof(id), self->maxItems);
350 #endif
351     self->itemsCount = 0;
352     return self;
353 }
354
355 - (id)initWithObjects:(id *)objects count:(unsigned int)count
356 {
357     unsigned i;
358
359     if (count > 0) {
360         self->maxItems   = count;
361         self->itemsCount = count;
362     }
363     else {
364         self->maxItems   = 1;
365         self->itemsCount = 0;
366     }
367     
368 #if DO_NOT_USE_ZONE
369     self->items = calloc(self->maxItems, sizeof(id));
370 #else
371     self->items = NSZoneCalloc([self zone], sizeof(id), self->maxItems);
372 #endif
373
374     for (i = 0; i < count; i++) {
375         if ((self->items[i] = RETAIN(objects[i])) == nil) {
376             [[[InvalidArgumentException alloc] 
377                     initWithReason:@"Nil object to be added in array"] raise];
378         }
379     }
380     return self;
381 }
382
383 - (id)initWithArray:(NSArray *)anotherArray
384 {
385     unsigned i, count = [anotherArray count];
386
387     if (count > 0) {
388         self->maxItems   = count;
389         self->itemsCount = count;
390     }
391     else {
392         self->maxItems   = 1;
393         self->itemsCount = 0;
394     }
395     
396 #if DO_NOT_USE_ZONE
397     self->items = calloc(self->maxItems, sizeof(id));
398 #else
399     self->items = NSZoneCalloc([self zone], sizeof(id), self->maxItems);
400 #endif
401
402     for (i = 0; i < self->itemsCount; i++) {
403         self->items[i] = RETAIN([anotherArray objectAtIndex:i]);
404     }
405     return self;
406 }
407
408 - (void)dealloc
409 {
410     /* basically a copy of the NSConcreteArray -dealloc, might use a macro */
411     register signed int index;
412     
413     if ((index = self->itemsCount) > 0) {
414         /* 
415            Not using static cache because the array content is only similiar
416            inside a single collection, not across collections (arrays being
417            used in various contexts).
418            Hitrate of the cache is about 75% including initial miss.
419         */
420         register Class LastClass  = Nil;
421         register IMP   objRelease = NULL;
422         
423         for (index--; index >= 0; index--) {
424             // TODO: cache RELEASE IMP
425 #if LIB_FOUNDATION_BOEHM_GC
426             self->items[index] = nil;
427 #else
428             register id obj = self->items[index];
429             
430             if (*(id *)obj != LastClass) {
431                 LastClass = *(id *)obj;
432                 objRelease = method_get_imp(object_is_instance(obj)
433                   ? class_get_instance_method(LastClass, @selector(release))
434                   : class_get_class_method(LastClass, @selector(release)));
435             }
436             
437             objRelease(obj, NULL /* dangerous? */);
438         }
439 #endif
440     }
441
442 #if DO_NOT_USE_ZONE
443     if (self->items) free(self->items);
444 #else
445     lfFree(self->items);
446 #endif
447     [super dealloc];
448 }
449
450 /* Altering the Array */
451
452 - (void)insertObject:(id)anObject atIndex:(unsigned int)index
453 {
454     unsigned int i;
455     
456     if (anObject == nil) {
457         [[[InvalidArgumentException alloc] 
458                 initWithReason:@"Nil object to be added in array"] raise];
459     }
460     if (index > itemsCount) {
461         [[[RangeException alloc] 
462                 initWithReason:@"__insertObject:atIndex: in NSMutableArray" 
463                 size:itemsCount index:index] raise];
464     }
465
466     /* resize item array */
467     if (itemsCount == maxItems) {
468         if (maxItems != 0) {
469             maxItems += (maxItems >> 1) ? (maxItems >>1) : 1;
470         }
471         else {
472             maxItems = 1;
473         }
474         items = (id*)Realloc(items, sizeof(id) * maxItems);
475     }
476     
477     /* move items */
478     for(i = itemsCount; i > index; i--)
479         items[i] = items[i - 1];
480     
481     /* place new item */
482     items[index] = RETAIN(anObject);
483     itemsCount++;
484 }
485
486 - (void)replaceObjectAtIndex:(unsigned int)index  withObject:(id)anObject
487 {
488     if (anObject == nil) {
489         [[[InvalidArgumentException alloc] 
490                 initWithReason:@"Nil object to be added in array"] raise];
491     }
492     if (index >= self->itemsCount) {
493         [[[RangeException alloc] 
494                 initWithReason:@"NSConcreteMutableArray replaceObjectAtIndex" 
495                 size:self->itemsCount index:index] raise];
496     }
497     ASSIGN(self->items[index], anObject);
498 }
499
500 /* removing objects */
501
502 static inline void
503 _removeObjectsFrom(register NSConcreteMutableArray *self,
504                    register unsigned int index, register unsigned int count)
505 {
506     register unsigned int i;
507     
508     if ((index + count) > self->itemsCount) {
509         [[[RangeException alloc]
510                 initWithReason:@"removeObjectsFrom:count in NSMutableArray"
511                 size:self->itemsCount index:(index + count)] raise];
512     }
513     if (count == 0)
514         return;
515
516 #if !LIB_FOUNDATION_BOEHM_GC
517     // TODO: why autorelease?
518     for (i = index; i < index + count; i++)
519         [self->items[i] autorelease];
520 #endif
521
522     for (i = index + count; i < self->itemsCount; i++, index++)
523         self->items[index] = self->items[i];
524     for (; index < self->itemsCount; index++) {
525 #if DEBUG // better for crashing
526         self->items[index] = (id)0x3;
527 #else // more stable against bugs
528         self->items[index] = nil;
529 #endif
530     }
531
532     self->itemsCount -= count;
533 }
534
535 - (void)removeObjectsFrom:(unsigned int)index count:(unsigned int)count
536 {
537     _removeObjectsFrom(self, index, count);
538 }
539 - (void)removeObjectsInRange:(NSRange)aRange
540 {
541     _removeObjectsFrom(self, aRange.location, aRange.length);
542 }
543 - (void)removeAllObjects
544 {
545     _removeObjectsFrom(self, 0, self->itemsCount);
546 }
547 - (void)removeLastObject
548 {
549     if (self->itemsCount > 0) _removeObjectsFrom(self, (itemsCount - 1), 1);
550 }
551 - (void)removeObjectAtIndex:(unsigned int)index
552 {
553     _removeObjectsFrom(self, index, 1);
554 }
555
556 @end /* NSConcreteMutableArray */
557
558 /*
559   Local Variables:
560   c-basic-offset: 4
561   tab-width: 8
562   End:
563 */