Various changes for improved uniquing

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@6478 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Richard Frith-MacDonald 2000-04-18 09:02:38 +00:00
parent 5c31e5274d
commit 654eda135c
14 changed files with 770 additions and 353 deletions

View file

@ -28,10 +28,27 @@
#include <Foundation/NSArray.h>
#include <Foundation/NSUtilities.h>
#include <Foundation/NSString.h>
#include <Foundation/NSLock.h>
#include <Foundation/NSNotification.h>
#include <Foundation/NSThread.h>
@class NSSetNonCore;
@class NSMutableSetNonCore;
/*
* Class variables for uniquing objects;
*/
static NSRecursiveLock *uniqueLock = nil;
static NSCountedSet *uniqueSet = nil;
static IMP uniqueImp = 0;
static IMP lockImp = 0;
static IMP unlockImp = 0;
static BOOL uniquing = NO;
@interface NSCountedSet (GSThreading)
+ (void) _becomeThreaded: (id)notification;
@end
@implementation NSCountedSet
static Class NSCountedSet_abstract_class;
@ -45,6 +62,18 @@ static Class NSCountedSet_concrete_class;
NSCountedSet_concrete_class = [NSGCountedSet class];
behavior_class_add_class(self, [NSMutableSetNonCore class]);
behavior_class_add_class(self, [NSSetNonCore class]);
if ([NSThread isMultiThreaded])
{
[self _becomeThreaded: nil];
}
else
{
[[NSNotificationCenter defaultCenter]
addObserver: self
selector: @selector(_becomeThreaded:)
name: NSWillBecomeMultiThreadedNotification
object: nil];
}
}
}
@ -65,7 +94,7 @@ static Class NSCountedSet_concrete_class;
return [super allocWithZone: z];
}
- (unsigned int) countForObject: anObject
- (unsigned int) countForObject: (id)anObject
{
[self subclassResponsibility: _cmd];
return 0;
@ -129,4 +158,110 @@ static Class NSCountedSet_concrete_class;
return self;
}
- (void) purge: (int)level
{
if (level > 0)
{
NSEnumerator *enumerator = [self objectEnumerator];
if (enumerator != nil)
{
id obj;
id (*nImp)(NSEnumerator*, SEL);
unsigned (*cImp)(NSCountedSet*, SEL, id);
void (*rImp)(NSCountedSet*, SEL, id);
nImp = (id (*)(NSEnumerator*, SEL))
[enumerator methodForSelector: @selector(nextObject)];
cImp = (unsigned (*)(NSCountedSet*, SEL, id))
[self methodForSelector: @selector(countForObject:)];
rImp = (void (*)(NSCountedSet*, SEL, id))
[self methodForSelector: @selector(removeObject:)];
while ((obj = (*nImp)(enumerator, @selector(nextObject))) != nil)
{
unsigned c = (*cImp)(self, @selector(countForObject:), obj);
if (c <= level)
{
while (c-- > 0)
{
(*rImp)(self, @selector(removeObject:), obj);
}
}
}
}
}
}
- (id) unique: (id)anObject
{
id o = [self member: anObject];
[self addObject: anObject];
#if !GS_WITH_GC
if (o != anObject)
{
[anObject release];
[o retain];
}
#endif
return o;
}
@end
@implementation NSCountedSet (GSThreading)
/*
* If we are multi-threaded, we must guard access to the uniquing set.
*/
+ (void) _becomeThreaded: (id)notification
{
uniqueLock = [NSLock new];
lockImp = [uniqueLock methodForSelector: @selector(lock)];
unlockImp = [uniqueLock methodForSelector: @selector(unlock)];
}
@end
void
GSUPurge(int level)
{
if (uniqueLock != nil)
{
(*lockImp)(uniqueLock, @selector(lock));
}
[uniqueSet purge: level];
if (uniqueLock != nil)
{
(*unlockImp)(uniqueLock, @selector(unlock));
}
}
id
GSUnique(id obj)
{
if (uniquing == YES)
{
if (uniqueLock != nil)
{
(*lockImp)(uniqueLock, @selector(lock));
}
obj = (*uniqueImp)(uniqueSet, @selector(unique:), obj);
if (uniqueLock != nil)
{
(*unlockImp)(uniqueLock, @selector(unlock));
}
}
return obj;
}
void
GSUniquing(BOOL flag)
{
if (uniqueSet == nil)
{
uniqueSet = [NSCountedSet new];
uniqueImp = [uniqueSet methodForSelector: @selector(unique:)];
}
uniquing = flag;
}

View file

@ -253,6 +253,29 @@
NSDefaultMallocZone()] initWithSet: self]);
}
- (void) purge: (int)level
{
if (level > 0)
{
GSIMapNode node = map.firstNode;
while (node != 0)
{
GSIMapNode tmp = node->nextInMap;
if (node->value.uint <= level)
{
GSIMapBucket bucket;
bucket = GSIMapBucketForKey(&map, node->key);
GSIMapRemoveNodeFromMap(&map, bucket, node);
GSIMapFreeNode(&map, node);
}
node = tmp;
}
}
}
- (void) removeObject: (NSObject*)anObject
{
GSIMapBucket bucket;
@ -284,4 +307,35 @@
GSIMapCleanMap(&map);
}
- (id) unique: (id)anObject
{
GSIMapNode node;
id result;
if (anObject == nil)
{
[NSException raise: NSInvalidArgumentException
format: @"Tried to unique nil value in counted set"];
}
node = GSIMapNodeForKey(&map, (GSIMapKey)anObject);
if (node == 0)
{
result = anObject;
GSIMapAddPairNoRetain(&map,(GSIMapKey)anObject,(GSIMapVal)(unsigned)1);
}
else
{
result = node->key.obj;
node->value.uint++;
#if !GS_WITH_GC
if (result != anObject)
{
[anObject release];
[result retain];
}
#endif
}
return result;
}
@end

View file

@ -112,7 +112,7 @@ typedef struct {
void (*serImp)(); // Serialize integer.
void (*setImp)(); // Set length of data.
unsigned count; // String counter.
GSIMapTable_t map; // For uniquing.
GSIMapTable_t map; // For uniquing.
BOOL shouldUnique; // Do we do uniquing?
} _NSSerializerInfo;
@ -347,15 +347,10 @@ static BOOL shouldBeCompact = NO;
/*
* Class variables for uniquing incoming strings.
*/
static NSRecursiveLock *uniqueLock = nil;
static NSMutableSet *uniqueSet = nil;
/*
* Variables to cache class information.
*/
static BOOL uniquing = NO; /* Make incoming strings unique */
static Class IACls = 0; /* Immutable Array */
static Class MACls = 0; /* Mutable Array */
static Class DCls = 0; /* Data */
@ -439,22 +434,8 @@ deserializeFromInfo(_NSDeserializerInfo* info)
/*
* If we are supposed to be doing uniquing of strings, handle it.
*/
if (uniqueSet != nil)
{
id uniqued;
if (uniqueLock != nil)
[uniqueLock lock];
[uniqueSet addObject: s];
uniqued = [uniqueSet member: s];
if (uniqueLock != nil)
[uniqueLock unlock];
if (uniqued != s)
{
RELEASE(s);
s = RETAIN(uniqued);
}
}
if (uniquing == YES)
s = GSUnique(s);
/*
* If uniquing was done on serialisation, store the string for
@ -477,22 +458,8 @@ deserializeFromInfo(_NSDeserializerInfo* info)
/*
* If we are supposed to be doing uniquing of strings, handle it.
*/
if (uniqueSet != nil)
{
id uniqued;
if (uniqueLock != nil)
[uniqueLock lock];
[uniqueSet addObject: s];
uniqued = [uniqueSet member: s];
if (uniqueLock != nil)
[uniqueLock unlock];
if (uniqued != s)
{
RELEASE(s);
s = RETAIN(uniqued);
}
}
if (uniquing == YES)
s = GSUnique(s);
/*
* If uniquing was done on serialisation, store the string for
@ -701,18 +668,6 @@ deserializeFromInfo(_NSDeserializerInfo* info)
maInitImp = [MACls instanceMethodForSelector: maInitSel];
idInitImp = [IDCls instanceMethodForSelector: idInitSel];
mdInitImp = [MDCls instanceMethodForSelector: mdInitSel];
if ([NSThread isMultiThreaded])
{
[self _becomeThreaded: nil];
}
else
{
[[NSNotificationCenter defaultCenter]
addObserver: self
selector: @selector(_becomeThreaded:)
name: NSWillBecomeMultiThreadedNotification
object: nil];
}
}
}
@ -780,57 +735,12 @@ deserializeFromInfo(_NSDeserializerInfo* info)
}
@end
@implementation NSDeserializer (GNUstep)
/*
* If we are multi-threaded, we must guard access to the uniquing set.
*/
+ (void) _becomeThreaded: (id)notification
{
uniqueLock = [NSRecursiveLock new];
}
+ (NSMutableSet*) uniqueSet
{
return uniqueSet;
}
+ (NSString*) unique: (NSString*)str
{
if (uniqueSet)
{
if (uniqueLock != nil)
[uniqueLock lock];
[uniqueSet addObject: str];
str = [uniqueSet member: str];
if (uniqueLock != nil)
[uniqueLock unlock];
}
return str;
}
/*
* Turn uniquing of deserialized strings on/off
*/
@implementation NSDeserializer (GNUstep)
+ (void) uniquing: (BOOL)flag
{
if (uniqueLock != nil)
[uniqueLock lock];
if (flag)
{
if (uniqueSet == nil)
{
uniqueSet = [NSMutableSet new];
}
}
else
{
if (uniqueSet != nil)
{
DESTROY(uniqueSet);
}
}
if (uniqueLock != nil)
[uniqueLock unlock];
if (flag == YES)
GSUniquing(YES);
uniquing = flag;
}
@end