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