mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-23 00:41:02 +00:00
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:
parent
1be02d0252
commit
807d1b8a65
8 changed files with 1059 additions and 233 deletions
15
ChangeLog
15
ChangeLog
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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].
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue