2002-11-19 05:37:42 +00:00
|
|
|
|
/* Implementation of garbage collecting dictionary classes
|
|
|
|
|
|
|
|
|
|
Copyright (C) 2002 Free Software Foundation, Inc.
|
|
|
|
|
|
|
|
|
|
Written by: Richard Frith-Macdonald <rfm@gnu.org>
|
|
|
|
|
Inspired by gc classes of Ovidiu Predescu and Mircea Oancea
|
|
|
|
|
|
|
|
|
|
This file is part of the GNUstep Base Library.
|
|
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
2007-09-14 11:36:11 +00:00
|
|
|
|
modify it under the terms of the GNU Lesser General Public
|
2002-11-19 05:37:42 +00:00
|
|
|
|
License as published by the Free Software Foundation; either
|
2008-06-08 10:38:33 +00:00
|
|
|
|
version 2 of the License, or (at your option) any later version.
|
2005-02-22 11:22:44 +00:00
|
|
|
|
|
2002-11-19 05:37:42 +00:00
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
Library General Public License for more details.
|
|
|
|
|
|
2007-09-14 11:36:11 +00:00
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
2002-11-19 05:37:42 +00:00
|
|
|
|
License along with this library; if not, write to the Free
|
2006-06-04 06:42:10 +00:00
|
|
|
|
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
|
|
|
Boston, MA 02111 USA.
|
2002-11-19 05:37:42 +00:00
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
2010-02-19 08:12:46 +00:00
|
|
|
|
#import "common.h"
|
2003-02-03 04:15:27 +00:00
|
|
|
|
#ifndef NeXT_Foundation_LIBRARY
|
2010-02-14 10:48:10 +00:00
|
|
|
|
#import "Foundation/NSException.h"
|
2003-02-03 04:15:27 +00:00
|
|
|
|
#else
|
2010-02-14 10:48:10 +00:00
|
|
|
|
#import <Foundation/Foundation.h>
|
2003-02-03 04:15:27 +00:00
|
|
|
|
#endif
|
2002-11-19 05:37:42 +00:00
|
|
|
|
|
2010-02-14 10:48:10 +00:00
|
|
|
|
#import "GNUstepBase/GSObjCRuntime.h"
|
|
|
|
|
#import "GNUstepBase/GCObject.h"
|
2002-11-19 05:37:42 +00:00
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
id object;
|
|
|
|
|
BOOL isGCObject;
|
|
|
|
|
} GCInfo;
|
|
|
|
|
|
|
|
|
|
@interface _GCDictionaryKeyEnumerator : NSObject
|
|
|
|
|
{
|
|
|
|
|
@public
|
|
|
|
|
GCDictionary *dict;
|
|
|
|
|
NSMapEnumerator enumerator;
|
|
|
|
|
}
|
|
|
|
|
- (id) nextObject;
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@interface _GCDictionaryObjectEnumerator : _GCDictionaryKeyEnumerator
|
|
|
|
|
- (id) nextObject;
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation _GCDictionaryKeyEnumerator
|
|
|
|
|
- (id) copyWithZone: (NSZone*)z
|
|
|
|
|
{
|
|
|
|
|
return [self retain];
|
|
|
|
|
}
|
|
|
|
|
- (void) dealloc
|
|
|
|
|
{
|
|
|
|
|
NSEndMapTableEnumeration(&enumerator);
|
2003-04-01 04:27:18 +00:00
|
|
|
|
DESTROY(dict);
|
2002-11-19 05:37:42 +00:00
|
|
|
|
[super dealloc];
|
|
|
|
|
}
|
|
|
|
|
- (id) nextObject
|
|
|
|
|
{
|
|
|
|
|
GCInfo *keyStruct;
|
|
|
|
|
GCInfo *valueStruct;
|
|
|
|
|
|
2005-02-22 11:22:44 +00:00
|
|
|
|
return NSNextMapEnumeratorPair(&enumerator,
|
2002-11-19 05:37:42 +00:00
|
|
|
|
(void**)&keyStruct, (void**)&valueStruct) ? keyStruct->object : nil;
|
|
|
|
|
}
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation _GCDictionaryObjectEnumerator
|
|
|
|
|
- (id) nextObject
|
|
|
|
|
{
|
|
|
|
|
GCInfo *keyStruct;
|
|
|
|
|
GCInfo *valueStruct;
|
|
|
|
|
|
2005-02-22 11:22:44 +00:00
|
|
|
|
return NSNextMapEnumeratorPair(&enumerator,
|
2002-11-19 05:37:42 +00:00
|
|
|
|
(void**)&keyStruct, (void**)&valueStruct) ? valueStruct->object : nil;
|
|
|
|
|
}
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation GCDictionary
|
|
|
|
|
|
|
|
|
|
static unsigned
|
|
|
|
|
_GCHashObject(NSMapTable *table, const GCInfo *objectStruct)
|
|
|
|
|
{
|
|
|
|
|
return [objectStruct->object hash];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static BOOL
|
|
|
|
|
_GCCompareObjects(NSMapTable *table, const GCInfo *o1, const GCInfo *o2)
|
|
|
|
|
{
|
|
|
|
|
return [o1->object isEqual: o2->object];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_GCRetainObjects(NSMapTable *table, const void *ptr)
|
|
|
|
|
{
|
2009-01-12 12:48:46 +00:00
|
|
|
|
#if !GS_WITH_GC
|
2002-11-19 05:37:42 +00:00
|
|
|
|
GCInfo *objectStruct = (GCInfo*)ptr;
|
|
|
|
|
|
2009-01-12 12:48:46 +00:00
|
|
|
|
[objectStruct->object retain];
|
|
|
|
|
#endif
|
2002-11-19 05:37:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_GCReleaseObjects(NSMapTable *table, const void *ptr)
|
|
|
|
|
{
|
|
|
|
|
GCInfo *objectStruct = (GCInfo*)ptr;
|
|
|
|
|
|
|
|
|
|
if ([GCObject gcIsCollecting])
|
|
|
|
|
{
|
|
|
|
|
if (objectStruct->isGCObject == NO)
|
|
|
|
|
{
|
2003-04-01 04:27:18 +00:00
|
|
|
|
DESTROY(objectStruct->object);
|
2002-11-19 05:37:42 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2003-04-01 04:27:18 +00:00
|
|
|
|
DESTROY(objectStruct->object);
|
2002-11-19 05:37:42 +00:00
|
|
|
|
}
|
|
|
|
|
NSZoneFree(NSDefaultMallocZone(), objectStruct);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static NSString*
|
|
|
|
|
_GCDescribeObjects(NSMapTable *table, const GCInfo *objectStruct)
|
|
|
|
|
{
|
|
|
|
|
return [objectStruct->object description];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const NSMapTableKeyCallBacks GCInfoMapKeyCallBacks = {
|
2010-02-25 05:30:05 +00:00
|
|
|
|
(NSUInteger(*)(NSMapTable *, const void *))_GCHashObject,
|
2002-11-19 05:37:42 +00:00
|
|
|
|
(BOOL(*)(NSMapTable *, const void *, const void *))_GCCompareObjects,
|
|
|
|
|
(void (*)(NSMapTable *, const void *))_GCRetainObjects,
|
2002-12-31 10:09:54 +00:00
|
|
|
|
(void (*)(NSMapTable *, void *))_GCReleaseObjects,
|
2002-11-19 05:37:42 +00:00
|
|
|
|
(NSString *(*)(NSMapTable *, const void *))_GCDescribeObjects,
|
|
|
|
|
(const void *)NULL
|
2005-02-22 11:22:44 +00:00
|
|
|
|
};
|
2002-11-19 05:37:42 +00:00
|
|
|
|
|
|
|
|
|
static const NSMapTableValueCallBacks GCInfoValueCallBacks = {
|
|
|
|
|
(void (*)(NSMapTable *, const void *))_GCRetainObjects,
|
2002-12-31 10:09:54 +00:00
|
|
|
|
(void (*)(NSMapTable *, void *))_GCReleaseObjects,
|
2002-11-19 05:37:42 +00:00
|
|
|
|
(NSString *(*)(NSMapTable *, const void *))_GCDescribeObjects
|
2005-02-22 11:22:44 +00:00
|
|
|
|
};
|
2002-11-19 05:37:42 +00:00
|
|
|
|
|
|
|
|
|
static Class gcClass = 0;
|
|
|
|
|
|
|
|
|
|
+ (void) initialize
|
|
|
|
|
{
|
|
|
|
|
if (gcClass == 0)
|
|
|
|
|
{
|
|
|
|
|
gcClass = [GCObject class];
|
2002-11-27 12:52:29 +00:00
|
|
|
|
GSObjCAddClassBehavior(self, gcClass);
|
2002-11-19 05:37:42 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (id) copyWithZone: (NSZone*)zone
|
|
|
|
|
{
|
|
|
|
|
if (NSShouldRetainWithZone(self, zone))
|
|
|
|
|
{
|
|
|
|
|
return [self retain];
|
|
|
|
|
}
|
|
|
|
|
return [[GCDictionary allocWithZone: zone] initWithDictionary: self];
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-23 20:42:32 +00:00
|
|
|
|
- (NSUInteger) count
|
2002-11-19 05:37:42 +00:00
|
|
|
|
{
|
|
|
|
|
return NSCountMapTable(_map);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
|
{
|
|
|
|
|
[GCObject gcObjectWillBeDeallocated: (GCObject*)self];
|
|
|
|
|
NSFreeMapTable(_map);
|
|
|
|
|
[super dealloc];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) gcDecrementRefCountOfContainedObjects
|
|
|
|
|
{
|
|
|
|
|
NSMapEnumerator enumerator = NSEnumerateMapTable(_map);
|
|
|
|
|
GCInfo *keyStruct;
|
|
|
|
|
GCInfo *valueStruct;
|
|
|
|
|
|
|
|
|
|
gc.flags.visited = 0;
|
|
|
|
|
while (NSNextMapEnumeratorPair(&enumerator,
|
|
|
|
|
(void**)&keyStruct, (void**)&valueStruct))
|
|
|
|
|
{
|
|
|
|
|
if (keyStruct->isGCObject)
|
|
|
|
|
{
|
|
|
|
|
[keyStruct->object gcDecrementRefCount];
|
|
|
|
|
}
|
|
|
|
|
if (valueStruct->isGCObject)
|
|
|
|
|
{
|
|
|
|
|
[valueStruct->object gcDecrementRefCount];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
NSEndMapTableEnumeration(&enumerator);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL) gcIncrementRefCountOfContainedObjects
|
|
|
|
|
{
|
|
|
|
|
NSMapEnumerator enumerator;
|
|
|
|
|
GCInfo *keyStruct;
|
|
|
|
|
GCInfo *valueStruct;
|
|
|
|
|
|
|
|
|
|
if (gc.flags.visited == 1)
|
|
|
|
|
{
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
gc.flags.visited = 1;
|
|
|
|
|
|
|
|
|
|
enumerator = NSEnumerateMapTable(_map);
|
|
|
|
|
while (NSNextMapEnumeratorPair(&enumerator,
|
|
|
|
|
(void**)&keyStruct, (void**)&valueStruct))
|
|
|
|
|
{
|
|
|
|
|
if (keyStruct->isGCObject)
|
|
|
|
|
{
|
|
|
|
|
[keyStruct->object gcIncrementRefCount];
|
|
|
|
|
[keyStruct->object gcIncrementRefCountOfContainedObjects];
|
|
|
|
|
}
|
|
|
|
|
if (valueStruct->isGCObject)
|
|
|
|
|
{
|
|
|
|
|
[valueStruct->object gcIncrementRefCount];
|
|
|
|
|
[valueStruct->object gcIncrementRefCountOfContainedObjects];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
NSEndMapTableEnumeration(&enumerator);
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (id) initWithDictionary: (NSDictionary*)dictionary
|
|
|
|
|
{
|
|
|
|
|
id keys = [dictionary keyEnumerator];
|
|
|
|
|
id key;
|
2009-02-23 20:42:32 +00:00
|
|
|
|
NSUInteger size = ([dictionary count] * 4) / 3;
|
2002-11-19 05:37:42 +00:00
|
|
|
|
NSZone *z = NSDefaultMallocZone();
|
|
|
|
|
|
|
|
|
|
_map = NSCreateMapTableWithZone(GCInfoMapKeyCallBacks,
|
|
|
|
|
GCInfoValueCallBacks, size, z);
|
|
|
|
|
|
|
|
|
|
while ((key = [keys nextObject]) != nil)
|
|
|
|
|
{
|
|
|
|
|
GCInfo *keyStruct;
|
|
|
|
|
GCInfo *valueStruct;
|
|
|
|
|
id value;
|
|
|
|
|
|
|
|
|
|
keyStruct = NSZoneMalloc(z, sizeof(GCInfo));
|
|
|
|
|
valueStruct = NSZoneMalloc(z, sizeof(GCInfo));
|
|
|
|
|
value = [dictionary objectForKey: key];
|
|
|
|
|
keyStruct->object = key;
|
|
|
|
|
keyStruct->isGCObject = [key isKindOfClass: gcClass];
|
|
|
|
|
valueStruct->object = value;
|
|
|
|
|
valueStruct->isGCObject = [value isKindOfClass: gcClass];
|
|
|
|
|
NSMapInsert(_map, keyStruct, valueStruct);
|
|
|
|
|
}
|
2005-02-22 11:22:44 +00:00
|
|
|
|
|
2002-11-19 05:37:42 +00:00
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (id) initWithObjects: (id*)objects
|
2005-02-22 11:22:44 +00:00
|
|
|
|
forKeys: (id*)keys
|
2009-02-23 20:42:32 +00:00
|
|
|
|
count: (NSUInteger)count
|
2002-11-19 05:37:42 +00:00
|
|
|
|
{
|
2009-02-23 20:42:32 +00:00
|
|
|
|
NSUInteger size = (count * 4) / 3;
|
2002-11-19 05:37:42 +00:00
|
|
|
|
NSZone *z = NSDefaultMallocZone();
|
|
|
|
|
|
|
|
|
|
_map = NSCreateMapTableWithZone(GCInfoMapKeyCallBacks,
|
|
|
|
|
GCInfoValueCallBacks, size, z);
|
|
|
|
|
|
|
|
|
|
while (count-- > 0)
|
|
|
|
|
{
|
|
|
|
|
GCInfo *keyStruct;
|
|
|
|
|
GCInfo *valueStruct;
|
|
|
|
|
|
|
|
|
|
if (!keys[count] || !objects[count])
|
|
|
|
|
{
|
2010-02-25 18:49:31 +00:00
|
|
|
|
DESTROY(self);
|
2002-11-19 05:37:42 +00:00
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
|
format: @"Nil object added in dictionary"];
|
|
|
|
|
}
|
|
|
|
|
keyStruct = NSZoneMalloc(z, sizeof(GCInfo));
|
|
|
|
|
valueStruct = NSZoneMalloc(z, sizeof(GCInfo));
|
|
|
|
|
keyStruct->object = keys[count];
|
|
|
|
|
keyStruct->isGCObject = [keys[count] isKindOfClass: gcClass];
|
|
|
|
|
valueStruct->object = objects[count];
|
|
|
|
|
valueStruct->isGCObject
|
|
|
|
|
= [objects[count] isKindOfClass: gcClass];
|
|
|
|
|
NSMapInsert(_map, keyStruct, valueStruct);
|
|
|
|
|
}
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* We use the same initial instance variable layout as a GCObject and
|
|
|
|
|
* ue the <em>behavior</em> mechanism to inherit methods from that class
|
|
|
|
|
* to implement a form of multiple inheritance. We need to implement
|
|
|
|
|
* this method to make this apparent at runtime.
|
|
|
|
|
*/
|
|
|
|
|
- (BOOL) isKindOfClass: (Class)c
|
|
|
|
|
{
|
|
|
|
|
if (c == gcClass)
|
|
|
|
|
{
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
return [super isKindOfClass: c];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSEnumerator*) keyEnumerator
|
|
|
|
|
{
|
|
|
|
|
_GCDictionaryKeyEnumerator *e;
|
|
|
|
|
|
|
|
|
|
e = [_GCDictionaryKeyEnumerator alloc];
|
|
|
|
|
e->dict = [self retain];
|
|
|
|
|
e->enumerator = NSEnumerateMapTable(_map);
|
2003-04-01 04:27:18 +00:00
|
|
|
|
return AUTORELEASE(e);
|
2002-11-19 05:37:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSEnumerator*) objectEnumerator
|
|
|
|
|
{
|
|
|
|
|
_GCDictionaryObjectEnumerator *e;
|
|
|
|
|
|
|
|
|
|
e = [_GCDictionaryObjectEnumerator alloc];
|
|
|
|
|
e->dict = [self retain];
|
|
|
|
|
e->enumerator = NSEnumerateMapTable(_map);
|
2003-04-01 04:27:18 +00:00
|
|
|
|
return AUTORELEASE(e);
|
2002-11-19 05:37:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (id) mutableCopyWithZone: (NSZone*)zone
|
|
|
|
|
{
|
|
|
|
|
return [[GCMutableDictionary allocWithZone: zone] initWithDictionary: self];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (id) objectForKey: (id)key
|
|
|
|
|
{
|
|
|
|
|
GCInfo keyStruct = { key, 0 };
|
|
|
|
|
GCInfo *valueStruct;
|
|
|
|
|
|
|
|
|
|
valueStruct = NSMapGet(_map, (void**)&keyStruct);
|
|
|
|
|
return valueStruct ? valueStruct->object : nil;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@implementation GCMutableDictionary
|
|
|
|
|
|
|
|
|
|
+ (void) initialize
|
|
|
|
|
{
|
|
|
|
|
static BOOL beenHere = NO;
|
|
|
|
|
|
|
|
|
|
if (beenHere == NO)
|
|
|
|
|
{
|
|
|
|
|
beenHere = YES;
|
2002-11-27 12:52:29 +00:00
|
|
|
|
GSObjCAddClassBehavior(self, [GCDictionary class]);
|
2002-11-19 05:37:42 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (id) init
|
|
|
|
|
{
|
|
|
|
|
return [self initWithCapacity: 0];
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-23 20:42:32 +00:00
|
|
|
|
- (id) initWithCapacity: (NSUInteger)aNumItems
|
2002-11-19 05:37:42 +00:00
|
|
|
|
{
|
2009-02-23 20:42:32 +00:00
|
|
|
|
NSUInteger size = (aNumItems * 4) / 3;
|
2002-11-19 05:37:42 +00:00
|
|
|
|
|
|
|
|
|
_map = NSCreateMapTableWithZone(GCInfoMapKeyCallBacks,
|
|
|
|
|
GCInfoValueCallBacks, size, [self zone]);
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (id) copyWithZone: (NSZone*)zone
|
|
|
|
|
{
|
|
|
|
|
return [[GCDictionary allocWithZone: zone] initWithDictionary: self];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) setObject: (id)anObject forKey: (id)aKey
|
|
|
|
|
{
|
|
|
|
|
GCInfo *keyStruct;
|
|
|
|
|
GCInfo *valueStruct;
|
|
|
|
|
NSZone *z = NSDefaultMallocZone();
|
|
|
|
|
|
|
|
|
|
keyStruct = NSZoneMalloc(z, sizeof(GCInfo));
|
|
|
|
|
valueStruct = NSZoneMalloc(z, sizeof(GCInfo));
|
|
|
|
|
keyStruct->object = aKey;
|
|
|
|
|
keyStruct->isGCObject = [aKey isKindOfClass: gcClass];
|
|
|
|
|
valueStruct->object = anObject;
|
|
|
|
|
valueStruct->isGCObject = [anObject isKindOfClass: gcClass];
|
|
|
|
|
NSMapInsert(_map, keyStruct, valueStruct);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) removeObjectForKey: (id)key
|
|
|
|
|
{
|
|
|
|
|
GCInfo keyStruct = { key, 0 };
|
|
|
|
|
|
|
|
|
|
NSMapRemove(_map, (void**)&keyStruct);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) removeAllObjects
|
|
|
|
|
{
|
|
|
|
|
NSResetMapTable(_map);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|