gc improvements

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@28234 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Richard Frith-MacDonald 2009-04-19 10:03:18 +00:00
parent 1be02d0252
commit 807d1b8a65
8 changed files with 1059 additions and 233 deletions

View file

@ -1,3 +1,18 @@
2009-04-19 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSHashTable.m:
* Source/NSConcreteHashTable.m:
* Source/NSMapTable.m:
* Source/NSConcreteMapTable.m:
* Headers/Additions/GNUstepBase/GSIMap.h:
Changes for GC zeroing weak pointers. Implement enumeration stuff for
new classes. Add code to support subclassing.
2009-04-18 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSArray.m: Implement the
([countByEnumeratingWithState:objects:count:]) method.
2009-04-18 10:15-EDT Gregory John Casamento <greg.casamento@gmail.com>
* Source/GSURLPrivate.h: Addition of private method to NSURLProtocol

View file

@ -84,6 +84,10 @@ extern "C" {
* GSI_MAP_NODES()
* Define this macro to allocate nodes for the map using typed
* memory when working with garbage collection.
*
* GSI_MAP_ZEROED()
* Define this macro to check whether a map uses keys which may
* be zeroed weak pointers. This is only used when GC is enabled.
*/
#ifndef GSI_MAP_HAS_VALUE
@ -112,6 +116,9 @@ extern "C" {
#define GSI_MAP_NODES(M, X) \
(GSIMapNode)NSAllocateCollectable(X*sizeof(GSIMapNode_t), NSScannedOption)
#endif
#ifndef GSI_MAP_ZEROED
#define GSI_MAP_ZEROED(M) 0
#endif
/*
* If there is no bitmask defined to supply the types that
@ -404,11 +411,63 @@ GSIMapRemoveNodeFromMap(GSIMapTable map, GSIMapBucket bkt, GSIMapNode node)
GSIMapRemoveNodeFromBucket(bkt, node);
}
static INLINE void
GSIMapFreeNode(GSIMapTable map, GSIMapNode node)
{
GSI_MAP_RELEASE_KEY(map, node->key);
GSI_MAP_CLEAR_KEY(node);
#if GSI_MAP_HAS_VALUE
GSI_MAP_RELEASE_VAL(map, node->value);
GSI_MAP_CLEAR_VAL(node);
#endif
node->nextInBucket = map->freeNodes;
map->freeNodes = node;
}
static INLINE GSIMapNode
GSIMapRemoveAndFreeNode(GSIMapTable map, size_t bkt, GSIMapNode node)
{
GSIMapNode next = node->nextInBucket;
GSIMapRemoveNodeFromMap(map, &(map->buckets[bkt]), node);
GSIMapFreeNode(map, node);
return next;
}
static INLINE void
GSIMapRemangleBuckets(GSIMapTable map,
GSIMapBucket old_buckets, size_t old_bucketCount,
GSIMapBucket new_buckets, size_t new_bucketCount)
{
#if GS_WITH_GC
if (GSI_MAP_ZEROED(map))
{
while (old_bucketCount-- > 0)
{
GSIMapNode node;
while ((node = old_buckets->firstNode) != 0)
{
if (node->key.addr == 0)
{
GSIMapRemoveNodeFromMap(map, old_buckets, node);
GSIMapFreeNode(map, node);
}
else
{
GSIMapBucket bkt;
GSIMapRemoveNodeFromBucket(old_buckets, node);
bkt = GSIMapPickBucket(GSI_MAP_HASH(map, node->key),
new_buckets, new_bucketCount);
GSIMapAddNodeToBucket(bkt, node);
}
}
old_buckets++;
}
return;
}
#endif
while (old_bucketCount-- > 0)
{
GSIMapNode node;
@ -489,25 +548,28 @@ GSIMapMoreNodes(GSIMapTable map, unsigned required)
}
}
static INLINE void
GSIMapFreeNode(GSIMapTable map, GSIMapNode node)
{
GSI_MAP_RELEASE_KEY(map, node->key);
GSI_MAP_CLEAR_KEY(node);
#if GSI_MAP_HAS_VALUE
GSI_MAP_RELEASE_VAL(map, node->value);
GSI_MAP_CLEAR_VAL(node);
#endif
node->nextInBucket = map->freeNodes;
map->freeNodes = node;
}
static INLINE GSIMapNode
GSIMapNodeForKeyInBucket(GSIMapTable map, GSIMapBucket bucket, GSIMapKey key)
{
GSIMapNode node = bucket->firstNode;
#if GS_WITH_GC
if (GSI_MAP_ZEROED(map))
{
while ((node != 0) && GSI_MAP_EQUAL(map, node->key, key) == NO)
{
GSIMapNode tmp = node->nextInBucket;
if (node->key.addr == 0)
{
GSIMapRemoveNodeFromMap(map, bucket, node);
GSIMapFreeNode(map, node);
}
node = tmp;
}
return node;
}
#endif
while ((node != 0) && GSI_MAP_EQUAL(map, node->key, key) == NO)
{
node = node->nextInBucket;
@ -548,6 +610,23 @@ GSIMapNodeForSimpleKey(GSIMapTable map, GSIMapKey key)
}
bucket = map->buckets + ((unsigned)key.addr) % map->bucketCount;
node = bucket->firstNode;
#if GS_WITH_GC
if (GSI_MAP_ZEROED(map))
{
while ((node != 0) && node->key.addr != key.addr)
{
GSIMapNode tmp = node->nextInBucket;
if (node->key.addr == 0)
{
GSIMapRemoveNodeFromMap(map, bucket, node);
GSIMapFreeNode(map, node);
}
node = tmp;
}
return node;
}
#endif
while ((node != 0) && node->key.addr != key.addr)
{
node = node->nextInBucket;
@ -657,12 +736,31 @@ GSIMapEnumeratorForMap(GSIMapTable map)
/*
* Locate next bucket and node to be returned.
*/
#if GS_WITH_GC
if (GSI_MAP_ZEROED(map))
{
while (enumerator.bucket < map->bucketCount)
{
GSIMapNode node = map->buckets[enumerator.bucket].firstNode;
while (node != 0 && node->key.addr == 0)
{
node = GSIMapRemoveAndFreeNode(map, enumerator.bucket, node);
}
if ((enumerator.node = node) != 0)
{
return enumerator;
}
enumerator.bucket++;
}
}
#endif
while (enumerator.bucket < map->bucketCount)
{
enumerator.node = map->buckets[enumerator.bucket].firstNode;
if (enumerator.node != 0)
{
break; // Got first node, and recorded its bucket.
return enumerator; // Got first node, and recorded its bucket.
}
enumerator.bucket++;
}
@ -707,16 +805,71 @@ GSIMapEnumeratorNextNode(GSIMapEnumerator enumerator)
{
GSIMapNode node = ((_GSIE)enumerator)->node;
#if GS_WITH_GC
/* Find the frst available non-zeroed node.
*/
if (node != 0 && GSI_MAP_ZEROED(map) && node->key.addr == 0)
{
GSIMapTable map = ((_GSIE)enumerator)->map;
size_t bucketCount = map->bucketCount;
size_t bucket = ((_GSIE)enumerator)->bucket;
while (node != 0 && node->key.addr == 0)
{
node = GSIMapRemoveAndFreeNode(map, bucket, node);
while (node == 0 && ++bucket < bucketCount)
{
node = (map->buckets[bucket]).firstNode;
while (node != 0 && node->key.addr == 0)
{
node = GSIMapRemoveAndFreeNode(map, bucket, node);
}
}
((_GSIE)enumerator)->bucket = bucket;
((_GSIE)enumerator)->node = node;
}
}
#endif
if (node != 0)
{
GSIMapNode next = node->nextInBucket;
#if GS_WITH_GC
if (GSI_MAP_ZEROED(map))
{
GSIMapTable map = ((_GSIE)enumerator)->map;
size_t bucket = ((_GSIE)enumerator)->bucket;
while (next != 0 && next->key.addr == 0)
{
next = GSIMapRemoveAndFreeNode(map, bucket, next);
}
}
#endif
if (next == 0)
{
GSIMapTable map = ((_GSIE)enumerator)->map;
size_t bucketCount = map->bucketCount;
size_t bucket = ((_GSIE)enumerator)->bucket;
#if GS_WITH_GC
if (GSI_MAP_ZEROED(map))
{
while (next == 0 && ++bucket < bucketCount)
{
next = (map->buckets[bucket]).firstNode;
while (next != 0 && next->key.addr == 0)
{
next = GSIMapRemoveAndFreeNode(map, bucket, next);
}
}
((_GSIE)enumerator)->bucket = bucket;
((_GSIE)enumerator)->node = next;
return node;
}
#endif
while (next == 0 && ++bucket < bucketCount)
{
next = (map->buckets[bucket]).firstNode;

View file

@ -912,7 +912,7 @@ static Class GSInlineArrayClass;
* get objects from after the end of the array. Don't pass negative values
* to memcpy.
*/
if (count >= 0)
if (count > 0)
{
memcpy(stackbuf, _contents_array + state->state, count * sizeof(id));
state->state += count;

View file

@ -386,6 +386,43 @@ static SEL rlSel;
return 0;
}
- (NSUInteger) countByEnumeratingWithState: (NSFastEnumerationState*)state
objects: (id*)stackbuf
count: (NSUInteger)len
{
NSUInteger size = [self count];
NSInteger count;
/* This is cached in the caller at the start and compared at each
* iteration. If it changes during the iteration then
* objc_enumerationMutation() will be called, throwing an exception.
*/
state->mutationsPtr = (unsigned long *)size;
count = MIN(len, size - state->state);
/* If a mutation has occurred then it's possible that we are being asked to
* get objects from after the end of the array. Don't pass negative values
* to memcpy.
*/
if (count > 0)
{
IMP imp = [self methodForSelector: @selector(objectAtIndex:)];
int p = state->state;
int i;
for (i = 0; i < count; i++, p++)
{
stackbuf[i] = (*imp)(self, @selector(objectAtIndex:), p);
}
state->state += count;
}
else
{
count = 0;
}
state->itemsPtr = stackbuf;
return count;
}
/**
* Encodes the receiver for storing to archive or sending over an
* [NSConnection].

View file

@ -40,6 +40,7 @@
#import "NSConcretePointerFunctions.h"
#import "NSCallBacks.h"
#import "GSPrivate.h"
static Class concreteClass = 0;
@ -62,6 +63,7 @@ typedef GSIMapNode_t *GSIMapNode;
GSIMapNode *nodeChunks; /* Chunks of allocated memory. */
size_t chunkCount; /* Number of chunks in array. */
size_t increment; /* Amount to grow by. */
unsigned long version; /* For fast enumeration. */
BOOL legacy; /* old style callbacks? */
union {
PFInfo pf;
@ -107,29 +109,7 @@ static GC_descr nodeW = 0;
NSArray *
NSAllHashTableObjects(NSHashTable *table)
{
NSMutableArray *array;
NSHashEnumerator enumerator;
id element;
if (table == nil)
{
NSWarnFLog(@"Null table argument supplied");
return nil;
}
/* Create our mutable key array. */
array = [NSMutableArray arrayWithCapacity: NSCountHashTable(table)];
/* Get an enumerator for TABLE. */
enumerator = NSEnumerateHashTable(table);
/* Step through TABLE... */
while ((element = NSNextHashEnumeratorItem(&enumerator)) != nil)
{
[array addObject: element];
}
NSEndHashTableEnumeration(&enumerator);
return array;
return [table allObjects];
}
/**
@ -280,12 +260,7 @@ NSCopyHashTableWithZone(NSHashTable *table, NSZone *zone)
unsigned int
NSCountHashTable(NSHashTable *table)
{
if (table == nil)
{
NSWarnFLog(@"Null table argument supplied");
return 0;
}
return ((GSIMapTable)table)->nodeCount;
return [table count];
}
/**
@ -361,7 +336,15 @@ NSEndHashTableEnumeration(NSHashEnumerator *enumerator)
NSWarnFLog(@"Null enumerator argument supplied");
return;
}
GSIMapEndEnumerator((GSIMapEnumerator)enumerator);
if (enumerator->map != 0)
{
GSIMapEndEnumerator((GSIMapEnumerator)enumerator);
}
else if (enumerator->node != 0)
{
[(id)enumerator->node release];
memset(enumerator, '\0', sizeof(GSIMapEnumerator));
}
}
/**
@ -378,7 +361,17 @@ NSEnumerateHashTable(NSHashTable *table)
NSWarnFLog(@"Null table argument supplied");
return v;
}
return GSIMapEnumeratorForMap((GSIMapTable)table);
if (GSObjCClass(table) == concreteClass)
{
return GSIMapEnumeratorForMap((GSIMapTable)table);
}
else
{
NSHashEnumerator v = {0, 0, 0};
v.node = (void*)[[table objectEnumerator] retain];
return v;
}
}
/**
@ -390,14 +383,7 @@ NSEnumerateHashTable(NSHashTable *table)
void
NSFreeHashTable(NSHashTable *table)
{
if (table == nil)
{
NSWarnFLog(@"Null table argument supplied");
}
else
{
[table release];
}
[table release];
}
/**
@ -407,22 +393,26 @@ NSFreeHashTable(NSHashTable *table)
void *
NSHashGet(NSHashTable *table, const void *element)
{
GSIMapNode n;
if (table == nil)
{
NSWarnFLog(@"Null table argument supplied");
return 0;
}
n = GSIMapNodeForKey((GSIMapTable)table, (GSIMapKey)element);
if (n == 0)
if (GSObjCClass(table) == concreteClass)
{
return 0;
}
else
{
return n->key.ptr;
GSIMapNode n;
n = GSIMapNodeForKey((GSIMapTable)table, (GSIMapKey)element);
if (n == 0)
{
return 0;
}
else
{
return n->key.ptr;
}
}
return [table member: (id)element];
}
/**
@ -433,9 +423,6 @@ NSHashGet(NSHashTable *table, const void *element)
void
NSHashInsert(NSHashTable *table, const void *element)
{
GSIMapTable t = (GSIMapTable)table;
GSIMapNode n;
if (table == 0)
{
[NSException raise: NSInvalidArgumentException
@ -446,16 +433,28 @@ NSHashInsert(NSHashTable *table, const void *element)
[NSException raise: NSInvalidArgumentException
format: @"Attempt to place null in hash table"];
}
n = GSIMapNodeForKey(t, (GSIMapKey)element);
if (n == 0)
if (GSObjCClass(table) == concreteClass)
{
GSIMapAddKey(t, (GSIMapKey)element);
GSIMapTable t = (GSIMapTable)table;
GSIMapNode n;
n = GSIMapNodeForKey(t, (GSIMapKey)element);
if (n == 0)
{
GSIMapAddKey(t, (GSIMapKey)element);
((NSConcreteHashTable*)table)->version++;
}
else if (element != n->key.ptr)
{
GSI_MAP_RELEASE_KEY(t, n->key);
n->key = (GSIMapKey)element;
GSI_MAP_RETAIN_KEY(t, n->key);
((NSConcreteHashTable*)table)->version++;
}
}
else
{
GSI_MAP_RELEASE_KEY(t, n->key);
n->key = (GSIMapKey)element;
GSI_MAP_RETAIN_KEY(t, n->key);
[table addObject: (id)element];
}
}
@ -468,9 +467,6 @@ NSHashInsert(NSHashTable *table, const void *element)
void *
NSHashInsertIfAbsent(NSHashTable *table, const void *element)
{
GSIMapTable t = (GSIMapTable)table;
GSIMapNode n;
if (table == 0)
{
[NSException raise: NSInvalidArgumentException
@ -481,15 +477,36 @@ NSHashInsertIfAbsent(NSHashTable *table, const void *element)
[NSException raise: NSInvalidArgumentException
format: @"Attempt to place null in hash table"];
}
n = GSIMapNodeForKey(t, (GSIMapKey)element);
if (n == 0)
if (GSObjCClass(table) == concreteClass)
{
GSIMapAddKey(t, (GSIMapKey)element);
return 0;
GSIMapTable t = (GSIMapTable)table;
GSIMapNode n;
n = GSIMapNodeForKey(t, (GSIMapKey)element);
if (n == 0)
{
GSIMapAddKey(t, (GSIMapKey)element);
((NSConcreteHashTable*)table)->version++;
return 0;
}
else
{
return n->key.ptr;
}
}
else
{
return n->key.ptr;
id old = [table member: (id)element];
if (old == nil)
{
[table addObject: (id)element];
return 0;
}
else
{
return (void*)old;
}
}
}
@ -501,9 +518,6 @@ NSHashInsertIfAbsent(NSHashTable *table, const void *element)
void
NSHashInsertKnownAbsent(NSHashTable *table, const void *element)
{
GSIMapTable t = (GSIMapTable)table;
GSIMapNode n;
if (table == 0)
{
[NSException raise: NSInvalidArgumentException
@ -514,15 +528,36 @@ NSHashInsertKnownAbsent(NSHashTable *table, const void *element)
[NSException raise: NSInvalidArgumentException
format: @"Attempt to place null in hash table"];
}
n = GSIMapNodeForKey(t, (GSIMapKey)element);
if (n == 0)
if (GSObjCClass(table) == concreteClass)
{
GSIMapAddKey(t, (GSIMapKey)element);
GSIMapTable t = (GSIMapTable)table;
GSIMapNode n;
n = GSIMapNodeForKey(t, (GSIMapKey)element);
if (n == 0)
{
GSIMapAddKey(t, (GSIMapKey)element);
((NSConcreteHashTable*)table)->version++;
}
else
{
[NSException raise: NSInvalidArgumentException
format: @"NSHashInsertKnownAbsent ... item not absent"];
}
}
else
{
[NSException raise: NSInvalidArgumentException
format: @"NSHashInsertKnownAbsent ... element not absent"];
id old = [table member: (id)element];
if (old == nil)
{
[table addObject: (id)element];
}
else
{
[NSException raise: NSInvalidArgumentException
format: @"NSHashInsertKnownAbsent ... item not absent"];
}
}
}
@ -535,10 +570,26 @@ NSHashRemove(NSHashTable *table, const void *element)
if (table == 0)
{
NSWarnFLog(@"Nul table argument supplied");
return;
}
if (GSObjCClass(table) == concreteClass)
{
GSIMapTable map = (GSIMapTable)table;
GSIMapBucket bucket;
GSIMapNode node;
bucket = GSIMapBucketForKey(map, (GSIMapKey)element);
node = GSIMapNodeForKeyInBucket(map, bucket, (GSIMapKey)element);
if (node != 0)
{
GSIMapRemoveNodeFromMap(map, bucket, node);
GSIMapFreeNode(map, node);
((NSConcreteHashTable*)table)->version++;
}
}
else
{
GSIMapRemoveKey((GSIMapTable)table, (GSIMapKey)element);
[table removeObject: (id)element];
}
}
@ -549,21 +600,32 @@ NSHashRemove(NSHashTable *table, const void *element)
void *
NSNextHashEnumeratorItem(NSHashEnumerator *enumerator)
{
GSIMapNode n;
if (enumerator == 0)
{
NSWarnFLog(@"Nul enumerator argument supplied");
return 0;
}
n = GSIMapEnumeratorNextNode((GSIMapEnumerator)enumerator);
if (n == 0)
if (enumerator->map != 0) // Got a GSIMapTable enumerator
{
return 0;
GSIMapNode n;
n = GSIMapEnumeratorNextNode((GSIMapEnumerator)enumerator);
if (n == 0)
{
return 0;
}
else
{
return n->key.ptr;
}
}
else if (enumerator->node != 0) // Got an enumerator object
{
return (void*)[(id)enumerator->node nextObject];
}
else
{
return n->key.ptr;
return 0;
}
}
@ -576,10 +638,21 @@ NSResetHashTable(NSHashTable *table)
if (table == 0)
{
NSWarnFLog(@"Nul table argument supplied");
return;
}
if (GSObjCClass(table) == concreteClass)
{
NSConcreteHashTable *t = (NSConcreteHashTable*)table;
if (t->nodeCount > 0)
{
GSIMapCleanMap((GSIMapTable)table);
t->version++;
}
}
else
{
GSIMapCleanMap((GSIMapTable)table);
[table removeAllObjects];
}
}
@ -704,6 +777,14 @@ const NSHashTableCallBacks NSPointerToStructHashCallBacks =
@interface NSConcreteHashTableEnumerator : NSEnumerator
{
NSConcreteHashTable *table;
GSIMapEnumerator_t enumerator;
}
- (id) initWithHashTable: (NSConcreteHashTable*)t;
@end
@implementation NSConcreteHashTable
+ (void) initialize
@ -735,15 +816,36 @@ const NSHashTableCallBacks NSPointerToStructHashCallBacks =
if (n == 0)
{
GSIMapAddKey(t, (GSIMapKey)anObject);
version++;
}
else if (n->key.obj != anObject)
{
GSI_MAP_RELEASE_KEY(t, n->key);
n->key = (GSIMapKey)anObject;
GSI_MAP_RETAIN_KEY(t, n->key);
version++;
}
}
- (NSArray*) allObjects
{
NSHashEnumerator enumerator;
unsigned index;
NSArray *a;
GS_BEGINITEMBUF(objects, nodeCount, id);
enumerator = NSEnumerateHashTable(self);
index = 0;
while ((objects[index] = NSNextHashEnumeratorItem(&enumerator)) != nil)
{
index++;
}
NSEndHashTableEnumeration(&enumerator);
a = [[[NSArray alloc] initWithObjects: objects count: nodeCount] autorelease];
GS_ENDITEMBUF();
return a;
}
- (id) copyWithZone: (NSZone*)aZone
{
return NSCopyHashTableWithZone(self, aZone);
@ -758,7 +860,49 @@ const NSHashTableCallBacks NSPointerToStructHashCallBacks =
objects: (id*)stackbuf
count: (NSUInteger)len
{
return (NSUInteger)[self subclassResponsibility: _cmd];
NSInteger count;
state->mutationsPtr = (unsigned long *)version;
if (state->state == 0 && state->extra[0] == 0)
{
while (state->extra[0] < bucketCount)
{
state->state = (unsigned long)buckets[state->extra[0]].firstNode;
if (state->state != 0)
{
break; // Got first node, and recorded its bucket.
}
state->extra[0]++;
}
}
for (count = 0; count < len; count++)
{
GSIMapNode node = (GSIMapNode)state->state;
if (node == 0)
{
break;
}
else
{
GSIMapNode next = node->nextInBucket;
if (next == 0)
{
size_t bucket = state->extra[0];
while (next == 0 && ++bucket < bucketCount)
{
next = buckets[bucket].firstNode;
}
state->extra[0] = bucket;
}
state->state = (unsigned long)(uintptr_t)next;
stackbuf[count] = node->key.obj;
}
}
state->itemsPtr = stackbuf;
return count;
}
- (void) dealloc
@ -826,7 +970,10 @@ const NSHashTableCallBacks NSPointerToStructHashCallBacks =
- (NSEnumerator*) objectEnumerator
{
return [self subclassResponsibility: _cmd];
NSEnumerator *e;
e = [[NSConcreteHashTableEnumerator alloc] initWithHashTable: self];
return [e autorelease];
}
- (id) member: (id)aKey
@ -853,12 +1000,64 @@ const NSHashTableCallBacks NSPointerToStructHashCallBacks =
- (void) removeAllObjects
{
GSIMapEmptyMap(self);
if (nodeCount > 0)
{
GSIMapCleanMap(self);
version++;
}
}
- (NSSet*) setRepresentation
- (void) removeObject: (id)anObject
{
return [self subclassResponsibility: _cmd];
if (anObject == nil)
{
NSWarnMLog(@"attempt to remove nil object from hash table %@", self);
return;
}
if (nodeCount > 0)
{
GSIMapTable map = (GSIMapTable)self;
GSIMapBucket bucket;
GSIMapNode node;
bucket = GSIMapBucketForKey(map, (GSIMapKey)anObject);
node = GSIMapNodeForKeyInBucket(map, bucket, (GSIMapKey)anObject);
if (node != 0)
{
GSIMapRemoveNodeFromMap(map, bucket, node);
GSIMapFreeNode(map, node);
version++;
}
}
}
@end
@implementation NSConcreteHashTableEnumerator
- (id) initWithHashTable: (NSConcreteHashTable*)t
{
table = RETAIN(t);
enumerator = GSIMapEnumeratorForMap(table);
return self;
}
- (id) nextObject
{
GSIMapNode node = GSIMapEnumeratorNextNode(&enumerator);
if (node == 0)
{
return nil;
}
return node->key.obj;
}
- (void) dealloc
{
GSIMapEndEnumerator(&enumerator);
RELEASE(table);
[super dealloc];
}
@end

View file

@ -62,6 +62,7 @@ typedef GSIMapNode_t *GSIMapNode;
GSIMapNode *nodeChunks; /* Chunks of allocated memory. */
size_t chunkCount; /* Number of chunks in array. */
size_t increment; /* Amount to grow by. */
unsigned long version; /* For fast enumeration. */
BOOL legacy; /* old style callbacks? */
union {
struct {
@ -329,7 +330,6 @@ NSCopyMapTableWithZone(NSMapTable *table, NSZone *zone)
GSIMapTable o = (GSIMapTable)table;
GSIMapTable t;
GSIMapNode n;
NSMapEnumerator enumerator;
if (table == nil)
{
@ -353,12 +353,28 @@ NSCopyMapTableWithZone(NSMapTable *table, NSZone *zone)
#endif
GSIMapInitWithZoneAndCapacity(t, zone, ((GSIMapTable)table)->nodeCount);
enumerator = GSIMapEnumeratorForMap((GSIMapTable)table);
while ((n = GSIMapEnumeratorNextNode(&enumerator)) != 0)
if (GSObjCClass(table) == concreteClass)
{
GSIMapAddPair(t, n->key, n->value);
NSMapEnumerator enumerator;
enumerator = GSIMapEnumeratorForMap((GSIMapTable)table);
while ((n = GSIMapEnumeratorNextNode(&enumerator)) != 0)
{
GSIMapAddPair(t, n->key, n->value);
}
GSIMapEndEnumerator((GSIMapEnumerator)&enumerator);
}
else
{
NSEnumerator *enumerator;
id k;
enumerator = [table keyEnumerator];
while ((k = [enumerator nextObject]) != nil)
{
GSIMapAddPair(t, (GSIMapKey)k, (GSIMapVal)[table objectForKey: k]);
}
}
GSIMapEndEnumerator((GSIMapEnumerator)&enumerator);
return (NSMapTable*)t;
}
@ -374,7 +390,11 @@ NSCountMapTable(NSMapTable *table)
NSWarnFLog(@"Null table argument supplied");
return 0;
}
return ((GSIMapTable)table)->nodeCount;
if (GSObjCClass(table) == concreteClass)
{
return ((GSIMapTable)table)->nodeCount;
}
return [table count];
}
/**
@ -461,7 +481,15 @@ NSEndMapTableEnumeration(NSMapEnumerator *enumerator)
NSWarnFLog(@"Null enumerator argument supplied");
return;
}
GSIMapEndEnumerator((GSIMapEnumerator)enumerator);
if (enumerator->map != 0)
{
GSIMapEndEnumerator((GSIMapEnumerator)enumerator);
}
else if (enumerator->node != 0)
{
[(id)enumerator->node release];
memset(enumerator, '\0', sizeof(GSIMapEnumerator));
}
}
/**
@ -473,12 +501,23 @@ NSEnumerateMapTable(NSMapTable *table)
{
if (table == nil)
{
NSMapEnumerator v = {0, 0};
NSMapEnumerator v = {0, 0, 0};
NSWarnFLog(@"Null table argument supplied");
return v;
}
return GSIMapEnumeratorForMap((GSIMapTable)table);
if (GSObjCClass(table) == concreteClass)
{
return GSIMapEnumeratorForMap((GSIMapTable)table);
}
else
{
NSMapEnumerator v = {0, 0, 0};
v.node = (void*)[[table keyEnumerator] retain];
v.bucket = (unsigned long)(uintptr_t)table;
return v;
}
}
/**
@ -507,21 +546,28 @@ NSFreeMapTable(NSMapTable *table)
void *
NSMapGet(NSMapTable *table, const void *key)
{
GSIMapNode n;
if (table == nil)
{
NSWarnFLog(@"Null table argument supplied");
return 0;
}
n = GSIMapNodeForKey((GSIMapTable)table, (GSIMapKey)key);
if (n == 0)
if (GSObjCClass(table) == concreteClass)
{
return 0;
GSIMapNode n;
n = GSIMapNodeForKey((GSIMapTable)table, (GSIMapKey)key);
if (n == 0)
{
return 0;
}
else
{
return n->value.ptr;
}
}
else
{
return n->value.ptr;
return [table objectForKey: (id)key];
}
}
@ -535,31 +581,48 @@ NSMapGet(NSMapTable *table, const void *key)
void
NSMapInsert(NSMapTable *table, const void *key, const void *value)
{
GSIMapTable t = (GSIMapTable)table;
GSIMapNode n;
if (table == nil)
{
[NSException raise: NSInvalidArgumentException
format: @"Attempt to place key-value in null table"];
}
if (key == t->cb.old.k.notAKeyMarker)
if (GSObjCClass(table) == concreteClass)
{
[NSException raise: NSInvalidArgumentException
format: @"Attempt to place notAKeyMarker in map table"];
}
n = GSIMapNodeForKey(t, (GSIMapKey)key);
if (n == 0)
{
GSIMapAddPair(t, (GSIMapKey)key, (GSIMapVal)value);
GSIMapTable t = (GSIMapTable)table;
GSIMapNode n;
if (t->legacy == YES)
{
if (key == t->cb.old.k.notAKeyMarker)
{
[NSException raise: NSInvalidArgumentException
format: @"Attempt to place notAKeyMarker in map"];
}
}
else if (key == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"Attempt to place nil key in map"];
}
n = GSIMapNodeForKey(t, (GSIMapKey)key);
if (n == 0)
{
GSIMapAddPair(t, (GSIMapKey)key, (GSIMapVal)value);
t->version++;
}
else if (n->value.ptr != value)
{
GSIMapVal tmp = n->value;
n->value = (GSIMapVal)value;
GSI_MAP_RETAIN_VAL(t, n->value);
GSI_MAP_RELEASE_VAL(t, tmp);
t->version++;
}
}
else
{
GSIMapVal tmp = n->value;
n->value = (GSIMapVal)value;
GSI_MAP_RETAIN_VAL(t, n->value);
GSI_MAP_RELEASE_VAL(t, tmp);
[table setObject: (id)value forKey: (id)key];
}
}
@ -573,28 +636,51 @@ NSMapInsert(NSMapTable *table, const void *key, const void *value)
void *
NSMapInsertIfAbsent(NSMapTable *table, const void *key, const void *value)
{
GSIMapTable t = (GSIMapTable)table;
GSIMapNode n;
if (table == nil)
{
[NSException raise: NSInvalidArgumentException
format: @"Attempt to place key-value in null table"];
}
if (key == t->cb.old.k.notAKeyMarker)
if (GSObjCClass(table) == concreteClass)
{
[NSException raise: NSInvalidArgumentException
format: @"Attempt to place notAKeyMarker in map table"];
}
n = GSIMapNodeForKey(t, (GSIMapKey)key);
if (n == 0)
{
GSIMapAddPair(t, (GSIMapKey)key, (GSIMapVal)value);
return 0;
GSIMapTable t = (GSIMapTable)table;
GSIMapNode n;
if (t->legacy == YES)
{
if (key == t->cb.old.k.notAKeyMarker)
{
[NSException raise: NSInvalidArgumentException
format: @"Attempt to place notAKeyMarker in map table"];
}
}
else if (key == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"Attempt to place nil key in map"];
}
n = GSIMapNodeForKey(t, (GSIMapKey)key);
if (n == 0)
{
GSIMapAddPair(t, (GSIMapKey)key, (GSIMapVal)value);
t->version++;
return 0;
}
else
{
return n->key.ptr;
}
}
else
{
return n->key.ptr;
void *v = (void*)[table objectForKey: (id)key];
if (v == 0)
{
[table setObject: (id)value forKey: (id)v];
return 0;
}
return v;
}
}
@ -607,28 +693,54 @@ NSMapInsertIfAbsent(NSMapTable *table, const void *key, const void *value)
void
NSMapInsertKnownAbsent(NSMapTable *table, const void *key, const void *value)
{
GSIMapTable t = (GSIMapTable)table;
GSIMapNode n;
if (table == nil)
{
[NSException raise: NSInvalidArgumentException
format: @"Attempt to place key-value in null table"];
}
if (key == t->cb.old.k.notAKeyMarker)
if (GSObjCClass(table) == concreteClass)
{
[NSException raise: NSInvalidArgumentException
format: @"Attempt to place notAKeyMarker in map table"];
}
n = GSIMapNodeForKey(t, (GSIMapKey)key);
if (n == 0)
{
GSIMapAddPair(t, (GSIMapKey)key, (GSIMapVal)value);
GSIMapTable t = (GSIMapTable)table;
GSIMapNode n;
if (t->legacy == YES)
{
if (key == t->cb.old.k.notAKeyMarker)
{
[NSException raise: NSInvalidArgumentException
format: @"Attempt to place notAKeyMarker in map table"];
}
}
else if (key == 0)
{
[NSException raise: NSInvalidArgumentException
format: @"Attempt to place nil key in map"];
}
n = GSIMapNodeForKey(t, (GSIMapKey)key);
if (n == 0)
{
GSIMapAddPair(t, (GSIMapKey)key, (GSIMapVal)value);
t->version++;
}
else
{
[NSException raise: NSInvalidArgumentException
format: @"NSMapInsertKnownAbsent ... key not absent"];
}
}
else
{
[NSException raise: NSInvalidArgumentException
format: @"NSMapInsertKnownAbsent ... key not absent"];
void *v = (void*)[table objectForKey: (id)key];
if (v == 0)
{
[table setObject: (id)value forKey: (id)v];
}
else
{
[NSException raise: NSInvalidArgumentException
format: @"NSMapInsertKnownAbsent ... key not absent"];
}
}
}
@ -642,29 +754,36 @@ BOOL
NSMapMember(NSMapTable *table, const void *key,
void **originalKey, void **value)
{
GSIMapNode n;
if (table == nil)
{
NSWarnFLog(@"Null table argument supplied");
return NO;
}
n = GSIMapNodeForKey((GSIMapTable)table, (GSIMapKey)key);
if (n == 0)
if (GSObjCClass(table) == concreteClass)
{
return NO;
GSIMapNode n;
n = GSIMapNodeForKey((GSIMapTable)table, (GSIMapKey)key);
if (n == 0)
{
return NO;
}
else
{
if (originalKey != 0)
{
*originalKey = n->key.ptr;
}
if (value != 0)
{
*value = n->value.ptr;
}
return YES;
}
}
else
{
if (originalKey != 0)
{
*originalKey = n->key.ptr;
}
if (value != 0)
{
*value = n->value.ptr;
}
return YES;
return [table objectForKey: (id)key] ? YES : NO;
}
}
@ -680,7 +799,18 @@ NSMapRemove(NSMapTable *table, const void *key)
NSWarnFLog(@"Null table argument supplied");
return;
}
GSIMapRemoveKey((GSIMapTable)table, (GSIMapKey)key);
if (GSObjCClass(table) == concreteClass)
{
if (((GSIMapTable)table)->nodeCount > 0)
{
GSIMapRemoveKey((GSIMapTable)table, (GSIMapKey)key);
((GSIMapTable)table)->version++;
}
}
else
{
[table removeObjectForKey: (id)key];
}
}
/**
@ -696,32 +826,61 @@ BOOL
NSNextMapEnumeratorPair(NSMapEnumerator *enumerator,
void **key, void **value)
{
GSIMapNode n;
if (enumerator == 0)
{
NSWarnFLog(@"Null enumerator argument supplied");
return NO;
}
n = GSIMapEnumeratorNextNode((GSIMapEnumerator)enumerator);
if (n == 0)
if (enumerator->map != 0)
{
return NO;
GSIMapNode n;
n = GSIMapEnumeratorNextNode((GSIMapEnumerator)enumerator);
if (n == 0)
{
return NO;
}
else
{
if (key != 0)
{
*key = n->key.ptr;
}
else
{
NSWarnFLog(@"Null key return address");
}
if (value != 0)
{
*value = n->value.ptr;
}
else
{
NSWarnFLog(@"Null value return address");
}
return YES;
}
}
else
else if (enumerator->node != 0)
{
id k = [(NSEnumerator*)enumerator->node nextObject];
if (k == nil)
{
return NO;
}
if (key != 0)
{
*key = n->key.ptr;
*key = k;
}
else
{
NSWarnFLog(@"Null key return address");
}
if (value != 0)
{
*value = n->value.ptr;
*value = [(NSMapTable*)enumerator->bucket objectForKey: k];
}
else
{
@ -729,6 +888,10 @@ NSNextMapEnumeratorPair(NSMapEnumerator *enumerator,
}
return YES;
}
else
{
return NO;
}
}
/**
@ -741,10 +904,19 @@ NSResetMapTable(NSMapTable *table)
if (table == nil)
{
NSWarnFLog(@"Null table argument supplied");
return;
}
if (GSObjCClass(table) == concreteClass)
{
if (((GSIMapTable)table)->nodeCount > 0)
{
GSIMapCleanMap((GSIMapTable)table);
((GSIMapTable)table)->version++;
}
}
else
{
GSIMapCleanMap((GSIMapTable)table);
[table removeAllObjects];
}
}
@ -757,44 +929,51 @@ NSResetMapTable(NSMapTable *table)
NSString *
NSStringFromMapTable(NSMapTable *table)
{
GSIMapTable t = (GSIMapTable)table;
NSMutableString *string;
NSMapEnumerator enumerator;
void *key;
void *value;
if (table == nil)
{
NSWarnFLog(@"Null table argument supplied");
return nil;
}
string = [NSMutableString stringWithCapacity: 0];
enumerator = NSEnumerateMapTable(table);
/*
* Now, just step through the elements of the table, and add their
* descriptions to the string.
*/
if (t->legacy)
if (GSObjCClass(table) == concreteClass)
{
while (NSNextMapEnumeratorPair(&enumerator, &key, &value) == YES)
GSIMapTable t = (GSIMapTable)table;
NSMutableString *string;
NSMapEnumerator enumerator;
void *key;
void *value;
string = [NSMutableString stringWithCapacity: 0];
enumerator = NSEnumerateMapTable(table);
/*
* Now, just step through the elements of the table, and add their
* descriptions to the string.
*/
if (t->legacy)
{
[string appendFormat: @"%@ = %@;\n",
(t->cb.old.k.describe)(table, key),
(t->cb.old.v.describe)(table, value)];
while (NSNextMapEnumeratorPair(&enumerator, &key, &value) == YES)
{
[string appendFormat: @"%@ = %@;\n",
(t->cb.old.k.describe)(table, key),
(t->cb.old.v.describe)(table, value)];
}
}
else
{
while (NSNextMapEnumeratorPair(&enumerator, &key, &value) == YES)
{
[string appendFormat: @"%@ = %@;\n",
(t->cb.pf.k.descriptionFunction)(key),
(t->cb.pf.v.descriptionFunction)(value)];
}
}
NSEndMapTableEnumeration(&enumerator);
return string;
}
else
{
while (NSNextMapEnumeratorPair(&enumerator, &key, &value) == YES)
{
[string appendFormat: @"%@ = %@;\n",
(t->cb.pf.k.descriptionFunction)(key),
(t->cb.pf.v.descriptionFunction)(value)];
}
return [table description];
}
NSEndMapTableEnumeration(&enumerator);
return string;
}
@ -916,6 +1095,17 @@ const NSMapTableValueCallBacks NSOwnedPointerMapValueCallBacks =
@interface NSConcreteMapTableKeyEnumerator : NSEnumerator
{
NSConcreteMapTable *table;
GSIMapEnumerator_t enumerator;
}
- (id) initWithMapTable: (NSConcreteMapTable*)m;
@end
@interface NSConcreteMapTableObjectEnumerator : NSConcreteMapTableKeyEnumerator
@end
@implementation NSConcreteMapTable
+ (void) initialize
@ -957,7 +1147,49 @@ const NSMapTableValueCallBacks NSOwnedPointerMapValueCallBacks =
objects: (id*)stackbuf
count: (NSUInteger)len
{
return (NSUInteger)[self subclassResponsibility: _cmd];
NSInteger count;
state->mutationsPtr = (unsigned long *)version;
if (state->state == 0 && state->extra[0] == 0)
{
while (state->extra[0] < bucketCount)
{
state->state = (unsigned long)buckets[state->extra[0]].firstNode;
if (state->state != 0)
{
break; // Got first node, and recorded its bucket.
}
state->extra[0]++;
}
}
for (count = 0; count < len; count++)
{
GSIMapNode node = (GSIMapNode)state->state;
if (node == 0)
{
break;
}
else
{
GSIMapNode next = node->nextInBucket;
if (next == 0)
{
size_t bucket = state->extra[0];
while (next == 0 && ++bucket < bucketCount)
{
next = buckets[bucket].firstNode;
}
state->extra[0] = bucket;
}
state->state = (unsigned long)(uintptr_t)next;
stackbuf[count] = node->key.obj;
}
}
state->itemsPtr = stackbuf;
return count;
}
- (void) dealloc
@ -966,11 +1198,6 @@ const NSMapTableValueCallBacks NSOwnedPointerMapValueCallBacks =
[super dealloc];
}
- (NSDictionary*) dictionaryRepresentation
{
return [self subclassResponsibility: _cmd];
}
- (void) encodeWithCoder: (NSCoder*)aCoder
{
[self subclassResponsibility: _cmd];
@ -1060,7 +1287,10 @@ const NSMapTableValueCallBacks NSOwnedPointerMapValueCallBacks =
- (NSEnumerator*) keyEnumerator
{
return [self subclassResponsibility: _cmd];
NSEnumerator *e;
e = [[NSConcreteMapTableKeyEnumerator alloc] initWithMapTable: self];
return [e autorelease];
}
- (NSPointerFunctions*) keyPointerFunctions
@ -1073,7 +1303,10 @@ const NSMapTableValueCallBacks NSOwnedPointerMapValueCallBacks =
- (NSEnumerator*) objectEnumerator
{
return [self subclassResponsibility: _cmd];
NSEnumerator *e;
e = [[NSConcreteMapTableObjectEnumerator alloc] initWithMapTable: self];
return [e autorelease];
}
- (id) objectForKey: (id)aKey
@ -1092,7 +1325,11 @@ const NSMapTableValueCallBacks NSOwnedPointerMapValueCallBacks =
- (void) removeAllObjects
{
GSIMapEmptyMap(self);
if (nodeCount > 0)
{
GSIMapEmptyMap(self);
version++;
}
}
- (void) removeObjectForKey: (id)aKey
@ -1102,7 +1339,21 @@ const NSMapTableValueCallBacks NSOwnedPointerMapValueCallBacks =
NSWarnMLog(@"attempt to remove nil key from map table %@", self);
return;
}
GSIMapRemoveKey(self, (GSIMapKey)aKey);
if (nodeCount > 0)
{
GSIMapTable map = (GSIMapTable)self;
GSIMapBucket bucket;
GSIMapNode node;
bucket = GSIMapBucketForKey(map, (GSIMapKey)aKey);
node = GSIMapNodeForKeyInBucket(map, bucket, (GSIMapKey)aKey);
if (node != 0)
{
GSIMapRemoveNodeFromMap(map, bucket, node);
GSIMapFreeNode(map, node);
version++;
}
}
}
- (void) setObject: (id)anObject forKey: (id)aKey
@ -1126,11 +1377,13 @@ const NSMapTableValueCallBacks NSOwnedPointerMapValueCallBacks =
GSI_MAP_RELEASE_VAL(self, node->value);
node->value.obj = anObject;
GSI_MAP_RETAIN_VAL(self, node->value);
version++;
}
}
else
{
GSIMapAddPair(self, (GSIMapKey)aKey, (GSIMapVal)anObject);
version++;
}
}
@ -1143,3 +1396,47 @@ const NSMapTableValueCallBacks NSOwnedPointerMapValueCallBacks =
}
@end
@implementation NSConcreteMapTableKeyEnumerator
- (id) initWithMapTable: (NSConcreteMapTable*)t
{
table = RETAIN(t);
enumerator = GSIMapEnumeratorForMap(table);
return self;
}
- (id) nextObject
{
GSIMapNode node = GSIMapEnumeratorNextNode(&enumerator);
if (node == 0)
{
return nil;
}
return node->key.obj;
}
- (void) dealloc
{
GSIMapEndEnumerator(&enumerator);
RELEASE(table);
[super dealloc];
}
@end
@implementation NSConcreteMapTableObjectEnumerator
- (id) nextObject
{
GSIMapNode node = GSIMapEnumeratorNextNode(&enumerator);
if (node == 0)
{
return nil;
}
return node->value.obj;
}
@end

View file

@ -30,10 +30,12 @@
#include "Foundation/NSArray.h"
#include "Foundation/NSException.h"
#include "Foundation/NSPointerFunctions.h"
#include "Foundation/NSSet.h"
#include "Foundation/NSZone.h"
#include "Foundation/NSHashTable.h"
#include "Foundation/NSDebug.h"
#include "NSCallBacks.h"
#include "GSPrivate.h"
@implementation NSHashTable
@ -103,12 +105,26 @@ static Class concreteClass = 0;
- (NSArray*) allObjects
{
return [self subclassResponsibility: _cmd];
NSEnumerator *enumerator;
unsigned nodeCount = [self count];
unsigned index;
NSArray *a;
GS_BEGINITEMBUF(objects, nodeCount, id);
enumerator = [self objectEnumerator];
index = 0;
while ((objects[index] = [enumerator nextObject]) != nil)
{
index++;
}
a = [[[NSArray alloc] initWithObjects: objects count: nodeCount] autorelease];
GS_ENDITEMBUF();
return a;
}
- (id) anyObject
{
return [self subclassResponsibility: _cmd];
return [[self objectEnumerator] nextObject];
}
- (BOOL) containsObject: (id)anObject
@ -140,7 +156,7 @@ static Class concreteClass = 0;
- (NSUInteger) hash
{
return (NSUInteger)[self subclassResponsibility: _cmd];
return [self count];
}
- (id) initWithCoder: (NSCoder*)aCoder
@ -150,12 +166,45 @@ static Class concreteClass = 0;
- (void) intersectHashTable: (NSHashTable*)other
{
[self subclassResponsibility: _cmd];
unsigned count = [self count];
if (count > 0)
{
NSEnumerator *enumerator;
NSMutableArray *array;
id object;
array = [NSMutableArray arrayWithCapacity: count];
enumerator = [self objectEnumerator];
while ((object = [enumerator nextObject]) != nil)
{
if ([other member: object] != nil)
{
[array addObject: object];
}
}
enumerator = [array objectEnumerator];
while ((object = [enumerator nextObject]) != nil)
{
[self removeObject: object];
}
}
}
- (BOOL) intersectsHashTable: (NSHashTable*)other
{
return (BOOL)(uintptr_t)[self subclassResponsibility: _cmd];
NSEnumerator *enumerator;
id object;
enumerator = [self objectEnumerator];
while ((object = [enumerator nextObject]) != nil)
{
if ([other member: object] != nil)
{
return YES;
}
}
return NO;
}
- (BOOL) isEqual: (id)other
@ -171,7 +220,18 @@ static Class concreteClass = 0;
- (BOOL) isSubsetOfHashTable: (NSHashTable*)other
{
return (BOOL)(uintptr_t)[self subclassResponsibility: _cmd];
NSEnumerator *enumerator;
id object;
enumerator = [self objectEnumerator];
while ((object = [enumerator nextObject]) != nil)
{
if ([other member: object] == nil)
{
return NO;
}
}
return YES;
}
- (id) member: (id)object
@ -181,7 +241,17 @@ static Class concreteClass = 0;
- (void) minusHashTable: (NSHashTable*)other
{
[self subclassResponsibility: _cmd];
if ([self count] > 0 && [other count] > 0)
{
NSEnumerator *enumerator;
id object;
enumerator = [other objectEnumerator];
while ((object = [enumerator nextObject]) != nil)
{
[self removeObject: object];
}
}
}
- (NSEnumerator*) objectEnumerator
@ -196,7 +266,14 @@ static Class concreteClass = 0;
- (void) removeAllObjects
{
[self subclassResponsibility: _cmd];
NSEnumerator *enumerator;
id object;
enumerator = [[self allObjects] objectEnumerator];
while ((object = [enumerator nextObject]) != nil)
{
[self removeObject: object];
}
}
- (void) removeObject: (id)aKey
@ -206,12 +283,29 @@ static Class concreteClass = 0;
- (NSSet*) setRepresentation
{
return [self subclassResponsibility: _cmd];
NSEnumerator *enumerator;
NSMutableSet *set;
id object;
set = [NSMutableSet setWithCapacity: [self count]];
enumerator = [[self allObjects] objectEnumerator];
while ((object = [enumerator nextObject]) != nil)
{
[set addObject: object];
}
return [[set copy] autorelease];
}
- (void) unionHashTable: (NSHashTable*)other
{
[self subclassResponsibility: _cmd];
NSEnumerator *enumerator;
id object;
enumerator = [other objectEnumerator];
while ((object = [enumerator nextObject]) != nil)
{
[self addObject: object];
}
}
@end

View file

@ -28,6 +28,7 @@
#include "Foundation/NSObject.h"
#include "Foundation/NSString.h"
#include "Foundation/NSArray.h"
#include "Foundation/NSDictionary.h"
#include "Foundation/NSException.h"
#include "Foundation/NSPointerFunctions.h"
#include "Foundation/NSZone.h"
@ -146,7 +147,17 @@ static Class concreteClass = 0;
- (NSDictionary*) dictionaryRepresentation
{
return [self subclassResponsibility: _cmd];
NSEnumerator *enumerator;
NSMutableDictionary *dictionary;
id key;
dictionary = [NSMutableDictionary dictionaryWithCapacity: [self count]];
enumerator = [self keyEnumerator];
while ((key = [enumerator nextObject]) != nil)
{
[dictionary setObject: [self objectForKey: key] forKey: key];
}
return [[dictionary copy] autorelease];
}
- (void) encodeWithCoder: (NSCoder*)aCoder
@ -156,7 +167,7 @@ static Class concreteClass = 0;
- (NSUInteger) hash
{
return (NSUInteger)[self subclassResponsibility: _cmd];
return [self count];
}
- (id) initWithCoder: (NSCoder*)aCoder
@ -192,7 +203,27 @@ static Class concreteClass = 0;
- (void) removeAllObjects
{
[self subclassResponsibility: _cmd];
unsigned count = [self count];
if (count > 0)
{
NSEnumerator *enumerator;
NSMutableArray *array;
id key;
array = [[NSMutableArray alloc] initWithCapacity: count];
enumerator = [self objectEnumerator];
while ((key = [enumerator nextObject]) != nil)
{
[array addObject: key];
}
enumerator = [array objectEnumerator];
while ((key = [enumerator nextObject]) != nil)
{
[self removeObjectForKey: key];
}
[array release];
}
}
- (void) removeObjectForKey: (id)aKey