Added fast enumeration to NSSet. This changed the layout of GSMutableSet, adding a _version ivar, but this is a private class so it doesn't change the public ABI. All mutator methods in GSMutableSet must increment this variable, allowing thread-safe fast enumeration.

As with GSArray, GSSet uses its isa pointer for detecting mutations.  This may change as a result of adding KVO notifications, so it might not be the best solution, but I can't currently think of a way we could catch isa changing to [GSMutableSet class] and not changing to a hidden class...



git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@29179 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
theraven 2009-12-29 15:59:14 +00:00
parent c5cf5e4535
commit cee58ca099
4 changed files with 130 additions and 3 deletions

View file

@ -1,3 +1,11 @@
2009-12-29 David Chisnall <theraven@gna.org>
* Source/GSSet.m
* Source/NSSet.m
* Headers/Foundation/NSSet.h
Added fast enumeration support to NSSet.
2009-12-27 David Chisnall <theraven@gna.org>
* Source/NSArray.m

View file

@ -31,6 +31,7 @@
#import <GNUstepBase/GSVersionMacros.h>
#import <Foundation/NSObject.h>
#import <Foundation/NSEnumerator.h>
#if defined(__cplusplus)
extern "C" {
@ -38,7 +39,7 @@ extern "C" {
@class NSArray, NSString, NSEnumerator, NSDictionary;
@interface NSSet : NSObject <NSCoding, NSCopying, NSMutableCopying>
@interface NSSet : NSObject <NSCoding, NSCopying, NSMutableCopying, NSFastEnumeration>
+ (id) set;
+ (id) setWithArray: (NSArray*)objects;

View file

@ -10,7 +10,7 @@
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
_version 2 of the License, or (at your option) any later _version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
@ -63,6 +63,8 @@ static SEL memberSel;
{
@public
GSIMapTable_t map;
@private
NSUInteger _version;
}
@end
@ -521,6 +523,58 @@ static Class mutableSetClass;
return AUTORELEASE([[GSSetEnumerator alloc] initWithSet: self]);
}
- (NSUInteger) countByEnumeratingWithState: (NSFastEnumerationState*)state
objects: (id*)stackbuf
count: (NSUInteger)len
{
NSInteger count;
NSInteger i;
/* 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 *)self;
count = MIN(len, map.nodeCount - state->state);
/* We can store a GSIMapEnumerator inside the extra buffer in state on all
* platforms that don't suck beyond belief (i.e. everything except win64),
* but we can't on anything where long is 32 bits and pointers are 64 bits,
* so we have to construct it here to avoid breaking on that platform.
*/
struct GSPartMapEnumerator
{
GSIMapNode node;
uintptr_t bucket;
};
GSIMapEnumerator_t enumerator;
/* Construct the real enumerator */
enumerator.map = &map;
if (0 == state->state)
{
enumerator = GSIMapEnumeratorForMap(&map);
}
else
{
enumerator.node = ((struct GSPartMapEnumerator*)&(state->extra))->node;
enumerator.bucket = ((struct GSPartMapEnumerator*)&(state->extra))->bucket;
}
/* Get the next count objects and put them in the stack buffer. */
for (i=0 ; i<count ; i++)
{
GSIMapNode node = GSIMapEnumeratorNextNode(&enumerator);
if (0 != node)
{
stackbuf[i] = node->key.obj;
}
}
/* Store the important bits of the enumerator in the caller. */
((struct GSPartMapEnumerator*)&(state->extra))->node = enumerator.node;
((struct GSPartMapEnumerator*)&(state->extra))->bucket = enumerator.bucket;
/* Update the rest of the state. */
state->state += count;
state->itemsPtr = stackbuf;
return count;
}
@end
@implementation GSMutableSet
@ -546,6 +600,7 @@ static Class mutableSetClass;
if (node == 0)
{
GSIMapAddKey(&map, (GSIMapKey)anObject);
_version++;
}
}
@ -570,12 +625,13 @@ static Class mutableSetClass;
if (node == 0)
{
GSIMapAddKey(&map, (GSIMapKey)anObject);
_version++;
}
}
}
}
/* Override version from GSSet */
/* Override _version from GSSet */
- (id) copyWithZone: (NSZone*)z
{
NSSet *copy = [setClass allocWithZone: z];
@ -672,6 +728,7 @@ static Class mutableSetClass;
while ((anObject = [e nextObject]) != nil)
{
GSIMapRemoveKey(&map, (GSIMapKey)anObject);
_version++;
}
}
}
@ -689,6 +746,7 @@ static Class mutableSetClass;
return;
}
GSIMapRemoveKey(&map, (GSIMapKey)anObject);
_version++;
}
- (void) unionSet: (NSSet*) other
@ -711,12 +769,65 @@ static Class mutableSetClass;
if (node == 0)
{
GSIMapAddKey(&map, (GSIMapKey)anObject);
_version++;
}
}
}
}
}
- (NSUInteger) countByEnumeratingWithState: (NSFastEnumerationState*)state
objects: (id*)stackbuf
count: (NSUInteger)len
{
NSInteger count;
NSInteger i;
/* 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 *)&_version;
count = MIN(len, map.nodeCount - state->state);
/* We can store a GSIMapEnumerator inside the extra buffer in state on all
* platforms that don't suck beyond belief (i.e. everything except win64),
* but we can't on anything where long is 32 bits and pointers are 64 bits,
* so we have to construct it here to avoid breaking on that platform.
*/
struct GSPartMapEnumerator
{
GSIMapNode node;
uintptr_t bucket;
};
GSIMapEnumerator_t enumerator;
/* Construct the real enumerator */
enumerator.map = &map;
if (0 == state->state)
{
enumerator = GSIMapEnumeratorForMap(&map);
}
else
{
enumerator.node = ((struct GSPartMapEnumerator*)&(state->extra))->node;
enumerator.bucket = ((struct GSPartMapEnumerator*)&(state->extra))->bucket;
}
/* Get the next count objects and put them in the stack buffer. */
for (i=0 ; i<count ; i++)
{
GSIMapNode node = GSIMapEnumeratorNextNode(&enumerator);
if (0 != node)
{
stackbuf[i] = node->key.obj;
}
}
/* Store the important bits of the enumerator in the caller. */
((struct GSPartMapEnumerator*)&(state->extra))->node = enumerator.node;
((struct GSPartMapEnumerator*)&(state->extra))->bucket = enumerator.bucket;
/* Update the rest of the state. */
state->state += count;
state->itemsPtr = stackbuf;
return count;
}
@end
@interface NSGSet : NSSet

View file

@ -902,6 +902,13 @@ static Class NSMutableSet_concrete_class;
return [s autorelease];
}
- (NSUInteger) countByEnumeratingWithState: (NSFastEnumerationState*)state
objects: (id*)stackbuf
count: (NSUInteger)len
{
[self subclassResponsibility: _cmd];
return 0;
}
@end