ftp.nice.ch/Attic/openStep/implementation/gnustep/sources/libFoundation.0.7.tgz#/libFoundation-0.7/libFoundation/Foundation/NSArray.m

This is NSArray.m in view mode; [Download] [Up]

/* 
   NSArray.m

   Copyright (C) 1995, 1996 Ovidiu Predescu and Mircea Oancea.
   All rights reserved.

   Author: Mircea Oancea <mircea@jupiter.elcom.pub.ro>

   This file is part of libFoundation.

   Permission to use, copy, modify, and distribute this software and its
   documentation for any purpose and without fee is hereby granted, provided
   that the above copyright notice appear in all copies and that both that
   copyright notice and this permission notice appear in supporting
   documentation.

   We disclaim all warranties with regard to this software, including all
   implied warranties of merchantability and fitness, in no event shall
   we be liable for any special, indirect or consequential damages or any
   damages whatsoever resulting from loss of use, data or profits, whether in
   an action of contract, negligence or other tortious action, arising out of
   or in connection with the use or performance of this software.
*/

#include <stdarg.h>
#include <stdlib.h>
#include <string.h>

#include <Foundation/common.h>
#include <Foundation/NSObject.h>
#include <Foundation/NSArray.h>
#include <Foundation/NSRange.h>
#include <Foundation/NSString.h>
#include <Foundation/NSAutoreleasePool.h>
#include <Foundation/NSException.h>
#include <Foundation/NSCoder.h>

#include <Foundation/exceptions/GeneralExceptions.h>

#include "NSConcreteArray.h"

/*
 * NSArray Implementation
 * 
 * primary methods are
 *     init
 *     initWithObjects:count:
 *     dealloc
 *     count
 *     objectAtIndex:
 */

@implementation NSArray

/* Allocating and Initializing an Array */

+ (id)allocWithZone:(NSZone*)zone
{
    return NSAllocateObject( (self == [NSArray class]) ? 
	    [NSConcreteArray class] : self, 0, zone);
}

+ (id)array
{
    return [[[self alloc] init] autorelease];
}

+ (id)arrayWithObject:(id)anObject
{
    return [[[self alloc] initWithObjects:&anObject count:1] autorelease];
}

+ (id)arrayWithObjects:(id)firstObj,...
{
    id array, obj, *objects;
    va_list list;
    unsigned int count;

    va_start(list, firstObj);
    for (count=0, obj = firstObj; obj; obj = va_arg(list,id))
	count++;
    va_end(list);

    objects = Malloc(sizeof(id)*count);
    va_start(list, firstObj);
    for (count = 0, obj = firstObj; obj; obj = va_arg(list,id))
	objects[count++] = obj;
    va_end(list);

    array = [[[self alloc] initWithObjects:objects count:count] autorelease];
    
    Free(objects);
    return array;
}

+ (id)arrayWithArray:(NSArray*)anotherArray
{
    return [[[self alloc] initWithArray:anotherArray] autorelease];
}

+ (id)arrayWithContentsOfFile:(NSString*)fileName
{
    return [[[self alloc] initWithContentsOfFile:fileName] autorelease];
}

+ (id)arrayWithObjects:(id*)objects count:(unsigned int)count
{
    return [[[self alloc] initWithObjects:objects count:count] autorelease];
}

- (NSArray*)arrayByAddingObject:(id)anObject
{
    int i, count = [self count];
    id *objects = Malloc(sizeof(id)*(count+1));
    id array;

    for (i = 0; i < count; i++)
	objects[i] = [self objectAtIndex:i];
    objects[i] = anObject;

    array = [[[NSArray alloc] initWithObjects:objects count:count+1] 
	autorelease];

    Free(objects);
    return array;
}

- (NSArray*)arrayByAddingObjectsFromArray:(NSArray*)anotherArray;
{
    unsigned int i, count = [self count], another = [anotherArray count];
    id array;
    id *objects = Malloc(sizeof(id)*(count+another));
    
    for (i=0; i<count; i++)
	objects[i] = [self objectAtIndex:i];
    for (i=0; i<another; i++)
	objects[i+count] = [anotherArray objectAtIndex:i];

    array = [[[NSArray alloc] initWithObjects:objects count:count+another]
	    autorelease];
    
    Free(objects);
    return array;
}

- (id)initWithArray:(NSArray*)anotherArray
{
    return [self initWithArray:anotherArray copyItems:NO];
}

- (id)initWithArray:(NSArray*)anotherArray copyItems:(BOOL)flag;
{
    unsigned int i, count = [anotherArray count];
    id* objects = Malloc(sizeof(id) * count);

    for (i = 0; i < count; i++)
	objects[i] = flag ? 
	    [anotherArray objectAtIndex:i] :
	    [[[anotherArray objectAtIndex:i] copyWithZone:NULL] autorelease];
    
    [self initWithObjects:objects count:count];
    Free(objects);

    return self;
}

- (id)initWithObjects:(id)firstObj,...
{
    id obj, *objects;
    va_list list;
    unsigned int count;

    va_start(list, firstObj);
    for (count=0, obj = firstObj; obj; obj = va_arg(list,id))
	count++;
    va_end(list);

    objects = Malloc(sizeof(id)*count);
    va_start(list, firstObj);
    for (count = 0, obj = firstObj; obj; obj = va_arg(list,id))
	objects[count++] = obj;
    va_end(list);

    [self initWithObjects:objects count:count];
    
    Free(objects);
    return self;
}

- (id)initWithObjects:(id*)objects count:(unsigned int)count
{
    [self subclassResponsibility:_cmd];
    return self;
}

- (id)initWithContentsOfFile:(NSString*)fileName
{
    volatile id plist = nil;
    
    TRY {
	plist = [[NSString stringWithContentsOfFile:fileName] propertyList];
	if (![plist isKindOfClass:[NSArray class]])
	    plist = nil;
    } END_TRY
    OTHERWISE {
	plist = nil;
    }
    END_CATCH
    
    if (plist == nil) {
	[self autorelease];
	return nil;
    }
    else
	return [self initWithArray:plist];
}

/* Querying the Array */

- (BOOL)containsObject:(id)anObject
{
    return ([self indexOfObject:anObject] == NSNotFound) ? NO : YES;
}

- (unsigned int)count
{
    [self subclassResponsibility:_cmd];
    return 0;
}

- (unsigned int)indexOfObject:(id)anObject
{
    return [self indexOfObject:anObject
	    inRange:NSMakeRange(0, [self count])];
}

- (unsigned int)indexOfObjectIdenticalTo:(id)anObject;
{
    return [self indexOfObjectIdenticalTo:anObject
	    inRange:NSMakeRange(0, [self count])];
}

- (unsigned int)indexOfObject:(id)anObject inRange:(NSRange)aRange
{
    unsigned int index;
    for (index = 0; index < aRange.length; index++)
	if ([anObject isEqual:[self objectAtIndex:aRange.location+index]])
	    return aRange.location+index;
    return NSNotFound;
}

- (unsigned int)indexOfObjectIdenticalTo:(id)anObject inRange:(NSRange)aRange
{
    unsigned int index;
    for (index = 0; index < aRange.length; index++)
	if (anObject == [self objectAtIndex:aRange.location+index])
	    return index;
    return NSNotFound;
}

- (id)lastObject
{
    return [self objectAtIndex:[self count]-1];
}

- (id)objectAtIndex:(unsigned int)index
{
    [self subclassResponsibility:_cmd];
    return self;
}

- (NSEnumerator*)objectEnumerator
{
    return [[[_NSArrayEnumerator alloc]
		initWithArray:self reverse:NO]
		autorelease];
}

- (NSEnumerator*)reverseObjectEnumerator
{
    return [[[_NSArrayEnumerator alloc]
		initWithArray:self reverse:YES]
		autorelease];
}

/* Sending Messages to Elements */

- (void)makeObjectsPerform:(SEL)aSelector
{
    unsigned int index, count = [self count];
    for (index = 0; index < count; index++)
	[[self objectAtIndex:index] perform:aSelector];
}

- (void)makeObjectsPerform:(SEL)aSelector withObject:(id)anObject;
{
    unsigned int index, count = [self count];
    for (index = 0; index < count; index++)
	[[self objectAtIndex:index] perform:aSelector withObject:anObject];
}

/* Comparing Arrays */

- (id)firstObjectCommonWithArray:(NSArray*)otherArray
{
    unsigned int index, count = [self count];
    for (index = 0; index < count; index++) {
	id object = [self objectAtIndex:index];
	if ([otherArray containsObject:object])
	    return object;
    }
    return nil;
}

- (BOOL)isEqualToArray:(NSArray*)otherArray
{
    unsigned int index, count;
    if( otherArray == self )
	return YES;
    if ([otherArray count] != (count = [self count]))
	return NO;
    for (index = 0; index < count; index++)
	if (![[self objectAtIndex:index] isEqual:
			[otherArray objectAtIndex:index]])
	    return NO;
    return YES;
}

/* Deriving New Arrays */

- (NSArray*)sortedArrayUsingFunction:
	    (int(*)(id element1, id element2, void *userData))comparator
    context:(void*)context
{
    id sortedArray = [[self mutableCopy] autorelease];

    [sortedArray sortUsingFunction:comparator context:context];
    return [[sortedArray copy] autorelease];
}

static int compare(id elem1, id elem2, void* comparator)
{
    return (int)[elem1 perform:comparator withObject:elem2];
}

- (NSArray*)sortedArrayUsingSelector:(SEL)comparator
    // Returns an array listing the receiver's elements in ascending order,
    // as determined by the comparison method specified by the selector 
    // comparator.
{
    return [self sortedArrayUsingFunction:compare context:(void*)comparator];
}

- (NSArray*)subarrayWithRange:(NSRange)range
{
    id array;
    unsigned int index;
    id *objects = Malloc(sizeof(id)*range.length);
    
    for (index = 0; index<range.length; index++)
	objects[index] = [self objectAtIndex:range.location+index];
	    
    array = [[[NSArray alloc] initWithObjects:objects count:range.length]
	    autorelease];
    
    Free(objects);
    return array;
}
    // Returns an array containing the receiver's elements
    // that fall within the limits specified by range.

/* Joining String Elements */

- (NSString*)componentsJoinedByString:(NSString*)separator
{
    unsigned int index, count = [self count];

    if(!separator)
	separator = @"";

    if(count) {
	NSMutableString* string = [NSMutableString new];
	id pool = [NSAutoreleasePool new];
	id elem = [self objectAtIndex:0];
	SEL sel = @selector(appendString:);
	IMP imp = [string methodForSelector:sel];

	if (![elem isKindOfClass:[NSString class]])
	    elem = [elem description];

	(*imp)(string, sel, elem);

	for (index = 1; index < count; index++) {
	    elem = [self objectAtIndex:index];
	    if (![elem isKindOfClass:[NSString class]])
		elem = [elem description];

	    (*imp)(string, sel, separator);
	    (*imp)(string, sel, elem);
	}
	[pool release];
	return [string autorelease];
    }

    return nil;
}

/* Creating a String Description of the Array */

- (NSString*)descriptionWithLocale:(NSDictionary*)locale
   indent:(unsigned int)indent;
{
    NSMutableString* description = [NSMutableString stringWithCString:"(\n"];
    unsigned int indent1 = indent + 4;
    NSMutableString* indentation
	    = [NSString stringWithFormat:
			[NSString stringWithFormat:@"%%%dc", indent1], ' '];
    unsigned int count = [self count];
    SEL sel = @selector(appendString:);
    IMP imp = [description methodForSelector:sel];

    if(count) {
	id pool = [NSAutoreleasePool new];
	id object;
	id stringRepresentation;
	unsigned int index;

	object = [self objectAtIndex:0];
	if ([object respondsToSelector:
		@selector(descriptionWithLocale:indent:)])
	    stringRepresentation = [object descriptionWithLocale:locale 
		indent:indent1];
	else if ([object
		respondsToSelector:@selector(descriptionWithLocale:)])
	    stringRepresentation = [object descriptionWithLocale:locale];
	else
	    stringRepresentation = [object stringRepresentation];

	(*imp)(description, sel, indentation);
	(*imp)(description, sel, stringRepresentation);

	for (index = 1; index < count; index++) {
	    object = [self objectAtIndex:index];
	    if ([object respondsToSelector:
		    @selector(descriptionWithLocale:indent:)])
		stringRepresentation = [object descriptionWithLocale:locale 
		    indent:indent1];
	    else if ([object
		    respondsToSelector:@selector(descriptionWithLocale:)])
		stringRepresentation = [object descriptionWithLocale:locale];
	    else
		stringRepresentation = [object stringRepresentation];

	    (*imp)(description, sel, @",\n");
	    (*imp)(description, sel, indentation);
	    (*imp)(description, sel, stringRepresentation);
	}
	[pool release];
    }

    (*imp)(description, sel, indent
	    ? [NSMutableString stringWithFormat:
			[NSString stringWithFormat:@"\n%%%dc)", indent], ' ']
	    : [NSMutableString stringWithCString:"\n)"]);
    return description;
}

- (NSString*)descriptionWithLocale:(NSDictionary*)locale
{
    return [self descriptionWithLocale:locale indent:0];
}

- (NSString*)description
{
    return [self descriptionWithLocale:nil indent:0];
}

- (NSString*)stringRepresentation
{
    return [self descriptionWithLocale:nil indent:0];
}

/* Write plist to file */

- (BOOL)writeToFile:(NSString*)fileName atomically:(BOOL)flag
{
    volatile BOOL success = NO;
    
    TRY {
	id content = [self description];
	success = [content writeToFile:fileName atomically:flag];
    } END_TRY
    OTHERWISE {
	success = NO;
    } END_CATCH
    
    return success;
}

/* From adopted/inherited protocols */

- (unsigned int)hash
{
    return [self count];
}

- (BOOL)isEqual:(id)anObject
{
    if ([anObject isKindOfClass:[NSArray class]] == NO)
	    return NO;
    return [self isEqualToArray:anObject];
}

/* Copying */

- (id)copyWithZone:(NSZone*)zone
{
    if (NSShouldRetainWithZone(self, zone))
	return [self retain];
    else
	return [[NSArray allocWithZone:zone] initWithArray:self copyItems:YES];
}

- (id)mutableCopyWithZone:(NSZone*)zone
{
    return [[NSMutableArray alloc] initWithArray:self];
}

/* Encoding */

- (Class)classForCoder
{
    return [NSArray class];
}

- (void)encodeWithCoder:(NSCoder*)aCoder
{
    IMP imp = [aCoder methodForSelector:@selector(encodeObject:)];
    int i, count = [self count];

    [aCoder encodeValueOfObjCType:@encode(int) at:&count];
    for(i = 0; i < count; i++)
	(*imp)(aCoder, @selector(encodeObject:), [self objectAtIndex:i]);
}

- (id)initWithCoder:(NSCoder*)aDecoder
{
    IMP imp = [aDecoder methodForSelector:@selector(decodeObject)];
    int i, count;
    id* objects;

    [aDecoder decodeValueOfObjCType:@encode(int) at:&count];
    objects = Malloc(sizeof(id) * count);

    for(i = 0; i < count; i++)
	objects[i] = (*imp)(aDecoder, @selector(decodeObject));

    [self initWithObjects:objects count:count];

    Free(objects);

    return self;
}

@end /* NSArray */

/*
 * Extensions to NSArray
 */

@implementation NSArray (NSArrayExtensions)

/* Sending Messages to Elements */

- (void)makeObjectsPerform:(SEL)aSelector
  withObject:(id)anObject1 withObject:(id)anObject2
{
    unsigned int index, count = [self count];
    for (index = 0; index < count; index++)
	[[self objectAtIndex:index]
		perform:aSelector withObject:anObject1 withObject:anObject2];
}

/* Deriving New Arrays */

- (NSArray*)map:(SEL)aSelector
{
    unsigned int index, count = [self count];
    id array = [NSMutableArray arrayWithCapacity:count];
    for (index = 0; index < count; index++)
	[array insertObject:[[self objectAtIndex:index] perform:aSelector]
		atIndex:index];
    return array;
}

- (NSArray*)map:(SEL)aSelector with:anObject
{
    unsigned int index, count = [self count];
    id array = [NSMutableArray arrayWithCapacity:count];
    for (index = 0; index < count; index++)
	[array insertObject:[[self objectAtIndex:index]
		perform:aSelector withObject:anObject]
		atIndex:index];
    return array;
}

- (NSArray*)map:(SEL)aSelector with:anObject with:otherObject;
{
    unsigned int index, count = [self count];
    id array = [NSMutableArray arrayWithCapacity:count];
    for (index = 0; index < count; index++)
	[array insertObject:[[self objectAtIndex:index]
		perform:aSelector withObject:anObject withObject:otherObject]
		atIndex:index];
    return array;
}

- (NSArray*)arrayWithObjectsThat:(BOOL(*)(id anObject))comparator;
    // Returns an array listing the receiver's elements for that comparator
    // function returns YES
{
    unsigned i, m, n = [self count];
    id *objects = Malloc(sizeof(id)*n);
    id array;
    
    for (i = m = 0; i < n; i++) {
	id obj = [self objectAtIndex:i];
	if (comparator(obj))
	    objects[m++] = obj;
    }
    
    array = [[[isa alloc] initWithObjects:objects count:m]
		autorelease];

    Free(objects);
    return array;
}

- (NSArray*)map:(id(*)(id anObject))function
  objectsThat:(BOOL(*)(id anObject))comparator;
    // Returns an array listing the objects returned by function applied to
    // objects for that comparator returns YES
{
    unsigned i, m, n = [self count];
    id *objects = Malloc(sizeof(id)*n);
    id array;
    
    for (i = m = 0; i < n; i++) {
	id obj = [self objectAtIndex:i];
	if (comparator(obj))
	    objects[m++] = function(obj);
    }
    
    array = [[[isa alloc] initWithObjects:objects count:m]
		autorelease];

    Free(objects);
    return array;
}

@end

/*
 * NSMutableArray class
 *
 * primary methods are
 *   init
 *   initWithCapacity:
 *   initWithObjects:count:
 *   dealloc
 *   count
 *   objectAtIndex:
 *   addObject:
 *   replaceObjectAtIndex:withObject:
 *   insertObject:atIndex:
 *   removeObjectAtIndex:
 */

@implementation NSMutableArray : NSArray

/* Creating and Initializing an NSMutableArray */

+ (id)allocWithZone:(NSZone*)zone
{
    return NSAllocateObject( (self == [NSMutableArray class]) ? 
		[NSConcreteMutableArray class] : self, 0, zone);
}

+ (id)arrayWithCapacity:(unsigned int)aNumItems
{
    return [[[self alloc] initWithCapacity:aNumItems] autorelease];
}

- (id)init
{
    return [self initWithCapacity:0];
}

- (id)initWithCapacity:(unsigned int)aNumItems
{
    [self subclassResponsibility:_cmd];
    return self;
}

- (id)copyWithZone:(NSZone*)zone
{
    return [[NSArray alloc] initWithArray:self copyItems:YES];
}

/* Adding Objects */

- (void)addObject:(id)anObject
{
    [self insertObject:anObject atIndex:[self count]];
}

- (void)addObjectsFromArray:(NSArray*)anotherArray
{
    unsigned int i, j, n;
    n = [anotherArray count];
    for (i = 0, j = [self count]; i < n; i++)
	[self insertObject:[anotherArray objectAtIndex:i] atIndex:j++];
}

- (void)insertObject:(id)anObject atIndex:(unsigned int)index
{
    [self subclassResponsibility:_cmd];
}

/* Removing Objects */

- (void)removeAllObjects
{
    int count = [self count];
    while (--count >= 0)
	[self removeObjectAtIndex:count];
}

- (void)removeLastObject
{
    [self removeObjectAtIndex:[self count]-1];
}

- (void)removeObject:(id)anObject
{
    unsigned int i, n;
    n = [self count];
    for (i = 0; i < n; i++) {
	id obj = [self objectAtIndex:i];
	if ([obj isEqual:anObject]) {
	    [self removeObjectAtIndex:i];
	    n--; i--;
	}
    }
}

- (void)removeObjectAtIndex:(unsigned int)index
{
    [self subclassResponsibility:_cmd];
}

- (void)removeObjectIdenticalTo:(id)anObject
{
    unsigned int i, n;
    i = n = [self count];
    for (i = 0; i < n; i++) {
	id obj = [self objectAtIndex:i];
	if (obj == anObject) {
	    [self removeObjectAtIndex:i];
	    n--; i--;
	}
    }
}

static int __cmp_unsigned_ints(unsigned int* i1, unsigned int* i2)
{
    return (*i1 == *i2) ? 0 : ((*i1 < *i2) ? -1 : +1);
}

- (void)removeObjectsFromIndices:(unsigned int*)indices
  numIndices:(unsigned int)count;
{
    unsigned int *indexes;
    int i;
    
    if (!count)
	return;
    
    indexes = Malloc(count*sizeof(unsigned int));
    memcpy(indexes, indices, count * sizeof(unsigned int));
    qsort(indexes, count, sizeof(unsigned int),
	(int(*)(const void *, const void*))__cmp_unsigned_ints);
		
    for (i = count-1; i>=0; i++)
	[self removeObjectAtIndex:indexes[i]];
    
    Free(indexes);
}

- (void)removeObjectsInArray:(NSArray*)otherArray
{
    unsigned int i, n = [otherArray count];
    for (i = 0; i < n; i++)
	[self removeObject:[otherArray objectAtIndex:i]];
}

- (void)removeObject:(id)anObject inRange:(NSRange)aRange
{
    unsigned int index;
    for (index = aRange.location-1; index >= aRange.location; index--)
	if ([anObject isEqual:[self objectAtIndex:index+aRange.location]])
	    [self removeObjectAtIndex:index+aRange.location];
}

- (void)removeObjectIdenticalTo:(id)anObject inRange:(NSRange)aRange
{
    unsigned int index;
    for (index = aRange.location-1; index >= aRange.location; index--)
	if (anObject == [self objectAtIndex:index+aRange.location])
	    [self removeObjectAtIndex:index+aRange.location];
}

- (void)removeObjectsInRange:(NSRange)aRange
{
    unsigned int index;
    for (index = aRange.location-1; index >= aRange.location; index--)
	[self removeObjectAtIndex:index+aRange.location];
}

/* Replacing Objects */

- (void)replaceObjectAtIndex:(unsigned int)index  withObject:(id)anObject
{
    [self subclassResponsibility:_cmd];
}

- (void)replaceObjectsInRange:(NSRange)rRange
  withObjectsFromArray:(NSArray*)anArray
{
    [self replaceObjectsInRange:rRange
	withObjectsFromArray:anArray
	range:NSMakeRange(0, [anArray count])];
}

- (void)replaceObjectsInRange:(NSRange)rRange
  withObjectsFromArray:(NSArray*)anArray range:(NSRange)aRange
{
    unsigned int index;
    [self removeObjectsInRange:rRange];
    for (index = 0; index < aRange.length; index++)
	[self insertObject:[anArray objectAtIndex:aRange.location+index]
	    atIndex:rRange.location+index];
}

- (void)setArray:(NSArray*)otherArray
{
    [self removeAllObjects];
    [self addObjectsFromArray:otherArray];
}

- (void)sortUsingFunction:
  (int(*)(id element1, id element2, void *userData))comparator
  context:(void*)context
{
    /* Shell sort algorithm taken from SortingInAction - a NeXT example */
#define STRIDE_FACTOR 3	// good value for stride factor is not well-understood
			// 3 is a fairly good choice (Sedgewick)
    int c,d, stride;
    BOOL found;
	int count = [self count];

    stride = 1;
    while (stride <= count)
    stride = stride * STRIDE_FACTOR + 1;
    
    while(stride > (STRIDE_FACTOR - 1)) {
	// loop to sort for each value of stride
	stride = stride / STRIDE_FACTOR;
	for (c = stride; c < count; c++) {
	    found = NO;
	    d = c - stride;
	    while ((d >= 0) && !found) {
		// move to left until correct place
		id a = [self objectAtIndex:d + stride];
		id b = [self objectAtIndex:d];
		if ((*comparator)(a, b, context) == NSOrderedAscending) {
		    [a retain];
		    [b retain];
		    [self replaceObjectAtIndex:d + stride withObject:b];
		    [self replaceObjectAtIndex:d withObject:a];
		    d -= stride;		// jump by stride factor
		    [a release];
		    [b release];
		}
		else found = YES;
	    }
	}
    }
}

static int selector_compare(id elem1, id elem2, void* comparator)
{
    return (int)[elem1 perform:(SEL)comparator withObject:elem2];
}

- (void)sortUsingSelector:(SEL)comparator
{
    [self sortUsingFunction:selector_compare context:(void*)comparator];
}

/* Encoding */
- (Class)classForCoder
{
    return [NSMutableArray class];
}

@end

/*
 * Extensions to NSArray
 */

@implementation NSMutableArray (NSMutableArrayExtensions)

- (void)removeObjectsFrom:(unsigned int)index count:(unsigned int)count
{
    if (count) {
	while (count--)
	[self removeObjectAtIndex:index+count];
    }
}

- (void)removeObjectsThat:(BOOL(*)(id anObject))comparator
{
    unsigned int index, count = [self count];
    for (index = 0; index < count; index++)
	if (comparator([self objectAtIndex:index])) {
	    [self removeObjectAtIndex:index];
	    index--; count--;
	}
}

@end /* NSMutableArray */

/*
 * NSArrayEnumerator class
 */

@implementation _NSArrayEnumerator 

- (id)initWithArray:(NSArray*)anArray reverse:(BOOL)isReverse
{
    unsigned count = [anArray count];

    reverse = isReverse;
    array = [anArray retain];
    index = (reverse) ? (count ? count - 1 : NSNotFound) : (count ? 0 : NSNotFound);
    return self;
}

- (void)dealloc
{
    [array release];
    [super dealloc];
}

- (id)nextObject
{
    id object;

    NSAssert(array, @"Invalid Array enumerator");
    if (index == NSNotFound)
	return nil;

    object = [array objectAtIndex:index];
    index += reverse ? -1 : +1;
    if (index == -1 || index >= [array count])
	index = NSNotFound;

    return object;
}

@end

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.