mirror of
https://github.com/gnustep/libs-base.git
synced 2025-05-30 16:30:41 +00:00
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:
parent
5c31e5274d
commit
654eda135c
14 changed files with 770 additions and 353 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue