mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-23 09:04:13 +00:00
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:
parent
c5cf5e4535
commit
cee58ca099
4 changed files with 130 additions and 3 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
115
Source/GSSet.m
115
Source/GSSet.m
|
@ -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 = ↦
|
||||
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 = ↦
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue