1/** NSArray - Array object to hold other objects.
2   Copyright (C) 1995-2015 Free Software Foundation, Inc.
3
4   Written by:  Andrew Kachites McCallum <mccallum@gnu.ai.mit.edu>
5   From skeleton by:  Adam Fedor <fedor@boulder.colorado.edu>
6   Created: March 1995
7
8   Rewrite by: Richard Frith-Macdonald <richard@brainstorm.co.uk>
9   January 1998 - new methods and changes as documented for Rhapsody plus
10   changes of array indices to type unsigned, plus major efficiency hacks.
11
12   This file is part of the GNUstep Base Library.
13
14   This library is free software; you can redistribute it and/or
15   modify it under the terms of the GNU Lesser General Public
16   License as published by the Free Software Foundation; either
17   version 2 of the License, or (at your option) any later version.
18
19   This library is distributed in the hope that it will be useful,
20   but WITHOUT ANY WARRANTY; without even the implied warranty of
21   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22   Lesser General Public License for more details.
23
24   You should have received a copy of the GNU Lesser General Public
25   License along with this library; if not, write to the Free
26   Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
27   Boston, MA 02110 USA.
28
29   <title>NSArray class reference</title>
30   $Date$ $Revision$
31   */
32
33#import "common.h"
34#import "Foundation/NSArray.h"
35#import "Foundation/NSEnumerator.h"
36#import "Foundation/NSCoder.h"
37#import "Foundation/NSData.h"
38#import "Foundation/NSRange.h"
39#import "Foundation/NSException.h"
40#import "Foundation/NSAutoreleasePool.h"
41#import "Foundation/NSThread.h"
42#import "Foundation/NSMapTable.h"
43#import "Foundation/NSLock.h"
44#import "Foundation/NSValue.h"
45#import "Foundation/NSNull.h"
46#import "Foundation/NSKeyValueCoding.h"
47#import "Foundation/NSSet.h"
48#import "Foundation/NSUserDefaults.h"
49#import "Foundation/NSIndexSet.h"
50// For private method _decodeArrayOfObjectsForKey:
51#import "Foundation/NSKeyedArchiver.h"
52#import "GSPrivate.h"
53#import "GSPThread.h"
54#import "GSFastEnumeration.h"
55#import "GSDispatch.h"
56#import "GSSorting.h"
57
58static BOOL GSMacOSXCompatiblePropertyLists(void)
59{
60  if (GSPrivateDefaultsFlag(NSWriteOldStylePropertyLists) == YES)
61    return NO;
62  return GSPrivateDefaultsFlag(GSMacOSXCompatible);
63}
64
65extern void     GSPropertyListMake(id,NSDictionary*,BOOL,BOOL,unsigned,id*);
66
67@interface NSArrayEnumerator : NSEnumerator
68{
69  NSArray	*array;
70  NSUInteger	pos;
71  IMP		get;
72  NSUInteger	(*cnt)(NSArray*, SEL);
73}
74- (id) initWithArray: (NSArray*)anArray;
75@end
76@interface NSArrayEnumeratorReverse : NSArrayEnumerator
77@end
78
79
80
81static Class NSArrayClass;
82static Class GSArrayClass;
83static Class NSMutableArrayClass;
84static Class GSMutableArrayClass;
85static Class GSPlaceholderArrayClass;
86
87static GSPlaceholderArray	*defaultPlaceholderArray;
88static NSMapTable		*placeholderMap;
89static pthread_mutex_t          placeholderLock = PTHREAD_MUTEX_INITIALIZER;
90
91
92/**
93 * A simple, low overhead, ordered container for objects.  All the objects
94 * in the container are retained by it.  The container may not contain nil
95 * (though it may contain [NSNull+null]).
96 */
97@implementation NSArray
98
99static SEL	addSel;
100static SEL	appSel;
101static SEL	countSel;
102static SEL	eqSel;
103static SEL	oaiSel;
104static SEL	remSel;
105static SEL	rlSel;
106
107+ (void) atExit
108{
109  DESTROY(defaultPlaceholderArray);
110  DESTROY(placeholderMap);
111}
112
113+ (void) initialize
114{
115  if (self == [NSArray class])
116    {
117      [self setVersion: 1];
118
119      addSel = @selector(addObject:);
120      appSel = @selector(appendString:);
121      countSel = @selector(count);
122      eqSel = @selector(isEqual:);
123      oaiSel = @selector(objectAtIndex:);
124      remSel = @selector(removeObjectAtIndex:);
125      rlSel = @selector(removeLastObject);
126
127      NSArrayClass = [NSArray class];
128      NSMutableArrayClass = [NSMutableArray class];
129      GSArrayClass = [GSArray class];
130      GSMutableArrayClass = [GSMutableArray class];
131      GSPlaceholderArrayClass = [GSPlaceholderArray class];
132
133      /*
134       * Set up infrastructure for placeholder arrays.
135       */
136      defaultPlaceholderArray = (GSPlaceholderArray*)
137	NSAllocateObject(GSPlaceholderArrayClass, 0, NSDefaultMallocZone());
138      placeholderMap = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
139	NSNonRetainedObjectMapValueCallBacks, 0);
140      [self registerAtExit];
141    }
142}
143
144+ (id) allocWithZone: (NSZone*)z
145{
146  if (self == NSArrayClass)
147    {
148      /*
149       * For a constant array, we return a placeholder object that can
150       * be converted to a real object when its initialisation method
151       * is called.
152       */
153      if (z == NSDefaultMallocZone() || z == 0)
154	{
155	  /*
156	   * As a special case, we can return a placeholder for an array
157	   * in the default malloc zone extremely efficiently.
158	   */
159	  return defaultPlaceholderArray;
160	}
161      else
162	{
163	  id	obj;
164
165	  /*
166	   * For anything other than the default zone, we need to
167	   * locate the correct placeholder in the (lock protected)
168	   * table of placeholders.
169	   */
170	  (void)pthread_mutex_lock(&placeholderLock);
171	  obj = (id)NSMapGet(placeholderMap, (void*)z);
172	  if (obj == nil)
173	    {
174	      /*
175	       * There is no placeholder object for this zone, so we
176	       * create a new one and use that.
177	       */
178	      obj = (id)NSAllocateObject(GSPlaceholderArrayClass, 0, z);
179	      NSMapInsert(placeholderMap, (void*)z, (void*)obj);
180	    }
181	  (void)pthread_mutex_unlock(&placeholderLock);
182	  return obj;
183	}
184    }
185  else
186    {
187      return NSAllocateObject(self, 0, z);
188    }
189}
190
191/**
192 * Returns an empty autoreleased array.
193 */
194+ (id) array
195{
196  id	o;
197
198  o = [self allocWithZone: NSDefaultMallocZone()];
199  o = [o initWithObjects: (id*)0 count: 0];
200  return AUTORELEASE(o);
201}
202
203/**
204 * Returns a new autoreleased NSArray instance containing all the objects from
205 * array, in the same order as the original.
206 */
207+ (id) arrayWithArray: (NSArray*)array
208{
209  id	o;
210
211  o = [self allocWithZone: NSDefaultMallocZone()];
212  o = [o initWithArray: array];
213  return AUTORELEASE(o);
214}
215
216/**
217 * Returns an autoreleased array based upon the file.  The new array is
218 * created using [NSObject+allocWithZone:] and initialised using the
219 * [NSArray-initWithContentsOfFile:] method. See the documentation for those
220 * methods for more detail.
221 */
222+ (id) arrayWithContentsOfFile: (NSString*)file
223{
224  id	o;
225
226  o = [self allocWithZone: NSDefaultMallocZone()];
227  o = [o initWithContentsOfFile: file];
228  return AUTORELEASE(o);
229}
230
231/**
232 * Returns an autoreleased array from the contents of aURL.  The new array is
233 * created using [NSObject+allocWithZone:] and initialised using the
234 * -initWithContentsOfURL: method. See the documentation for those
235 * methods for more detail.
236 */
237+ (id) arrayWithContentsOfURL: (NSURL*)aURL
238{
239  id	o;
240
241  o = [self allocWithZone: NSDefaultMallocZone()];
242  o = [o initWithContentsOfURL: aURL];
243  return AUTORELEASE(o);
244}
245
246/**
247 * Returns an autoreleased array containing anObject.
248 */
249+ (id) arrayWithObject: (id)anObject
250{
251  id	o;
252
253  o = [self allocWithZone: NSDefaultMallocZone()];
254  o = [o initWithObjects: &anObject count: 1];
255  return AUTORELEASE(o);
256}
257
258/**
259 * Returns an autoreleased array containing the list
260 * of objects, preserving order.
261 */
262+ (id) arrayWithObjects: firstObject, ...
263{
264  id	a = [self allocWithZone: NSDefaultMallocZone()];
265
266  GS_USEIDLIST(firstObject,
267    a = [a initWithObjects: __objects count: __count]);
268  return AUTORELEASE(a);
269}
270
271/**
272 * Returns an autoreleased array containing the specified
273 * objects, preserving order.
274 */
275+ (id) arrayWithObjects: (const id[])objects count: (NSUInteger)count
276{
277  return AUTORELEASE([[self allocWithZone: NSDefaultMallocZone()]
278    initWithObjects: objects count: count]);
279}
280
281/**
282 * Returns an autoreleased array formed from the contents of
283 * the receiver and adding anObject as the last item.
284 */
285- (NSArray*) arrayByAddingObject: (id)anObject
286{
287  id na;
288  NSUInteger	c = [self count];
289
290  if (anObject == nil)
291    [NSException raise: NSInvalidArgumentException
292		format: @"Attempt to add nil to an array"];
293  if (c == 0)
294    {
295      na = [[GSArrayClass allocWithZone: NSDefaultMallocZone()]
296	initWithObjects: &anObject count: 1];
297    }
298  else
299    {
300      GS_BEGINIDBUF(objects, c+1);
301
302      [self getObjects: objects];
303      objects[c] = anObject;
304      na = [[GSArrayClass allocWithZone: NSDefaultMallocZone()]
305	initWithObjects: objects count: c+1];
306
307      GS_ENDIDBUF();
308    }
309  return AUTORELEASE(na);
310}
311
312/**
313 * Returns a new array which is the concatenation of self and
314 * otherArray (in this precise order).
315 */
316- (NSArray*) arrayByAddingObjectsFromArray: (NSArray*)anotherArray
317{
318  id		na;
319  NSUInteger	c;
320  NSUInteger	l;
321  NSUInteger	e;
322
323  c = [self count];
324  l = [anotherArray count];
325  e = c + l;
326
327  {
328    GS_BEGINIDBUF(objects, e);
329
330    [self getObjects: objects];
331    if ([anotherArray isProxy])
332      {
333	NSUInteger	i = c;
334	NSUInteger	j = 0;
335
336	while (i < e)
337	  {
338	    objects[i++] = [anotherArray objectAtIndex: j++];
339	  }
340      }
341    else
342      {
343        [anotherArray getObjects: &objects[c]];
344      }
345    na = [NSArrayClass arrayWithObjects: objects count: e];
346
347    GS_ENDIDBUF();
348  }
349
350  return na;
351}
352
353/**
354 * Returns the abstract class ... arrays are coded as abstract arrays.
355 */
356- (Class) classForCoder
357{
358  return NSArrayClass;
359}
360
361/**
362 * Returns YES if anObject belongs to self. No otherwise.<br />
363 * The [NSObject-isEqual:] method of anObject is used to test for equality.
364 */
365- (BOOL) containsObject: (id)anObject
366{
367  return ([self indexOfObject: anObject] != NSNotFound);
368}
369
370/**
371 * Returns a new copy of the receiver.<br />
372 * The default abstract implementation of a copy is to use the
373 * -initWithArray:copyItems: method with the flag set to YES.<br />
374 * Immutable subclasses generally simply retain and return the receiver.
375 */
376- (id) copyWithZone: (NSZone*)zone
377{
378  NSArray	*copy = [NSArrayClass allocWithZone: zone];
379
380  return [copy initWithArray: self copyItems: YES];
381}
382
383- (NSUInteger) count
384{
385  [self subclassResponsibility: _cmd];
386  return 0;
387}
388
389- (NSUInteger) countByEnumeratingWithState: (NSFastEnumerationState*)state
390				   objects: (__unsafe_unretained id[])stackbuf
391				     count: (NSUInteger)len
392{
393  NSInteger count;
394
395  /* In a mutable subclass, the mutationsPtr should be set to point to a
396   * value (unsigned long) which will be changed (incremented) whenever
397   * the container is mutated (content added, removed, re-ordered).
398   * This is cached in the caller at the start and compared at each
399   * iteration.   If it changes during the iteration then
400   * objc_enumerationMutation() will be called, throwing an exception.
401   * The abstract base class implementation points to a fixed value
402   * (the enumeration state pointer should exist and be unchanged for as
403   * long as the enumeration process runs), which is fine for enumerating
404   * an immutable array.
405   */
406  state->mutationsPtr = (unsigned long *)&state->mutationsPtr;
407  count = MIN(len, [self count] - state->state);
408  /* If a mutation has occurred then it's possible that we are being asked to
409   * get objects from after the end of the array.  Don't pass negative values
410   * to memcpy.
411   */
412  if (count > 0)
413    {
414      IMP	imp = [self methodForSelector: @selector(objectAtIndex:)];
415      int	p = state->state;
416      int	i;
417
418      for (i = 0; i < count; i++, p++)
419	{
420	  stackbuf[i] = (*imp)(self, @selector(objectAtIndex:), p);
421	}
422      state->state += count;
423    }
424  else
425    {
426      count = 0;
427    }
428  state->itemsPtr = stackbuf;
429  return count;
430}
431
432/**
433 * Encodes the receiver for storing to archive or sending over an
434 * [NSConnection].
435 */
436- (void) encodeWithCoder: (NSCoder*)aCoder
437{
438  NSUInteger	count = [self count];
439
440  if ([aCoder allowsKeyedCoding])
441    {
442      /* HACK ... MacOS-X seems to code differently if the coder is an
443       * actual instance of NSKeyedArchiver
444       */
445      if ([aCoder class] == [NSKeyedArchiver class])
446	{
447	  [(NSKeyedArchiver*)aCoder _encodeArrayOfObjects: self
448						   forKey: @"NS.objects"];
449	}
450      else
451	{
452	  NSUInteger	i;
453
454	  for (i = 0; i < count; i++)
455	    {
456	      NSString	*key;
457
458	      key = [NSString stringWithFormat: @"NS.object.%lu", (unsigned long)i];
459	      [(NSKeyedArchiver*)aCoder encodeObject: [self objectAtIndex: i]
460					      forKey: key];
461	    }
462	}
463    }
464  else
465    {
466      unsigned  items = (unsigned)count;
467
468      [aCoder encodeValueOfObjCType: @encode(unsigned)
469				 at: &items];
470      if (count > 0)
471	{
472	  GS_BEGINIDBUF(a, count);
473
474	  [self getObjects: a];
475	  [aCoder encodeArrayOfObjCType: @encode(id)
476				  count: count
477				     at: a];
478	  GS_ENDIDBUF();
479	}
480    }
481}
482
483/**
484 * Copies the objects from the receiver to aBuffer, which must be
485 * an area of memory large enough to hold them.
486 */
487- (void) getObjects: (__unsafe_unretained id[])aBuffer
488{
489  NSUInteger i, c = [self count];
490  IMP	get = [self methodForSelector: oaiSel];
491
492  for (i = 0; i < c; i++)
493    aBuffer[i] = (*get)(self, oaiSel, i);
494}
495
496/**
497 * Copies the objects from the range aRange of the receiver to aBuffer,
498 * which must be an area of memory large enough to hold them.
499 */
500- (void) getObjects: (__unsafe_unretained id[])aBuffer range: (NSRange)aRange
501{
502  NSUInteger i, j = 0, c = [self count], e = aRange.location + aRange.length;
503  IMP	get = [self methodForSelector: oaiSel];
504
505  GS_RANGE_CHECK(aRange, c);
506
507  for (i = aRange.location; i < e; i++)
508    aBuffer[j++] = (*get)(self, oaiSel, i);
509}
510
511/**
512 * Returns the same value as -count
513 */
514- (NSUInteger) hash
515{
516  return [self count];
517}
518
519/**
520 * Returns the index of the specified object in the receiver, or
521 * NSNotFound if the object is not present.
522 */
523- (NSUInteger) indexOfObjectIdenticalTo: (id)anObject
524{
525  NSUInteger c = [self count];
526
527  if (c > 0)
528    {
529      IMP	get = [self methodForSelector: oaiSel];
530      NSUInteger	i;
531
532      for (i = 0; i < c; i++)
533	if (anObject == (*get)(self, oaiSel, i))
534	  return i;
535    }
536  return NSNotFound;
537}
538
539/**
540 * Returns the index of the specified object in the range of the receiver,
541 * or NSNotFound if the object is not present.
542 */
543- (NSUInteger) indexOfObjectIdenticalTo: anObject inRange: (NSRange)aRange
544{
545  NSUInteger i, e = aRange.location + aRange.length, c = [self count];
546  IMP	get = [self methodForSelector: oaiSel];
547
548  GS_RANGE_CHECK(aRange, c);
549
550  for (i = aRange.location; i < e; i++)
551    if (anObject == (*get)(self, oaiSel, i))
552      return i;
553  return NSNotFound;
554}
555
556/**
557 * Returns the index of the first object found in the receiver
558 * which is equal to anObject (using anObject's [NSObject-isEqual:] method).
559 * Returns NSNotFound on failure.
560 */
561- (NSUInteger) indexOfObject: (id)anObject
562{
563  NSUInteger	c = [self count];
564
565  if (c > 0 && anObject != nil)
566    {
567      NSUInteger	i;
568      IMP	get = [self methodForSelector: oaiSel];
569      BOOL	(*eq)(id, SEL, id)
570	= (BOOL (*)(id, SEL, id))[anObject methodForSelector: eqSel];
571
572      for (i = 0; i < c; i++)
573	if ((*eq)(anObject, eqSel, (*get)(self, oaiSel, i)) == YES)
574	  return i;
575    }
576  return NSNotFound;
577}
578
579/**
580 * Returns the index of the first object found in aRange of receiver
581 * which is equal to anObject (using anObject's [NSObject-isEqual:] method).
582 * Returns NSNotFound on failure.
583 */
584- (NSUInteger) indexOfObject: (id)anObject inRange: (NSRange)aRange
585{
586  NSUInteger i, e = aRange.location + aRange.length, c = [self count];
587  IMP	get = [self methodForSelector: oaiSel];
588  BOOL	(*eq)(id, SEL, id)
589    = (BOOL (*)(id, SEL, id))[anObject methodForSelector: eqSel];
590
591  GS_RANGE_CHECK(aRange, c);
592
593  for (i = aRange.location; i < e; i++)
594    {
595      if ((*eq)(anObject, eqSel, (*get)(self, oaiSel, i)) == YES)
596        return i;
597    }
598  return NSNotFound;
599}
600
601/**
602 * <p>In MacOS-X class clusters do not have designated initialisers,
603 * and there is a general rule that -init is treated as the designated
604 * initialiser of the class cluster, but that other intitialisers
605 * may not work s expected an would need to be individually overridden
606 * in any subclass.
607 * </p>
608 * <p>GNUstep tries to make it easier to subclass a class cluster,
609 * by making class clusters follow the same convention as normal
610 * classes, so the designated initialiser is the <em>richest</em>
611 * initialiser.  This means that all other initialisers call the
612 * documented designated initialiser (which calls -init only for
613 * MacOS-X compatibility), and anyone writing a subclass only needs
614 * to override that one initialiser in order to have all the other
615 * ones work.
616 * </p>
617 * <p>For MacOS-X compatibility, you may also need to override various
618 * other initialisers.  Exactly which ones, you will need to determine
619 * by trial on a MacOS-X system ... and may vary between releases of
620 * MacOS-X.  So to be safe, on MacOS-X you probably need to re-implement
621 * <em>all</em> the class cluster initialisers you might use in conjunction
622 * with your subclass.
623 * </p>
624 */
625- (id) init
626{
627  self = [super init];
628  return self;
629}
630
631/**
632 * Initialize the receiver with the contents of array.
633 * The order of array is preserved.<br />
634 * If shouldCopy is YES then the objects are copied
635 * rather than simply retained.<br />
636 * Invokes -initWithObjects:count:
637 */
638- (id) initWithArray: (NSArray*)array copyItems: (BOOL)shouldCopy
639{
640  NSUInteger	c = [array count];
641  GS_BEGINIDBUF(objects, c);
642
643  if ([array isProxy])
644    {
645      NSUInteger	i;
646
647      for (i = 0; i < c; i++)
648	{
649	  objects[i] = [array objectAtIndex: i];
650	}
651    }
652  else
653    {
654      [array getObjects: objects];
655    }
656  if (shouldCopy == YES)
657    {
658      NSUInteger	i;
659
660      for (i = 0; i < c; i++)
661	{
662	  objects[i] = [objects[i] copy];
663	}
664      self = [self initWithObjects: objects count: c];
665      while (i > 0)
666	{
667	  [objects[--i] release];
668	}
669    }
670  else
671    {
672      self = [self initWithObjects: objects count: c];
673    }
674  GS_ENDIDBUF();
675  return self;
676}
677
678/**
679 * Initialize the receiver with the contents of array.
680 * The order of array is preserved.<br />
681 * Invokes -initWithObjects:count:
682 */
683- (id) initWithArray: (NSArray*)array
684{
685  NSUInteger	c = [array count];
686  GS_BEGINIDBUF(objects, c);
687
688  if ([array isProxy])
689    {
690      NSUInteger	i;
691
692      for (i = 0; i < c; i++)
693	{
694	  objects[i] = [array objectAtIndex: i];
695	}
696    }
697  else
698    {
699      [array getObjects: objects];
700    }
701  self = [self initWithObjects: objects count: c];
702  GS_ENDIDBUF();
703  return self;
704}
705
706/**
707 * Initialize the array by decoding from an archive.<br />
708 * Invokes -initWithObjects:count:
709 */
710- (id) initWithCoder: (NSCoder*)aCoder
711{
712  if ([aCoder allowsKeyedCoding])
713    {
714      id	array;
715
716      array = [(NSKeyedUnarchiver*)aCoder _decodeArrayOfObjectsForKey:
717						@"NS.objects"];
718      if (array == nil)
719	{
720	  NSUInteger	i = 0;
721	  NSString	*key;
722	  id		val;
723
724	  array = [NSMutableArray arrayWithCapacity: 2];
725	  key = [NSString stringWithFormat: @"NS.object.%lu", (unsigned long)i];
726	  val = [(NSKeyedUnarchiver*)aCoder decodeObjectForKey: key];
727
728	  while (val != nil)
729	    {
730	      [array addObject: val];
731	      i++;
732	      key = [NSString stringWithFormat: @"NS.object.%lu", (unsigned long)i];
733	      val = [(NSKeyedUnarchiver*)aCoder decodeObjectForKey: key];
734	    }
735	}
736
737      self = [self initWithArray: array];
738    }
739  else
740    {
741      unsigned    items;
742
743      [aCoder decodeValueOfObjCType: @encode(unsigned)
744	                         at: &items];
745      if (items > 0)
746        {
747	  GS_BEGINIDBUF(contents, items);
748
749	  [aCoder decodeArrayOfObjCType: @encode(id)
750		  count: items
751		  at: contents];
752	  self = [self initWithObjects: contents count: items];
753	  while (items-- > 0)
754	    {
755	      [contents[items] release];
756	    }
757	  GS_ENDIDBUF();
758	}
759      else
760        {
761	  self = [self initWithObjects: 0 count: 0];
762	}
763    }
764  return self;
765}
766
767/**
768 * <p>Initialises the array with the contents of the specified file,
769 * which must contain an array in property-list format.
770 * </p>
771 * <p>In GNUstep, the property-list format may be either the OpenStep
772 * format (ASCII data), or the MacOS-X format (UTF-8 XML data) ... this
773 * method will recognise which it is.
774 * </p>
775 * <p>If there is a failure to load the file for any reason, the receiver
776 * will be released, the method will return nil, and a warning may be logged.
777 * </p>
778 * <p>Works by invoking [NSString-initWithContentsOfFile:] and
779 * [NSString-propertyList] then checking that the result is an array.
780 * </p>
781 */
782- (id) initWithContentsOfFile: (NSString*)file
783{
784  NSString 	*myString;
785
786  myString = [[NSString allocWithZone: NSDefaultMallocZone()]
787    initWithContentsOfFile: file];
788  if (myString == nil)
789    {
790      DESTROY(self);
791    }
792  else
793    {
794      id result;
795
796      NS_DURING
797	{
798	  result = [myString propertyList];
799	}
800      NS_HANDLER
801	{
802          result = nil;
803	}
804      NS_ENDHANDLER
805      RELEASE(myString);
806      if ([result isKindOfClass: NSArrayClass])
807	{
808	  //self = [self initWithArray: result];
809	  /* OSX appears to always return a mutable array rather than
810	   * the class of the receiver.
811	   */
812	  RELEASE(self);
813	  self = RETAIN(result);
814	}
815      else
816	{
817	  NSWarnMLog(@"Contents of file '%@' does not contain an array", file);
818	  DESTROY(self);
819	}
820    }
821  return self;
822}
823
824/**
825 * <p>Initialises the array with the contents of the specified URL,
826 * which must contain an array in property-list format.
827 * </p>
828 * <p>In GNUstep, the property-list format may be either the OpenStep
829 * format (ASCII data), or the MacOS-X format (UTF8 XML data) ... this
830 * method will recognise which it is.
831 * </p>
832 * <p>If there is a failure to load the URL for any reason, the receiver
833 * will be released, the method will return nil, and a warning may be logged.
834 * </p>
835 * <p>Works by invoking [NSString-initWithContentsOfURL:] and
836 * [NSString-propertyList] then checking that the result is an array.
837 * </p>
838 */
839- (id) initWithContentsOfURL: (NSURL*)aURL
840{
841  NSString 	*myString;
842
843  myString = [[NSString allocWithZone: NSDefaultMallocZone()]
844    initWithContentsOfURL: aURL];
845  if (myString == nil)
846    {
847      DESTROY(self);
848    }
849  else
850    {
851      id result;
852
853      NS_DURING
854	{
855	  result = [myString propertyList];
856	}
857      NS_HANDLER
858	{
859          result = nil;
860	}
861      NS_ENDHANDLER
862      RELEASE(myString);
863      if ([result isKindOfClass: NSArrayClass])
864	{
865	  self = [self initWithArray: result];
866	}
867      else
868	{
869	  NSWarnMLog(@"Contents of URL '%@' does not contain an array", aURL);
870	  DESTROY(self);
871	}
872    }
873  return self;
874}
875
876- (id) initWithObjects: (const id[])objects count: (NSUInteger)count
877{
878  self = [self init];
879  return self;
880}
881
882/**
883 * Initialize the array the list of objects.
884 * <br />May change the value of self before returning it.
885 */
886- (id) initWithObjects: firstObject, ...
887{
888  GS_USEIDLIST(firstObject,
889    self = [self initWithObjects: __objects count: __count]);
890  return self;
891}
892
893/**
894 * Returns an NSMutableArray instance containing the same objects as
895 * the receiver.<br />
896 * The default implementation does this by calling the
897 * -initWithArray:copyItems: method on a newly created object,
898 * and passing it NO to tell it just to retain the items.
899 */
900- (id) mutableCopyWithZone: (NSZone*)zone
901{
902  NSMutableArray	*copy = [NSMutableArrayClass allocWithZone: zone];
903
904  return [copy initWithArray: self copyItems: NO];
905}
906
907- (id) objectAtIndex: (NSUInteger)index
908{
909  [self subclassResponsibility: _cmd];
910  return nil;
911}
912
913- (id) objectAtIndexedSubscript: (NSUInteger)anIndex
914{
915  return [self objectAtIndex: anIndex];
916}
917
918- (NSArray *) objectsAtIndexes: (NSIndexSet *)indexes
919{
920  //FIXME: probably slow!
921  NSMutableArray *group = [NSMutableArray arrayWithCapacity: [indexes count]];
922
923  NSUInteger i = [indexes firstIndex];
924  while (i != NSNotFound)
925    {
926      [group addObject: [self objectAtIndex: i]];
927      i = [indexes indexGreaterThanIndex: i];
928    }
929
930  return GS_IMMUTABLE(group);
931}
932
933- (BOOL) isEqual: (id)anObject
934{
935  if (self == anObject)
936    return YES;
937  if ([anObject isKindOfClass: NSArrayClass])
938    return [self isEqualToArray: anObject];
939  return NO;
940}
941
942
943/**
944 * Returns YES if the receiver is equal to otherArray, NO otherwise.
945 */
946- (BOOL) isEqualToArray: (NSArray*)otherArray
947{
948  NSUInteger i, c;
949
950  if (self == (id)otherArray)
951    return YES;
952  c = [self count];
953  if (c != [otherArray count])
954    return NO;
955  if (c > 0)
956    {
957      IMP	get0 = [self methodForSelector: oaiSel];
958      IMP	get1 = [otherArray methodForSelector: oaiSel];
959
960      for (i = 0; i < c; i++)
961	if (![(*get0)(self, oaiSel, i) isEqual: (*get1)(otherArray, oaiSel, i)])
962	  return NO;
963    }
964  return YES;
965}
966
967/**
968 * Returns the last object in the receiver, or nil if the receiver is empty.
969 */
970- (id) lastObject
971{
972  NSUInteger count = [self count];
973  if (count == 0)
974    return nil;
975  return [self objectAtIndex: count-1];
976}
977
978/**
979 * Returns the first object in the receiver, or nil if the receiver is empty.
980 */
981- (id) firstObject
982{
983  NSUInteger count = [self count];
984  if (count == 0)
985    return nil;
986  return [self objectAtIndex: 0];
987}
988
989/**
990 * Makes each object in the array perform aSelector.<br />
991 * This is done sequentially from the first to the last object.
992 */
993- (void) makeObjectsPerformSelector: (SEL)aSelector
994{
995  NSUInteger	c = [self count];
996
997  if (c > 0)
998    {
999      IMP	        get = [self methodForSelector: oaiSel];
1000      NSUInteger	i = 0;
1001
1002      while (i < c)
1003	{
1004	  [(*get)(self, oaiSel, i++) performSelector: aSelector];
1005	}
1006    }
1007}
1008
1009/**
1010 * Obsolete version of -makeObjectsPerformSelector:
1011 */
1012- (void) makeObjectsPerform: (SEL)aSelector
1013{
1014   [self makeObjectsPerformSelector: aSelector];
1015}
1016
1017/**
1018 * Makes each object in the array perform aSelector with arg.<br />
1019 * This is done sequentially from the first to the last object.
1020 */
1021- (void) makeObjectsPerformSelector: (SEL)aSelector withObject: (id)arg
1022{
1023  NSUInteger    c = [self count];
1024
1025  if (c > 0)
1026    {
1027      IMP	        get = [self methodForSelector: oaiSel];
1028      NSUInteger	i = 0;
1029
1030      while (i < c)
1031	{
1032	  [(*get)(self, oaiSel, i++) performSelector: aSelector
1033					  withObject: arg];
1034	}
1035    }
1036}
1037
1038/**
1039 * Obsolete version of -makeObjectsPerformSelector:withObject:
1040 */
1041- (void) makeObjectsPerform: (SEL)aSelector withObject: (id)argument
1042{
1043   [self makeObjectsPerformSelector: aSelector withObject: argument];
1044}
1045
1046static NSComparisonResult
1047compare(id elem1, id elem2, void* context)
1048{
1049  NSComparisonResult (*imp)(id, SEL, id);
1050
1051  if (context == 0)
1052    {
1053      [NSException raise: NSInvalidArgumentException
1054		   format: @"compare null selector given"];
1055    }
1056
1057  imp = (NSComparisonResult (*)(id, SEL, id))
1058    [elem1 methodForSelector: context];
1059
1060  if (imp == NULL)
1061    {
1062      [NSException raise: NSGenericException
1063		  format: @"invalid selector passed to compare"];
1064    }
1065
1066  return (*imp)(elem1, context, elem2);
1067}
1068
1069/**
1070 * Returns an autoreleased array in which the objects are ordered
1071 * according to a sort with comparator.
1072 */
1073- (NSArray*) sortedArrayUsingSelector: (SEL)comparator
1074{
1075  return [self sortedArrayUsingFunction: compare context: (void *)comparator];
1076}
1077
1078/**
1079 * Returns an autoreleased array in which the objects are ordered
1080 * according to a sort with comparator.  This invokes
1081 * -sortedArrayUsingFunction:context:hint: with a nil hint.
1082 */
1083- (NSArray*) sortedArrayUsingFunction:
1084  (NSComparisonResult(*)(id,id,void*))comparator
1085  context: (void*)context
1086{
1087  return [self sortedArrayUsingFunction: comparator context: context hint: nil];
1088}
1089
1090/**
1091 * Subclasses may provide a hint for sorting ...  The default GNUstep
1092 * implementation just returns nil.
1093 */
1094- (NSData*) sortedArrayHint
1095{
1096  return nil;
1097}
1098
1099/**
1100 * Returns an autoreleased array in which the objects are ordered
1101 * according to a sort with comparator, where the comparator function
1102 * is passed two objects to compare, and the context as the third
1103 * argument.  The hint argument is currently ignored, and may be nil.
1104 */
1105- (NSArray*) sortedArrayUsingFunction:
1106  (NSComparisonResult(*)(id,id,void*))comparator
1107  context: (void*)context
1108  hint: (NSData*)hint
1109{
1110  NSMutableArray	*sortedArray;
1111
1112  sortedArray = AUTORELEASE([[NSMutableArrayClass allocWithZone:
1113    NSDefaultMallocZone()] initWithArray: self copyItems: NO]);
1114  [sortedArray sortUsingFunction: comparator context: context];
1115
1116  return GS_IMMUTABLE(sortedArray);
1117}
1118
1119
1120- (NSArray*) sortedArrayWithOptions: (NSSortOptions)options
1121                    usingComparator: (NSComparator)comparator
1122{
1123  NSMutableArray	*sortedArray;
1124
1125  sortedArray = AUTORELEASE([[NSMutableArrayClass allocWithZone:
1126    NSDefaultMallocZone()] initWithArray: self copyItems: NO]);
1127  [sortedArray sortWithOptions: options usingComparator: comparator];
1128
1129  return GS_IMMUTABLE(sortedArray);
1130}
1131
1132- (NSArray*) sortedArrayUsingComparator: (NSComparator)comparator
1133{
1134  return [self sortedArrayWithOptions: 0 usingComparator: comparator];
1135}
1136
1137- (NSUInteger) indexOfObject: (id)key
1138               inSortedRange: (NSRange)range
1139                     options: (NSBinarySearchingOptions)options
1140             usingComparator: (NSComparator)comparator
1141{
1142  if (range.length == 0)
1143    {
1144      return options & NSBinarySearchingInsertionIndex
1145        ? range.location : NSNotFound;
1146    }
1147  if (range.length == 1)
1148    {
1149      switch (CALL_BLOCK(comparator, key, [self objectAtIndex: range.location]))
1150        {
1151          case NSOrderedSame:
1152            return range.location;
1153          case NSOrderedAscending:
1154            return options & NSBinarySearchingInsertionIndex
1155              ? range.location : NSNotFound;
1156          case NSOrderedDescending:
1157            return options & NSBinarySearchingInsertionIndex
1158              ? (range.location + 1) : NSNotFound;
1159          default:
1160            // Shouldn't happen
1161            return NSNotFound;
1162        }
1163    }
1164  else
1165    {
1166      NSUInteger index = NSNotFound;
1167      NSUInteger count = [self count];
1168      GS_BEGINIDBUF(objects, count);
1169
1170      [self getObjects: objects];
1171      // We use the timsort galloping to find the insertion index:
1172      if (options & NSBinarySearchingLastEqual)
1173        {
1174          index = GSRightInsertionPointForKeyInSortedRange(key,
1175            objects, range, comparator);
1176        }
1177      else
1178        {
1179          // Left insertion is our default
1180          index = GSLeftInsertionPointForKeyInSortedRange(key,
1181            objects, range, comparator);
1182        }
1183      GS_ENDIDBUF()
1184
1185      // If we were looking for the insertion point, we are done here
1186      if (options & NSBinarySearchingInsertionIndex)
1187        {
1188          return index;
1189        }
1190
1191      /* Otherwise, we need need another equality check in order to
1192       * know whether we need return NSNotFound.
1193       */
1194
1195      if (options & NSBinarySearchingLastEqual)
1196        {
1197          /* For search from the right, the equal object would be
1198           * the one before the index, but only if it's not at the
1199           * very beginning of the range (though that might not
1200           * actually be possible, it's better to check nonetheless).
1201           */
1202          if (index > range.location)
1203            {
1204              index--;
1205            }
1206        }
1207      if (index >= NSMaxRange(range))
1208        {
1209          return NSNotFound;
1210        }
1211      /*
1212       * For a search from the left, we'd have the correct index anyways. Check
1213       * whether it's equal to the key and return NSNotFound otherwise
1214       */
1215      return (NSOrderedSame == CALL_BLOCK(comparator,
1216        key, [self objectAtIndex: index]) ? index : NSNotFound);
1217    }
1218  // Never reached
1219  return NSNotFound;
1220}
1221
1222/**
1223 * Returns a string formed by concatenating the objects in the receiver,
1224 * with the specified separator string inserted between each part.
1225 */
1226- (NSString*) componentsJoinedByString: (NSString*)separator
1227{
1228  NSUInteger		c = [self count];
1229  NSMutableString	*s;
1230
1231  s = [NSMutableString stringWithCapacity: c];
1232  if (c > 0)
1233    {
1234      NSUInteger	l = [separator length];
1235      NSUInteger	i;
1236
1237      [s appendString: [[self objectAtIndex: 0] description]];
1238      for (i = 1; i < c; i++)
1239	{
1240	  if (l > 0)
1241	    {
1242	      [s appendString: separator];
1243	    }
1244	  [s appendString: [[self objectAtIndex: i] description]];
1245	}
1246    }
1247  return GS_IMMUTABLE(s);
1248}
1249
1250/**
1251 * Assumes that the receiver is an array of paths, and returns an
1252 * array formed by selecting the subset of those patch matching
1253 * the specified array of extensions.
1254 */
1255- (NSArray*) pathsMatchingExtensions: (NSArray*)extensions
1256{
1257  NSUInteger i, c = [self count];
1258  NSMutableArray *a = AUTORELEASE([[NSMutableArray alloc] initWithCapacity: 1]);
1259  Class	cls = [NSString class];
1260  IMP	get = [self methodForSelector: oaiSel];
1261  IMP	add = [a methodForSelector: addSel];
1262
1263  for (i = 0; i < c; i++)
1264    {
1265      id o = (*get)(self, oaiSel, i);
1266
1267      if ([o isKindOfClass: cls])
1268	{
1269	  if ([extensions containsObject: [o pathExtension]])
1270	    {
1271	      (*add)(a, addSel, o);
1272	    }
1273	}
1274    }
1275  return GS_IMMUTABLE(a);
1276}
1277
1278/**
1279 * Returns the first object found in the receiver (starting at index 0)
1280 * which is present in the otherArray as determined by using the
1281 * -containsObject: method.
1282 */
1283- (id) firstObjectCommonWithArray: (NSArray*)otherArray
1284{
1285  NSUInteger i, c = [self count];
1286  id o;
1287
1288  for (i = 0; i < c; i++)
1289    {
1290      if ([otherArray containsObject: (o = [self objectAtIndex: i])])
1291	{
1292	  return o;
1293	}
1294    }
1295  return nil;
1296}
1297
1298/**
1299 * Returns a subarray of the receiver containing the objects found in
1300 * the specified range aRange.
1301 */
1302- (NSArray*) subarrayWithRange: (NSRange)aRange
1303{
1304  id na;
1305  NSUInteger c = [self count];
1306
1307  GS_RANGE_CHECK(aRange, c);
1308
1309  if (aRange.length == 0)
1310    {
1311      na = [NSArray array];
1312    }
1313  else
1314    {
1315      GS_BEGINIDBUF(objects, aRange.length);
1316
1317      [self getObjects: objects range: aRange];
1318      na = [NSArray arrayWithObjects: objects count: aRange.length];
1319      GS_ENDIDBUF();
1320    }
1321  return na;
1322}
1323
1324/**
1325 * Returns an enumerator describing the array sequentially
1326 * from the first to the last element.<br/>
1327 * If you use a mutable subclass of NSArray,
1328 * you should not modify the array during enumeration.
1329 */
1330- (NSEnumerator*) objectEnumerator
1331{
1332  id	e;
1333
1334  e = [NSArrayEnumerator allocWithZone: NSDefaultMallocZone()];
1335  e = [e initWithArray: self];
1336  return AUTORELEASE(e);
1337}
1338
1339/**
1340 * Returns an enumerator describing the array sequentially
1341 * from the last to the first element.<br/>
1342 * If you use a mutable subclass of NSArray,
1343 * you should not modify the array during enumeration.
1344 */
1345- (NSEnumerator*) reverseObjectEnumerator
1346{
1347  id	e;
1348
1349  e = [NSArrayEnumeratorReverse allocWithZone: NSDefaultMallocZone()];
1350  e = [e initWithArray: self];
1351  return AUTORELEASE(e);
1352}
1353
1354/**
1355 * Returns the result of invoking -descriptionWithLocale:indent: with a nil
1356 * locale and zero indent.
1357 */
1358- (NSString*) description
1359{
1360  return [self descriptionWithLocale: nil];
1361}
1362
1363/**
1364 * Returns the result of invoking -descriptionWithLocale:indent:
1365 * with a zero indent.
1366 */
1367- (NSString*) descriptionWithLocale: (id)locale
1368{
1369  return [self descriptionWithLocale: locale indent: 0];
1370}
1371
1372/**
1373 * Returns the receiver as a text property list in the traditional format.<br />
1374 * See [NSString-propertyList] for details.<br />
1375 * If locale is nil, no formatting is done, otherwise entries are formatted
1376 * according to the locale, and indented according to level.<br />
1377 * Unless locale is nil, a level of zero indents items by four spaces,
1378 * while a level of one indents them by a tab.<br />
1379 * The items in the property list string appear in the same order as
1380 * they appear in the receiver.
1381 */
1382- (NSString*) descriptionWithLocale: (id)locale
1383			     indent: (NSUInteger)level
1384{
1385  NSString	*result = nil;
1386
1387  GSPropertyListMake(self, locale, NO, YES, level == 1 ? 3 : 2, &result);
1388
1389  return result;
1390}
1391
1392/**
1393 * <p>Writes the contents of the array to the file specified by path.
1394 * The file contents will be in property-list format ... under GNUstep
1395 * this is either OpenStep style (ASCII characters using \U hexadecimal
1396 * escape sequences for unicode), or MacOS-X style (XML in the UTF8
1397 * character set).
1398 * </p>
1399 * <p>If the useAuxiliaryFile flag is YES, the file write operation is
1400 * atomic ... the data is written to a temporary file, which is then
1401 * renamed to the actual file name.
1402 * </p>
1403 * <p>If the conversion of data into the correct property-list format fails
1404 * or the write operation fails, the method returns NO, otherwise it
1405 * returns YES.
1406 * </p>
1407 * <p>NB. The fact that the file is in property-list format does not
1408 * necessarily mean that it can be used to reconstruct the array using
1409 * the -initWithContentsOfFile: method.  If the original array contains
1410 * non-property-list objects, the descriptions of those objects will
1411 * have been written, and reading in the file as a property-list will
1412 * result in a new array containing the string descriptions.
1413 * </p>
1414 */
1415- (BOOL) writeToFile: (NSString *)path atomically: (BOOL)useAuxiliaryFile
1416{
1417  NSDictionary	*loc;
1418  NSString	*desc = nil;
1419  NSData	*data;
1420
1421  loc = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation];
1422  if (GSMacOSXCompatiblePropertyLists() == YES)
1423    {
1424      GSPropertyListMake(self, loc, YES, NO, 2, &desc);
1425      data = [desc dataUsingEncoding: NSUTF8StringEncoding];
1426    }
1427  else
1428    {
1429      GSPropertyListMake(self, loc, NO, NO, 2, &desc);
1430      data = [desc dataUsingEncoding: NSASCIIStringEncoding];
1431    }
1432
1433  return [data writeToFile: path atomically: useAuxiliaryFile];
1434}
1435
1436/**
1437 * <p>Writes the contents of the array to the specified url.
1438 * This functions just like -writeToFile:atomically: except that the
1439 * output may be written to any URL, not just a local file.
1440 * </p>
1441 */
1442- (BOOL) writeToURL: (NSURL *)url atomically: (BOOL)useAuxiliaryFile
1443{
1444  NSDictionary	*loc;
1445  NSString	*desc = nil;
1446  NSData	*data;
1447
1448  loc = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation];
1449  if (GSMacOSXCompatiblePropertyLists() == YES)
1450    {
1451      GSPropertyListMake(self, loc, YES, NO, 2, &desc);
1452      data = [desc dataUsingEncoding: NSUTF8StringEncoding];
1453    }
1454  else
1455    {
1456      GSPropertyListMake(self, loc, NO, NO, 2, &desc);
1457      data = [desc dataUsingEncoding: NSASCIIStringEncoding];
1458    }
1459
1460  return [data writeToURL: url atomically: useAuxiliaryFile];
1461}
1462
1463/**
1464 * This overrides NSObjects implementation of this method.
1465 * This method returns an array of objects returned by
1466 * invoking -valueForKey: for each item in the receiver,
1467 * substituting NSNull for nil.
1468 * A special case: the key "count" is not forwarded to each object
1469 * of the receiver but returns the number of objects of the receiver.<br/>
1470 */
1471- (id) valueForKey: (NSString*)key
1472{
1473  id result = nil;
1474
1475  if ([key isEqualToString: @"@count"] == YES)
1476    {
1477      result = [NSNumber numberWithUnsignedInteger: [self count]];
1478    }
1479  else if ([key isEqualToString: @"count"] == YES)
1480    {
1481      GSOnceMLog(
1482@"[NSArray-valueForKey:] called with 'count' is deprecated .. use '@count'");
1483      result = [NSNumber numberWithUnsignedInteger: [self count]];
1484    }
1485  else
1486    {
1487      NSMutableArray	*results = nil;
1488      static NSNull	*null = nil;
1489      NSUInteger	i;
1490      NSUInteger	count = [self count];
1491      volatile id	object = nil;
1492
1493      results = [NSMutableArray arrayWithCapacity: count];
1494
1495      for (i = 0; i < count; i++)
1496        {
1497          id	result;
1498
1499          object = [self objectAtIndex: i];
1500          result = [object valueForKey: key];
1501          if (result == nil)
1502            {
1503              if (null == nil)
1504		{
1505		  null = RETAIN([NSNull null]);
1506		}
1507              result = null;
1508            }
1509
1510          [results addObject: result];
1511        }
1512
1513      result = results;
1514    }
1515  return result;
1516}
1517
1518- (id) valueForKeyPath: (NSString*)path
1519{
1520  id	result = nil;
1521
1522  if ([path hasPrefix: @"@"])
1523    {
1524      NSRange   r;
1525
1526      r = [path rangeOfString: @"."];
1527      if (r.length == 0)
1528        {
1529          if ([path isEqualToString: @"@count"] == YES)
1530            {
1531              result = [NSNumber numberWithUnsignedInteger: [self count]];
1532            }
1533          else
1534            {
1535              result = [self valueForKey: path];
1536            }
1537        }
1538      else
1539        {
1540          NSString      *op = [path substringToIndex: r.location];
1541          NSString      *rem = [path substringFromIndex: NSMaxRange(r)];
1542          NSUInteger    count = [self count];
1543
1544          if ([op isEqualToString: @"@count"] == YES)
1545            {
1546              result = [NSNumber numberWithUnsignedInteger: count];
1547            }
1548          else if ([op isEqualToString: @"@avg"] == YES)
1549            {
1550              double        d = 0;
1551
1552              if (count > 0)
1553                {
1554                  NSEnumerator  *e = [self objectEnumerator];
1555                  id            o;
1556
1557                  while ((o = [e nextObject]) != nil)
1558                    {
1559                      d += [[o valueForKeyPath: rem] doubleValue];
1560                    }
1561                  d /= count;
1562                }
1563              result = [NSNumber numberWithDouble: d];
1564            }
1565          else if ([op isEqualToString: @"@max"] == YES)
1566            {
1567              if (count > 0)
1568                {
1569                  NSEnumerator  *e = [self objectEnumerator];
1570                  id            o;
1571
1572                  while ((o = [e nextObject]) != nil)
1573                    {
1574                      o = [o valueForKeyPath: rem];
1575                      if (result == nil
1576                        || [result compare: o] == NSOrderedAscending)
1577                        {
1578                          result = o;
1579                        }
1580                    }
1581                }
1582            }
1583          else if ([op isEqualToString: @"@min"] == YES)
1584            {
1585              if (count > 0)
1586                {
1587                  NSEnumerator  *e = [self objectEnumerator];
1588                  id            o;
1589
1590                  while ((o = [e nextObject]) != nil)
1591                    {
1592                      o = [o valueForKeyPath: rem];
1593                      if (result == nil
1594                        || [result compare: o] == NSOrderedDescending)
1595                        {
1596                          result = o;
1597                        }
1598                    }
1599                }
1600            }
1601          else if ([op isEqualToString: @"@sum"] == YES)
1602            {
1603              double        d = 0;
1604
1605              if (count > 0)
1606                {
1607                  NSEnumerator  *e = [self objectEnumerator];
1608                  id            o;
1609
1610                  while ((o = [e nextObject]) != nil)
1611                    {
1612                      d += [[o valueForKeyPath: rem] doubleValue];
1613                    }
1614                }
1615              result = [NSNumber numberWithDouble: d];
1616            }
1617          else if ([op isEqualToString: @"@distinctUnionOfArrays"] == YES)
1618            {
1619              if (count > 0)
1620                {
1621                  NSEnumerator  *e = [self objectEnumerator];
1622                  id            o;
1623
1624                  result = [NSMutableSet set];
1625                  while ((o = [e nextObject]) != nil)
1626                    {
1627                      o = [o valueForKeyPath: rem];
1628                      [result addObjectsFromArray: o];
1629                    }
1630                  result = [result allObjects];
1631                }
1632              else
1633                {
1634                  result = [NSArray array];
1635                }
1636            }
1637          else if ([op isEqualToString: @"@distinctUnionOfObjects"] == YES)
1638            {
1639              if (count > 0)
1640                {
1641                  NSEnumerator  *e = [self objectEnumerator];
1642                  id            o;
1643
1644                  result = [NSMutableSet set];
1645                  while ((o = [e nextObject]) != nil)
1646                    {
1647                      o = [o valueForKeyPath: rem];
1648                      [result addObject: o];
1649                    }
1650                  result = [result allObjects];
1651                }
1652              else
1653                {
1654                  result = [NSArray array];
1655                }
1656            }
1657          else if ([op isEqualToString: @"@distinctUnionOfSets"] == YES)
1658            {
1659              if (count > 0)
1660                {
1661                  NSEnumerator  *e = [self objectEnumerator];
1662                  id            o;
1663
1664                  result = [NSMutableSet set];
1665                  while ((o = [e nextObject]) != nil)
1666                    {
1667                      o = [o valueForKeyPath: rem];
1668                      [result addObjectsFromArray: [o allObjects]];
1669                    }
1670                  result = [result allObjects];
1671                }
1672              else
1673                {
1674                  result = [NSArray array];
1675                }
1676            }
1677          else if ([op isEqualToString: @"@unionOfArrays"] == YES)
1678            {
1679              if (count > 0)
1680                {
1681                  NSEnumerator  *e = [self objectEnumerator];
1682                  id            o;
1683
1684                  result = [GSMutableArray array];
1685                  while ((o = [e nextObject]) != nil)
1686                    {
1687                      o = [o valueForKeyPath: rem];
1688                      [result addObjectsFromArray: o];
1689                    }
1690                  result = GS_IMMUTABLE(result);
1691                }
1692              else
1693                {
1694                  result = [NSArray array];
1695                }
1696            }
1697          else if ([op isEqualToString: @"@unionOfObjects"] == YES)
1698            {
1699              if (count > 0)
1700                {
1701                  NSEnumerator  *e = [self objectEnumerator];
1702                  id            o;
1703
1704                  result = [GSMutableArray array];
1705                  while ((o = [e nextObject]) != nil)
1706                    {
1707                      o = [o valueForKeyPath: rem];
1708                      [result addObject: o];
1709                    }
1710                  result = GS_IMMUTABLE(result);
1711                }
1712              else
1713                {
1714                  result = [NSArray array];
1715                }
1716            }
1717          else if ([op isEqualToString: @"@unionOfSets"] == YES)
1718            {
1719              if (count > 0)
1720                {
1721                  NSEnumerator  *e = [self objectEnumerator];
1722                  id            o;
1723
1724                  result = [GSMutableArray array];
1725                  while ((o = [e nextObject]) != nil)
1726                    {
1727                      o = [o valueForKeyPath: rem];
1728                      [result addObjectsFromArray: [o allObjects]];
1729                    }
1730                  result = GS_IMMUTABLE(result);
1731                }
1732              else
1733                {
1734                  result = [NSArray array];
1735                }
1736            }
1737          else
1738            {
1739              result = [super valueForKeyPath: path];
1740            }
1741        }
1742    }
1743  else
1744    {
1745      result = [super valueForKeyPath: path];
1746    }
1747
1748  return result;
1749}
1750
1751/**
1752 * Call setValue:forKey: on each of the receiver's items
1753 * with the value and key.
1754 */
1755- (void) setValue: (id)value forKey: (NSString*)key
1756{
1757  NSUInteger    i;
1758  NSUInteger	count = [self count];
1759  volatile id	object = nil;
1760
1761  for (i = 0; i < count; i++)
1762    {
1763      object = [self objectAtIndex: i];
1764      [object setValue: value
1765		forKey: key];
1766    }
1767}
1768
1769- (void) enumerateObjectsUsingBlock: (GSEnumeratorBlock)aBlock
1770{
1771  [self enumerateObjectsWithOptions: 0 usingBlock: aBlock];
1772}
1773
1774- (void) enumerateObjectsWithOptions: (NSEnumerationOptions)opts
1775			  usingBlock: (GSEnumeratorBlock)aBlock
1776{
1777  NSUInteger count = 0;
1778  BLOCK_SCOPE BOOL shouldStop = NO;
1779  BOOL isReverse = (opts & NSEnumerationReverse);
1780  id<NSFastEnumeration> enumerator = self;
1781
1782  /* If we are enumerating in reverse, use the reverse enumerator for fast
1783   * enumeration. */
1784  if (isReverse)
1785    {
1786      enumerator = [self reverseObjectEnumerator];
1787      count = ([self count] - 1);
1788    }
1789
1790  {
1791  GS_DISPATCH_CREATE_QUEUE_AND_GROUP_FOR_ENUMERATION(enumQueue, opts)
1792  FOR_IN (id, obj, enumerator)
1793    GS_DISPATCH_SUBMIT_BLOCK(enumQueueGroup, enumQueue, if (shouldStop == NO) {, }, aBlock, obj, count, &shouldStop);
1794      if (isReverse)
1795        {
1796          count--;
1797        }
1798      else
1799        {
1800          count++;
1801        }
1802
1803      if (shouldStop)
1804        {
1805          break;
1806        }
1807    END_FOR_IN(enumerator)
1808    GS_DISPATCH_TEARDOWN_QUEUE_AND_GROUP_FOR_ENUMERATION(enumQueue, opts)
1809  }
1810}
1811
1812- (void) enumerateObjectsAtIndexes: (NSIndexSet*)indexSet
1813			   options: (NSEnumerationOptions)opts
1814		        usingBlock: (GSEnumeratorBlock)block
1815{
1816  [[self objectsAtIndexes: indexSet] enumerateObjectsWithOptions: opts
1817						      usingBlock: block];
1818}
1819
1820- (NSIndexSet *) indexesOfObjectsWithOptions: (NSEnumerationOptions)opts
1821				 passingTest: (GSPredicateBlock)predicate
1822{
1823  /* TODO: Concurrency. */
1824  NSMutableIndexSet     *set = [NSMutableIndexSet indexSet];
1825  BLOCK_SCOPE BOOL      shouldStop = NO;
1826  id<NSFastEnumeration> enumerator = self;
1827  NSUInteger            count = 0;
1828  BLOCK_SCOPE NSLock    *setLock = nil;
1829
1830  /* If we are enumerating in reverse, use the reverse enumerator for fast
1831   * enumeration. */
1832  if (opts & NSEnumerationReverse)
1833    {
1834      enumerator = [self reverseObjectEnumerator];
1835    }
1836  if (opts & NSEnumerationConcurrent)
1837    {
1838      setLock = [NSLock new];
1839    }
1840  {
1841    GS_DISPATCH_CREATE_QUEUE_AND_GROUP_FOR_ENUMERATION(enumQueue, opts)
1842    FOR_IN (id, obj, enumerator)
1843#     if __has_feature(blocks) && (GS_USE_LIBDISPATCH == 1)
1844      if (enumQueue != NULL)
1845        {
1846          dispatch_group_async(enumQueueGroup, enumQueue, ^(void){
1847            if (shouldStop)
1848            {
1849              return;
1850            }
1851            if (predicate(obj, count, &shouldStop))
1852            {
1853              [setLock lock];
1854              [set addIndex: count];
1855              [setLock unlock];
1856            }
1857          });
1858        }
1859      else // call block directly
1860#     endif
1861      if (CALL_BLOCK(predicate, obj, count, &shouldStop))
1862        {
1863          /* TODO: It would be more efficient to collect an NSRange and only
1864           * pass it to the index set when CALL_BLOCK returned NO. */
1865          [set addIndex: count];
1866        }
1867      if (shouldStop)
1868        {
1869          break;
1870        }
1871      count++;
1872    END_FOR_IN(enumerator)
1873    GS_DISPATCH_TEARDOWN_QUEUE_AND_GROUP_FOR_ENUMERATION(enumQueue, opts);
1874  }
1875  RELEASE(setLock);
1876  return set;
1877}
1878
1879- (NSIndexSet*) indexesOfObjectsPassingTest: (GSPredicateBlock)predicate
1880{
1881  return [self indexesOfObjectsWithOptions: 0 passingTest: predicate];
1882}
1883
1884- (NSIndexSet*) indexesOfObjectsAtIndexes: (NSIndexSet*)indexSet
1885				  options: (NSEnumerationOptions)opts
1886			      passingTest: (GSPredicateBlock)predicate
1887{
1888  NSIndexSet *rindexes =[[self objectsAtIndexes: indexSet]
1889			 indexesOfObjectsWithOptions: opts
1890					 passingTest: predicate];
1891  NSUInteger count = [indexSet count];
1892  NSUInteger resultCount = [rindexes count];
1893  NSUInteger indexArray[count], resultIndexArray[resultCount];
1894  NSMutableIndexSet *resultSet = [NSMutableIndexSet indexSet];
1895  NSUInteger i = 0;
1896
1897  [indexSet getIndexes: indexArray
1898	      maxCount: count
1899	  inIndexRange: NULL];
1900
1901  [rindexes getIndexes: resultIndexArray
1902	      maxCount: resultCount
1903	  inIndexRange: NULL];
1904
1905  // interate over indexes and collect the matching ones..
1906  for(i = 0; i < resultCount; i++)
1907    {
1908      NSUInteger rindx = resultIndexArray[i];
1909      NSUInteger indx = indexArray[rindx];
1910      [resultSet addIndex: indx];
1911    }
1912
1913  return resultSet;
1914}
1915
1916- (NSUInteger) indexOfObjectWithOptions: (NSEnumerationOptions)opts
1917			    passingTest: (GSPredicateBlock)predicate
1918{
1919  /* TODO: Concurrency. */
1920  id<NSFastEnumeration> enumerator = self;
1921  BLOCK_SCOPE BOOL      shouldStop = NO;
1922  NSUInteger            count = 0;
1923  BLOCK_SCOPE NSUInteger index = NSNotFound;
1924  BLOCK_SCOPE NSLock    *indexLock = nil;
1925
1926  /* If we are enumerating in reverse, use the reverse enumerator for fast
1927   * enumeration. */
1928  if (opts & NSEnumerationReverse)
1929    {
1930      enumerator = [self reverseObjectEnumerator];
1931    }
1932
1933  if (opts & NSEnumerationConcurrent)
1934    {
1935      indexLock = [NSLock new];
1936    }
1937  {
1938    GS_DISPATCH_CREATE_QUEUE_AND_GROUP_FOR_ENUMERATION(enumQueue, opts)
1939    FOR_IN (id, obj, enumerator)
1940#     if __has_feature(blocks) && (GS_USE_LIBDISPATCH == 1)
1941      if (enumQueue != NULL)
1942        {
1943          dispatch_group_async(enumQueueGroup, enumQueue, ^(void){
1944            if (shouldStop)
1945            {
1946              return;
1947            }
1948            if (predicate(obj, count, &shouldStop))
1949            {
1950              // FIXME: atomic operation on the shouldStop variable would be nicer,
1951              // but we don't expose the GSAtomic* primitives anywhere.
1952              [indexLock lock];
1953              index =  count;
1954              // Cancel all other predicate evaluations:
1955              shouldStop = YES;
1956              [indexLock unlock];
1957            }
1958          });
1959        }
1960      else // call block directly
1961#     endif
1962      if (CALL_BLOCK(predicate, obj, count, &shouldStop))
1963        {
1964          index = count;
1965          shouldStop = YES;
1966        }
1967      if (shouldStop)
1968        {
1969          break;
1970        }
1971      count++;
1972    END_FOR_IN(enumerator)
1973    GS_DISPATCH_TEARDOWN_QUEUE_AND_GROUP_FOR_ENUMERATION(enumQueue, opts);
1974  }
1975  RELEASE(indexLock);
1976  return index;
1977}
1978
1979- (NSUInteger) indexOfObjectPassingTest: (GSPredicateBlock)predicate
1980{
1981  return [self indexOfObjectWithOptions: 0 passingTest: predicate];
1982}
1983
1984- (NSUInteger) indexOfObjectAtIndexes: (NSIndexSet*)indexSet
1985			      options: (NSEnumerationOptions)opts
1986			  passingTest: (GSPredicateBlock)predicate
1987{
1988  NSUInteger index = [[self objectsAtIndexes: indexSet]
1989		       indexOfObjectWithOptions: 0
1990				    passingTest: predicate];
1991  NSUInteger count = [indexSet count];
1992  NSUInteger indexArray[count];
1993
1994  [indexSet getIndexes: indexArray
1995	      maxCount: count
1996	  inIndexRange: NULL];
1997
1998  return indexArray[index];
1999}
2000
2001@end
2002
2003
2004/**
2005 *  <code>NSMutableArray</code> is the mutable version of [NSArray].  It
2006 *  provides methods for altering the contents of the array.
2007 */
2008@implementation NSMutableArray
2009
2010+ (void) initialize
2011{
2012  if (self == [NSMutableArray class])
2013    {
2014    }
2015}
2016
2017+ (id) allocWithZone: (NSZone*)z
2018{
2019  if (self == NSMutableArrayClass)
2020    {
2021      return NSAllocateObject(GSMutableArrayClass, 0, z);
2022    }
2023  else
2024    {
2025      return NSAllocateObject(self, 0, z);
2026    }
2027}
2028
2029+ (id) arrayWithObject: (id)anObject
2030{
2031  NSMutableArray	*obj = [self allocWithZone: NSDefaultMallocZone()];
2032
2033  obj = [obj initWithObjects: &anObject count: 1];
2034  return AUTORELEASE(obj);
2035}
2036
2037- (Class) classForCoder
2038{
2039  return NSMutableArrayClass;
2040}
2041
2042- (id) initWithCapacity: (NSUInteger)numItems
2043{
2044  self = [self init];
2045  return self;
2046}
2047
2048- (void) addObject: (id)anObject
2049{
2050  [self subclassResponsibility: _cmd];
2051}
2052
2053/**
2054 * Swaps the positions of two objects in the array.  Raises an exception
2055 * if either array index is out of bounds.
2056 */
2057- (void) exchangeObjectAtIndex: (NSUInteger)i1
2058             withObjectAtIndex: (NSUInteger)i2
2059{
2060  id	tmp = [self objectAtIndex: i1];
2061
2062  RETAIN(tmp);
2063  [self replaceObjectAtIndex: i1 withObject: [self objectAtIndex: i2]];
2064  [self replaceObjectAtIndex: i2 withObject: tmp];
2065  RELEASE(tmp);
2066}
2067
2068- (void) replaceObjectAtIndex: (NSUInteger)index withObject: (id)anObject
2069{
2070  [self subclassResponsibility: _cmd];
2071}
2072
2073- (void) setObject: (id)anObject atIndexedSubscript: (NSUInteger)anIndex
2074{
2075  if ([self count] == anIndex)
2076    {
2077      [self addObject: anObject];
2078    }
2079  else
2080    {
2081      [self replaceObjectAtIndex: anIndex withObject: anObject];
2082    }
2083}
2084
2085/** Replaces the values in the receiver at the locations given by the
2086 * indexes set with values from the objects array.
2087 */
2088- (void) replaceObjectsAtIndexes: (NSIndexSet *)indexes
2089                     withObjects: (NSArray *)objects
2090{
2091  NSUInteger	index = [indexes firstIndex];
2092  NSEnumerator	*enumerator = [objects objectEnumerator];
2093  id		object = [enumerator nextObject];
2094
2095  while (object != nil && index != NSNotFound)
2096    {
2097      [self replaceObjectAtIndex: index withObject: object];
2098      object = [enumerator nextObject];
2099      index = [indexes indexGreaterThanIndex: index];
2100    }
2101}
2102
2103/**
2104 * Replaces objects in the receiver with those from anArray.<br />
2105 * Raises an exception if given a range extending beyond the array.<br />
2106 */
2107- (void) replaceObjectsInRange: (NSRange)aRange
2108	  withObjectsFromArray: (NSArray*)anArray
2109{
2110  id e, o;
2111
2112  if ([self count] < (aRange.location + aRange.length))
2113    [NSException raise: NSRangeException
2114		 format: @"Replacing objects beyond end of array."];
2115  [self removeObjectsInRange: aRange];
2116  e = [anArray reverseObjectEnumerator];
2117  while ((o = [e nextObject]))
2118    [self insertObject: o atIndex: aRange.location];
2119}
2120
2121/**
2122 * Replaces objects in the receiver with some of those from anArray.<br />
2123 * Raises an exception if given a range extending beyond the array.<br />
2124 */
2125- (void) replaceObjectsInRange: (NSRange)aRange
2126	  withObjectsFromArray: (NSArray*)anArray
2127			 range: (NSRange)anotherRange
2128{
2129  [self replaceObjectsInRange: aRange
2130	 withObjectsFromArray: [anArray subarrayWithRange: anotherRange]];
2131}
2132
2133- (void) insertObject: anObject atIndex: (NSUInteger)index
2134{
2135  [self subclassResponsibility: _cmd];
2136}
2137
2138/** Inserts the values from the objects array into the receiver at the
2139 * locations given by the indexes set.<br />
2140 * The values are inserted in the same order that they appear in the
2141 * array.
2142 */
2143- (void) insertObjects: (NSArray *)objects atIndexes: (NSIndexSet *)indexes
2144{
2145  NSUInteger	index = [indexes firstIndex];
2146  NSEnumerator	*enumerator = [objects objectEnumerator];
2147  id		object = [enumerator nextObject];
2148
2149  while (object != nil && index != NSNotFound)
2150    {
2151      [self insertObject: object atIndex: index];
2152      object = [enumerator nextObject];
2153      index = [indexes indexGreaterThanIndex: index];
2154    }
2155}
2156
2157- (void) removeObjectAtIndex: (NSUInteger)index
2158{
2159  [self subclassResponsibility: _cmd];
2160}
2161
2162/**
2163 * Creates an autoreleased mutable array able to store at least numItems.
2164 * See the -initWithCapacity: method.
2165 */
2166+ (id) arrayWithCapacity: (NSUInteger)numItems
2167{
2168  return AUTORELEASE([[self allocWithZone: NSDefaultMallocZone()]
2169    initWithCapacity: numItems]);
2170}
2171
2172/**
2173 * Override our superclass's designated initializer to go our's
2174 */
2175- (id) initWithObjects: (const id[])objects count: (NSUInteger)count
2176{
2177  self = [self initWithCapacity: count];
2178  if (count > 0)
2179    {
2180      NSUInteger	i;
2181      IMP	add = [self methodForSelector: addSel];
2182
2183      for (i = 0; i < count; i++)
2184	(*add)(self, addSel, objects[i]);
2185    }
2186  return self;
2187}
2188
2189/**
2190 * Removes the last object in the array.  Raises an exception if the array
2191 * is already empty.
2192 */
2193- (void) removeLastObject
2194{
2195  NSUInteger	count = [self count];
2196
2197  if (count == 0)
2198    [NSException raise: NSRangeException
2199		 format: @"Trying to remove from an empty array."];
2200  [self removeObjectAtIndex: count-1];
2201}
2202
2203/**
2204 * Removes all occurrences of anObject (found by pointer equality)
2205 * from the receiver.
2206 */
2207- (void) removeObjectIdenticalTo: (id)anObject
2208{
2209  NSUInteger	i;
2210
2211  if (anObject == nil)
2212    {
2213      NSWarnMLog(@"attempt to remove nil object");
2214      return;
2215    }
2216  i = [self count];
2217  if (i > 0)
2218    {
2219      IMP	rem = 0;
2220      IMP	get = [self methodForSelector: oaiSel];
2221
2222      while (i-- > 0)
2223	{
2224	  id	o = (*get)(self, oaiSel, i);
2225
2226	  if (o == anObject)
2227	    {
2228	      if (rem == 0)
2229		{
2230		  rem = [self methodForSelector: remSel];
2231		}
2232	      (*rem)(self, remSel, i);
2233	    }
2234	}
2235    }
2236}
2237
2238/**
2239 * Removes all occurrences of anObject (found by the [NSObject-isEqual:] method
2240 * of anObject) aRange in the receiver.
2241 */
2242- (void) removeObject: (id)anObject inRange: (NSRange)aRange
2243{
2244  NSUInteger	c;
2245  NSUInteger	s;
2246  NSUInteger	i;
2247
2248  if (anObject == nil)
2249    {
2250      NSWarnMLog(@"attempt to remove nil object");
2251      return;
2252    }
2253  c = [self count];
2254  s = aRange.location;
2255  i = aRange.location + aRange.length;
2256  if (i > c)
2257    {
2258      i = c;
2259    }
2260  if (i > s)
2261    {
2262      IMP	rem = 0;
2263      IMP	get = [self methodForSelector: oaiSel];
2264      BOOL	(*eq)(id, SEL, id)
2265	= (BOOL (*)(id, SEL, id))[anObject methodForSelector: eqSel];
2266
2267      while (i-- > s)
2268	{
2269	  id	o = (*get)(self, oaiSel, i);
2270
2271	  if (o == anObject || (*eq)(anObject, eqSel, o) == YES)
2272	    {
2273	      if (rem == 0)
2274		{
2275		  rem = [self methodForSelector: remSel];
2276		  /*
2277		   * We need to retain the object so that when we remove the
2278		   * first equal object we don't get left with a bad object
2279		   * pointer for later comparisons.
2280		   */
2281		  RETAIN(anObject);
2282		}
2283	      (*rem)(self, remSel, i);
2284	    }
2285	}
2286      if (rem != 0)
2287	{
2288	  RELEASE(anObject);
2289	}
2290    }
2291}
2292
2293/**
2294 * Removes all occurrences of anObject (found by pointer equality)
2295 * from aRange in the receiver.
2296 */
2297- (void) removeObjectIdenticalTo: (id)anObject inRange: (NSRange)aRange
2298{
2299  NSUInteger	c;
2300  NSUInteger	s;
2301  NSUInteger	i;
2302
2303  if (anObject == nil)
2304    {
2305      NSWarnMLog(@"attempt to remove nil object");
2306      return;
2307    }
2308  c = [self count];
2309  s = aRange.location;
2310  i = aRange.location + aRange.length;
2311  if (i > c)
2312    {
2313      i = c;
2314    }
2315  if (i > s)
2316    {
2317      IMP	rem = 0;
2318      IMP	get = [self methodForSelector: oaiSel];
2319
2320      while (i-- > s)
2321	{
2322	  id	o = (*get)(self, oaiSel, i);
2323
2324	  if (o == anObject)
2325	    {
2326	      if (rem == 0)
2327		{
2328		  rem = [self methodForSelector: remSel];
2329		}
2330	      (*rem)(self, remSel, i);
2331	    }
2332	}
2333    }
2334}
2335
2336/**
2337 * Removes all occurrences of anObject (found by anObject's
2338 * [NSObject-isEqual:] method) from the receiver.
2339 */
2340- (void) removeObject: (id)anObject
2341{
2342  NSUInteger	i;
2343
2344  if (anObject == nil)
2345    {
2346      NSWarnMLog(@"attempt to remove nil object");
2347      return;
2348    }
2349  i = [self count];
2350  if (i > 0)
2351    {
2352      IMP	rem = 0;
2353      IMP	get = [self methodForSelector: oaiSel];
2354      BOOL	(*eq)(id, SEL, id)
2355	= (BOOL (*)(id, SEL, id))[anObject methodForSelector: eqSel];
2356
2357      while (i-- > 0)
2358	{
2359	  id	o = (*get)(self, oaiSel, i);
2360
2361	  if (o == anObject || (*eq)(anObject, eqSel, o) == YES)
2362	    {
2363	      if (rem == 0)
2364		{
2365		  rem = [self methodForSelector: remSel];
2366		  /*
2367		   * We need to retain the object so that when we remove the
2368		   * first equal object we don't get left with a bad object
2369		   * pointer for later comparisons.
2370		   */
2371		  RETAIN(anObject);
2372		}
2373	      (*rem)(self, remSel, i);
2374	    }
2375	}
2376      if (rem != 0)
2377	{
2378	  RELEASE(anObject);
2379	}
2380    }
2381}
2382
2383/**
2384 * Removes all objects from the receiver, leaving an empty array.
2385 */
2386- (void) removeAllObjects
2387{
2388  NSUInteger	c = [self count];
2389
2390  if (c > 0)
2391    {
2392      IMP	remLast = [self methodForSelector: rlSel];
2393
2394      while (c--)
2395	{
2396	  (*remLast)(self, rlSel);
2397	}
2398    }
2399}
2400
2401/**
2402 * Adds each object from otherArray to the receiver, in first to last order.
2403 */
2404- (void) addObjectsFromArray: (NSArray*)otherArray
2405{
2406  NSUInteger c = [otherArray count];
2407
2408  if (c > 0)
2409    {
2410      NSUInteger	i;
2411      IMP	get = [otherArray methodForSelector: oaiSel];
2412      IMP	add = [self methodForSelector: addSel];
2413
2414      for (i = 0; i < c; i++)
2415	(*add)(self, addSel,  (*get)(otherArray, oaiSel, i));
2416    }
2417}
2418
2419/**
2420 * Sets the contents of the receiver to be identical to the contents
2421 * of otherArray.
2422 */
2423- (void) setArray: (NSArray *)otherArray
2424{
2425  [self removeAllObjects];
2426  [self addObjectsFromArray: otherArray];
2427}
2428
2429/**
2430 * Removes objects from the receiver at the indices supplied by an NSIndexSet
2431 */
2432- (void) removeObjectsAtIndexes: (NSIndexSet *)indexes
2433{
2434  NSUInteger count = [indexes count];
2435  NSUInteger indexArray[count];
2436
2437  [indexes getIndexes: indexArray
2438             maxCount: count
2439         inIndexRange: NULL];
2440
2441  [self removeObjectsFromIndices: indexArray
2442                      numIndices: count];
2443}
2444
2445/**
2446 * Supplied with a C array of indices containing count values, this method
2447 * removes all corresponding objects from the receiver.  The objects are
2448 * removed in such a way that the removal is <em>safe</em> irrespective
2449 * of the order in which they are specified in the indices array.
2450 */
2451- (void) removeObjectsFromIndices: (NSUInteger*)indices
2452		       numIndices: (NSUInteger)count
2453{
2454  if (count > 0)
2455    {
2456      NSUInteger	to = 0;
2457      NSUInteger	from = 0;
2458      NSUInteger	i;
2459      GS_BEGINITEMBUF(sorted, count, NSUInteger);
2460
2461      while (from < count)
2462	{
2463	  NSUInteger	val = indices[from++];
2464
2465	  i = to;
2466	  while (i > 0 && sorted[i-1] > val)
2467	    {
2468	      i--;
2469	    }
2470	  if (i == to)
2471	    {
2472	      sorted[to++] = val;
2473	    }
2474	  else if (sorted[i] != val)
2475	    {
2476	      NSUInteger	j = to++;
2477
2478	      if (sorted[i] < val)
2479		{
2480		  i++;
2481		}
2482	      while (j > i)
2483		{
2484		  sorted[j] = sorted[j-1];
2485		  j--;
2486		}
2487	      sorted[i] = val;
2488	    }
2489	}
2490
2491      if (to > 0)
2492	{
2493	  IMP	rem = [self methodForSelector: remSel];
2494
2495	  while (to--)
2496	    {
2497	      (*rem)(self, remSel, sorted[to]);
2498	    }
2499	}
2500      GS_ENDITEMBUF();
2501    }
2502}
2503
2504/**
2505 * Removes from the receiver, all the objects present in otherArray,
2506 * as determined by using the [NSObject-isEqual:] method.
2507 */
2508- (void) removeObjectsInArray: (NSArray*)otherArray
2509{
2510  if (otherArray == self)
2511    {
2512      [self removeAllObjects];
2513    }
2514  else if (otherArray != nil)
2515    {
2516      NSUInteger	c;
2517
2518      if (NO == [otherArray isKindOfClass: NSArrayClass])
2519	{
2520	  [NSException raise: NSInvalidArgumentException
2521		      format: @"-removeObjectsInArray: non-array argument"];
2522	}
2523      if ((c = [otherArray count]) > 0)
2524	{
2525	  NSUInteger	i;
2526	  IMP	get = [otherArray methodForSelector: oaiSel];
2527	  IMP	rem = [self methodForSelector: @selector(removeObject:)];
2528
2529	  /* Guard otherArray in case it's a subclass which does not
2530	   * retain its contents; in that case it would be possible
2531	   * for otherArray to be contained by the receiver and be
2532	   * deallocated during the loop below.
2533	   */
2534	  RETAIN(otherArray);
2535	  for (i = 0; i < c; i++)
2536	    {
2537	      (*rem)(self, @selector(removeObject:),
2538		(*get)(otherArray, oaiSel, i));
2539	    }
2540	  RELEASE(otherArray);
2541	}
2542    }
2543}
2544
2545/**
2546 * Removes all the objects in aRange from the receiver.
2547 */
2548- (void) removeObjectsInRange: (NSRange)aRange
2549{
2550  NSUInteger	i;
2551  NSUInteger	s = aRange.location;
2552  NSUInteger	c = [self count];
2553
2554  i = aRange.location + aRange.length;
2555
2556  if (c < i)
2557    i = c;
2558
2559  if (i > s)
2560    {
2561      IMP	rem = [self methodForSelector: remSel];
2562
2563      while (i-- > s)
2564	{
2565	  (*rem)(self, remSel, i);
2566	}
2567    }
2568}
2569
2570/**
2571 * Sorts the array according to the supplied comparator.
2572 */
2573- (void) sortUsingSelector: (SEL)comparator
2574{
2575  [self sortUsingFunction: compare context: (void *)comparator];
2576}
2577
2578/**
2579 * Sorts the array according to the supplied compare function
2580 * with the context information.
2581 */
2582- (void) sortUsingFunction: (NSComparisonResult (*)(id,id,void*))compare
2583		   context: (void*)context
2584{
2585  NSUInteger count = [self count];
2586
2587  if ((1 < count) && (NULL != compare))
2588    {
2589      NSArray *res = nil;
2590      GS_BEGINIDBUF(objects, count);
2591      [self getObjects: objects];
2592
2593      GSSortUnstable(objects,
2594        NSMakeRange(0,count), (id)compare, GSComparisonTypeFunction, context);
2595
2596      res = [[NSArray alloc] initWithObjects: objects count: count];
2597      [self setArray: res];
2598      RELEASE(res);
2599      GS_ENDIDBUF();
2600    }
2601}
2602
2603- (void) sortWithOptions: (NSSortOptions)options
2604         usingComparator: (NSComparator)comparator
2605{
2606  NSUInteger count = [self count];
2607
2608  if ((1 < count) && (NULL != comparator))
2609    {
2610      NSArray *res = nil;
2611      GS_BEGINIDBUF(objects, count);
2612      [self getObjects: objects];
2613
2614      if (options & NSSortStable)
2615        {
2616          if (options & NSSortConcurrent)
2617            {
2618              GSSortStableConcurrent(objects, NSMakeRange(0,count),
2619                (id)comparator, GSComparisonTypeComparatorBlock, NULL);
2620            }
2621          else
2622            {
2623              GSSortStable(objects, NSMakeRange(0,count),
2624                (id)comparator, GSComparisonTypeComparatorBlock, NULL);
2625            }
2626        }
2627      else
2628        {
2629          if (options & NSSortConcurrent)
2630            {
2631              GSSortUnstableConcurrent(objects, NSMakeRange(0,count),
2632                (id)comparator, GSComparisonTypeComparatorBlock, NULL);
2633            }
2634          else
2635            {
2636              GSSortUnstable(objects, NSMakeRange(0,count),
2637                (id)comparator, GSComparisonTypeComparatorBlock, NULL);
2638            }
2639        }
2640      res = [[NSArray alloc] initWithObjects: objects count: count];
2641      [self setArray: res];
2642      RELEASE(res);
2643      GS_ENDIDBUF();
2644    }
2645}
2646
2647- (void) sortUsingComparator: (NSComparator)comparator
2648{
2649  [self sortWithOptions: 0 usingComparator: comparator];
2650}
2651@end
2652
2653@implementation NSArrayEnumerator
2654
2655- (id) initWithArray: (NSArray*)anArray
2656{
2657  self = [super init];
2658  if (self != nil)
2659    {
2660      array = anArray;
2661      IF_NO_GC(RETAIN(array));
2662      pos = 0;
2663      get = [array methodForSelector: oaiSel];
2664      cnt = (NSUInteger (*)(NSArray*, SEL))[array methodForSelector: countSel];
2665    }
2666  return self;
2667}
2668
2669/**
2670 * Returns the next object in the enumeration or nil if there are no more
2671 * objects.<br />
2672 * NB. modifying a mutable array during an enumeration can break things ...
2673 * don't do it.
2674 */
2675- (id) nextObject
2676{
2677  if (pos >= (*cnt)(array, countSel))
2678    return nil;
2679  return (*get)(array, oaiSel, pos++);
2680}
2681
2682- (void) dealloc
2683{
2684  RELEASE(array);
2685  [super dealloc];
2686}
2687
2688@end
2689
2690@implementation NSArrayEnumeratorReverse
2691
2692- (id) initWithArray: (NSArray*)anArray
2693{
2694  self = [super initWithArray: anArray];
2695  if (self != nil)
2696    {
2697      pos = (*cnt)(array, countSel);
2698    }
2699  return self;
2700}
2701
2702/**
2703 * Returns the next object in the enumeration or nil if there are no more
2704 * objects.<br />
2705 * NB. modifying a mutable array during an enumeration can break things ...
2706 * don't do it.
2707 */
2708- (id) nextObject
2709{
2710  if (pos == 0)
2711    return nil;
2712  return (*get)(array, oaiSel, --pos);
2713}
2714@end
2715
2716