From bfeb8dbf697fe65ea75834f260203ba5433faedf Mon Sep 17 00:00:00 2001 From: Richard Frith-Macdonald Date: Tue, 19 Nov 2002 05:37:42 +0000 Subject: [PATCH] Garbage collecting updates and moved more gnustep specifiec code to the Additions library git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@15016 72102866-910b-0410-8b05-ffd578937521 --- ChangeLog | 10 + Headers/gnustep/base/GCObject.h | 109 ++++++++ Headers/gnustep/base/behavior.h | 5 +- Source/Additions/GCArray.m | 405 ++++++++++++++++++++++++++++++ Source/Additions/GCDictionary.m | 403 +++++++++++++++++++++++++++++ Source/Additions/GCObject.m | 338 +++++++++++++++++++++++++ Source/Additions/GNUmakefile | 7 +- Source/{ => Additions}/Unicode.m | 0 Source/{ => Additions}/behavior.m | 0 Source/DocMakefile | 3 + Source/GNUmakefile | 3 +- 11 files changed, 1279 insertions(+), 4 deletions(-) create mode 100644 Headers/gnustep/base/GCObject.h create mode 100644 Source/Additions/GCArray.m create mode 100644 Source/Additions/GCDictionary.m create mode 100644 Source/Additions/GCObject.m rename Source/{ => Additions}/Unicode.m (100%) rename Source/{ => Additions}/behavior.m (100%) diff --git a/ChangeLog b/ChangeLog index dd09f77dc..bf72bde41 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2002-11-19 Richard Frith-Macdonald + + * Source/behavior.m: Moved to Source/Additions/behavior.m + * Source/Unicode.m: Moved to Source/Additions/Unicode.m + * Source/Additions/GCObject.m: new experimental GC class. + * Source/Additions/GCArray.m: ditto + * Source/Additions/GCDictionary.m: ditto + * Headers/gnustep/base/GCObject.h: Garbage collection classes intended + for use by gdl2 and gsweb. + 2002-11-18 Richard Frith-Macdonald * Source/GSCompatibility.m: Fix for case where a non property list diff --git a/Headers/gnustep/base/GCObject.h b/Headers/gnustep/base/GCObject.h new file mode 100644 index 000000000..20abd40d4 --- /dev/null +++ b/Headers/gnustep/base/GCObject.h @@ -0,0 +1,109 @@ +/** Interface for simple garbage collected classes + + Copyright (C) 2002 Free Software Foundation, Inc. + + Written by: Richard Frith-Macdonald + 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 + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + 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 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. + + AutogsdocSource: Additions/GCObject.m + AutogsdocSource: Additions/GCArray.m + AutogsdocSource: Additions/GCDictionary.m + +*/ + +#ifndef __INCLUDED_GCOBJECT_H +#define __INCLUDED_GCOBJECT_H + +#include +#include +#include +#include + + +@class GCObject; + +typedef struct { + GCObject *next; + GCObject *previous; + struct { + unsigned visited:1; + unsigned refCount:31; + } flags; +} gcInfo; + +@interface GCObject : NSObject +{ + gcInfo gc; +} + ++ (void) gcCollectGarbage; ++ (BOOL) gcIsCollecting; ++ (void) gcObjectWillBeDeallocated: (GCObject*)anObject; + +- (void) gcDecrementRefCount; +- (void) gcDecrementRefCountOfContainedObjects; +- (void) gcIncrementRefCount; +- (BOOL) gcIncrementRefCountOfContainedObjects; + +@end + +@interface GCObject (Extra) +- (GCObject*) gcNextObject; +- (GCObject*) gcPreviousObject; +- (GCObject*) gcSetNextObject: (GCObject*)anObject; +- (GCObject*) gcSetPreviousObject: (GCObject*)anObject; +- (BOOL) gcAlreadyVisited; +- (void) gcSetVisited: (BOOL)flag; +@end + +@interface GCArray : NSArray +{ + gcInfo gc; + id *_contents; // C array of content objects + BOOL *_isGCObject; // Is content object collectable? + unsigned int _count; // Number of content objects. +} +@end + + +@interface GCMutableArray : NSMutableArray +{ + gcInfo gc; + id *_contents; + BOOL *_isGCObject; + unsigned _count; + unsigned _maxCount; // Maximum number of content objects. +} +@end + +@interface GCDictionary : NSDictionary +{ + gcInfo gc; + NSMapTable *_map; +} +@end + +@interface GCMutableDictionary : NSMutableDictionary +{ + gcInfo gc; + NSMapTable *_map; +} +@end + +#endif /* __INCLUDED_GCOBJECT_H */ diff --git a/Headers/gnustep/base/behavior.h b/Headers/gnustep/base/behavior.h index 6d2dca15d..090f0f6d0 100644 --- a/Headers/gnustep/base/behavior.h +++ b/Headers/gnustep/base/behavior.h @@ -1,4 +1,4 @@ -/* Interface for behaviors for Obj-C, "for Protocols with implementations". +/** Interface for behaviors for Obj-C, "for Protocols with implementations". Copyright (C) 1995, 1996 Free Software Foundation, Inc. Written by: Andrew Kachites McCallum @@ -19,6 +19,9 @@ You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. + + AutogsdocSource: Additions/behavior.m + */ #ifndef __behavior_h_GNUSTEP_BASE_INCLUDE diff --git a/Source/Additions/GCArray.m b/Source/Additions/GCArray.m new file mode 100644 index 000000000..acb75adf0 --- /dev/null +++ b/Source/Additions/GCArray.m @@ -0,0 +1,405 @@ +/* Implementation of garbage collecting array classes. + + Copyright (C) 2002 Free Software Foundation, Inc. + + Written by: Richard Frith-Macdonald + 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 + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + 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 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. + +*/ + +#include +#include +#include + +#include +#include + +@implementation GCArray + +static Class gcClass = 0; + ++ (void) initialize +{ + if (gcClass == 0) + { + gcClass = [GCObject class]; + behavior_class_add_class(self, gcClass); + } +} + +- (Class) classForCoder +{ + return [GCArray class]; +} + +- (id) copyWithZone: (NSZone*)zone +{ + if (NSShouldRetainWithZone(self, zone)) + { + return [self retain]; + } + return [[GCArray allocWithZone: zone] initWithArray: self copyItems: YES]; +} + +- (unsigned int) count +{ + return _count; +} + +- (void) dealloc +{ + unsigned int c = _count; + + [GCObject gcObjectWillBeDeallocated: (GCObject*)self]; + if ([GCObject gcIsCollecting]) + { + while (c-- > 0) + { + if (_isGCObject[c] == NO) + { + [_contents[c] release]; + } + } + } + else + { + while (c-- > 0) + { + [_contents[c] release]; + } + } + + NSZoneFree([self zone], _contents); + [super dealloc]; +} + +- (void) gcDecrementRefCountOfContainedObjects +{ + unsigned int c = _count; + + gc.flags.visited = 0; + while (c-- > 0) + { + if (_isGCObject[c]) + { + [_contents[c] gcDecrementRefCount]; + } + } +} + +- (BOOL) gcIncrementRefCountOfContainedObjects +{ + if (gc.flags.visited == 1) + { + return NO; + } + else + { + unsigned int c = _count; + + gc.flags.visited = 1; + while (c-- > 0) + { + if (_isGCObject[c]) + { + [_contents[c] gcIncrementRefCount]; + [_contents[c] gcIncrementRefCountOfContainedObjects]; + } + } + return YES; + } +} + +- (id) initWithObjects: (id*)objects count: (unsigned int)count +{ + unsigned int i; + + _contents = NSZoneMalloc([self zone], count * (sizeof(id) + sizeof(BOOL))); + _isGCObject = (BOOL*)&_contents[count]; + _count = count; + for (i = 0; i < count; i++) + { + _contents[i] = [objects[i] retain]; + if (_contents[i] == nil) + { + [self release]; + [NSException raise: NSInvalidArgumentException + format: @"Nil object to be added in array"]; + } + else + { + _isGCObject[i] = [objects[i] isKindOfClass: gcClass]; + } + } + return self; +} + +- (id) initWithArray: (NSArray*)anotherArray +{ + unsigned int i; + unsigned int count = [anotherArray count]; + + _contents = NSZoneMalloc([self zone], count * (sizeof(id) + sizeof(BOOL))); + _isGCObject = (BOOL*)&_contents[count]; + _count = count; + for (i = 0; i < _count; i++) + { + _contents[i] = [[anotherArray objectAtIndex: i] retain]; + _isGCObject[i] = [_contents[i] isKindOfClass: gcClass]; + } + return self; +} + +/** + * We use the same initial instance variable layout as a GCObject and + * ue the behavior 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]; +} + +- (id) mutableCopyWithZone: (NSZone*)zone +{ + return [[GCMutableArray allocWithZone: zone] + initWithArray: self copyItems: YES]; +} + +- (id) objectAtIndex: (unsigned int)index +{ + if (index >= _count) + { + [NSException raise: NSRangeException + format: @"[%@-%@]: index: %u", + NSStringFromClass([self class]), NSStringFromSelector(_cmd), index]; + } + return _contents[index]; +} + +@end + + + +@implementation GCMutableArray + ++ (void)initialize +{ + static BOOL beenHere = NO; + + if (beenHere == NO) + { + beenHere = YES; + behavior_class_add_class(self, [GCArray class]); + } +} + +- (void) addObject: (id)anObject +{ + [self insertObject: anObject atIndex: _count]; +} + +- (Class) classForCoder +{ + return [GCMutableArray class]; +} + +- (id) copyWithZone: (NSZone*)zone +{ + return [[GCArray allocWithZone: zone] + initWithArray: self copyItems: YES]; +} + +- (id) init +{ + return [self initWithCapacity: 1]; +} + +- (id) initWithCapacity: (unsigned int)aNumItems +{ + if (aNumItems < 1) + { + aNumItems = 1; + } + _contents = NSZoneMalloc([self zone], aNumItems * (sizeof(id) + sizeof(BOOL))); + _isGCObject = (BOOL*)&_contents[aNumItems]; + _maxCount = aNumItems; + _count = 0; + return self; +} + +- (id) initWithObjects: (id *)objects count: (unsigned int)count +{ + self = [self initWithCapacity: count]; + if (self != nil) + { + unsigned int i; + + for (i = 0; i < count; i++) + { + _contents[i] = [objects[i] retain]; + if (_contents[i] == nil) + { + [self release]; + [NSException raise: NSInvalidArgumentException + format: @"Nil object to be added in array"]; + } + else + { + _isGCObject[i] = [objects[i] isKindOfClass: gcClass]; + } + } + } + return self; +} + +- (id) initWithArray: (NSArray*)anotherArray +{ + unsigned int count = [anotherArray count]; + + self = [self initWithCapacity: count]; + if (self != nil) + { + unsigned int i; + + for (i = 0; i < _count; i++) + { + _contents[i] = [[anotherArray objectAtIndex: i] retain]; + _isGCObject[i] = [_contents[i] isKindOfClass: gcClass]; + } + } + return self; +} + +- (void) insertObject: (id)anObject atIndex: (unsigned int)index +{ + unsigned int i; + + if (anObject == nil) + { + [NSException raise: NSInvalidArgumentException + format: @"[%@-%@]: nil argument", + NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; + } + if (index > _count) + { + [NSException raise: NSRangeException + format: @"[%@-%@]: bad index %u", + NSStringFromClass([self class]), NSStringFromSelector(_cmd), index]; + } + + if (_count == _maxCount) + { + unsigned old = _maxCount; + BOOL *optr; + + if (_maxCount > 0) + { + _maxCount += (_maxCount >> 1) ? (_maxCount >> 1) : 1; + } + else + { + _maxCount = 1; + } + _contents = (id*)NSZoneRealloc([self zone], _contents, + _maxCount * (sizeof(id) + sizeof(BOOL))); + optr = (BOOL*)&_contents[old]; + _isGCObject = (BOOL*)&_contents[_maxCount]; + memmove(_isGCObject, optr, sizeof(BOOL)*old); + } + for(i = _count; i > index; i--) + { + _contents[i] = _contents[i - 1]; + _isGCObject[i] = _isGCObject[i - 1]; + } + _contents[index] = [anObject retain]; + _isGCObject[index] = [anObject isKindOfClass: gcClass]; + _count++; +} + +- (id) mutableCopyWithZone: (NSZone*)zone +{ + return [[GCMutableArray allocWithZone: zone] + initWithArray: self copyItems: YES]; +} + +- (void) removeAllObjects +{ + [self removeObjectsInRange: NSMakeRange(0, _count)]; +} + +- (void) removeObjectAtIndex: (unsigned int)index +{ + [self removeObjectsInRange: NSMakeRange(index, 1)]; +} + +- (void) removeObjectsInRange: (NSRange)range +{ + unsigned int i; + + if (NSMaxRange(range) > _count) + { + [NSException raise: NSRangeException + format: @"[%@-%@]: bad range %@", + NSStringFromClass([self class]), NSStringFromSelector(_cmd), + NSStringFromRange(range)]; + } + if (range.length == 0) + { + return; + } + for (i = range.location; i < NSMaxRange(range); i++) + { + [_contents[i] release]; + } + for (i = NSMaxRange(range); i < _count; i++, range.location++) + { + _contents[range.location] = _contents[i]; + _isGCObject[range.location] = _isGCObject[i]; + } + _count -= range.length; +} + +- (void) replaceObjectAtIndex: (unsigned int)index withObject: (id)anObject +{ + if (anObject == nil) + { + [NSException raise: NSInvalidArgumentException + format: @"[%@-%@]: nil argument", + NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; + } + if (index >= _count) + { + [NSException raise: NSRangeException + format: @"[%@-%@]: bad index %u", + NSStringFromClass([self class]), NSStringFromSelector(_cmd), index]; + } + [anObject retain]; + [_contents[index] release]; + _contents[index] = anObject; + _isGCObject[index] = [anObject isKindOfClass: gcClass]; +} + +@end + diff --git a/Source/Additions/GCDictionary.m b/Source/Additions/GCDictionary.m new file mode 100644 index 000000000..2a33fd0ac --- /dev/null +++ b/Source/Additions/GCDictionary.m @@ -0,0 +1,403 @@ +/* Implementation of garbage collecting dictionary classes + + Copyright (C) 2002 Free Software Foundation, Inc. + + Written by: Richard Frith-Macdonald + 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 + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + 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 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. + +*/ + +#include +#include + +#include +#include + +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); + [dict release]; + [super dealloc]; +} +- (id) nextObject +{ + GCInfo *keyStruct; + GCInfo *valueStruct; + + return NSNextMapEnumeratorPair(&enumerator, + (void**)&keyStruct, (void**)&valueStruct) ? keyStruct->object : nil; +} +@end + +@implementation _GCDictionaryObjectEnumerator +- (id) nextObject +{ + GCInfo *keyStruct; + GCInfo *valueStruct; + + return NSNextMapEnumeratorPair(&enumerator, + (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) +{ + GCInfo *objectStruct = (GCInfo*)ptr; + + [objectStruct->object retain]; +} + +static void +_GCReleaseObjects(NSMapTable *table, const void *ptr) +{ + GCInfo *objectStruct = (GCInfo*)ptr; + + if ([GCObject gcIsCollecting]) + { + if (objectStruct->isGCObject == NO) + { + [objectStruct->object release]; + } + } + else + { + [objectStruct->object release]; + } + NSZoneFree(NSDefaultMallocZone(), objectStruct); +} + +static NSString* +_GCDescribeObjects(NSMapTable *table, const GCInfo *objectStruct) +{ + return [objectStruct->object description]; +} + +static const NSMapTableKeyCallBacks GCInfoMapKeyCallBacks = { + (unsigned(*)(NSMapTable *, const void *))_GCHashObject, + (BOOL(*)(NSMapTable *, const void *, const void *))_GCCompareObjects, + (void (*)(NSMapTable *, const void *))_GCRetainObjects, + (void (*)(NSMapTable *, const void *))_GCReleaseObjects, + (NSString *(*)(NSMapTable *, const void *))_GCDescribeObjects, + (const void *)NULL +}; + +static const NSMapTableValueCallBacks GCInfoValueCallBacks = { + (void (*)(NSMapTable *, const void *))_GCRetainObjects, + (void (*)(NSMapTable *, const void *))_GCReleaseObjects, + (NSString *(*)(NSMapTable *, const void *))_GCDescribeObjects +}; + +static Class gcClass = 0; + ++ (void) initialize +{ + if (gcClass == 0) + { + gcClass = [GCObject class]; + behavior_class_add_class(self, gcClass); + } +} + +- (id) copyWithZone: (NSZone*)zone +{ + if (NSShouldRetainWithZone(self, zone)) + { + return [self retain]; + } + return [[GCDictionary allocWithZone: zone] initWithDictionary: self]; +} + +- (unsigned int) count +{ + 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; + unsigned int size = ([dictionary count] * 4) / 3; + 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); + } + + return self; +} + +- (id) initWithObjects: (id*)objects + forKeys: (id*)keys + count: (unsigned int)count +{ + unsigned int size = (count * 4) / 3; + NSZone *z = NSDefaultMallocZone(); + + _map = NSCreateMapTableWithZone(GCInfoMapKeyCallBacks, + GCInfoValueCallBacks, size, z); + + while (count-- > 0) + { + GCInfo *keyStruct; + GCInfo *valueStruct; + + if (!keys[count] || !objects[count]) + { + [self release]; + [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 behavior 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); + return [e autorelease]; +} + +- (NSEnumerator*) objectEnumerator +{ + _GCDictionaryObjectEnumerator *e; + + e = [_GCDictionaryObjectEnumerator alloc]; + e->dict = [self retain]; + e->enumerator = NSEnumerateMapTable(_map); + return [e autorelease]; +} + +- (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; + behavior_class_add_class(self, [GCDictionary class]); + } +} + +- (id) init +{ + return [self initWithCapacity: 0]; +} + +- (id) initWithCapacity: (unsigned int)aNumItems +{ + unsigned int size = (aNumItems * 4) / 3; + + _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 diff --git a/Source/Additions/GCObject.m b/Source/Additions/GCObject.m new file mode 100644 index 000000000..8f59cb96b --- /dev/null +++ b/Source/Additions/GCObject.m @@ -0,0 +1,338 @@ +/* Implementation of garbage collecting classe framework + + Copyright (C) 2002 Free Software Foundation, Inc. + + Written by: Richard Frith-Macdonald + 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 + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + 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 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. + + AutogsdocSource: Additions/GCObject.m + AutogsdocSource: Additions/GCArray.m + AutogsdocSource: Additions/GCDictionary.m + +*/ + +#include +#include + +#include + +/* + * The head of a linked list of all garbage collecting objects is a + * special object which is never deallocated. + */ +@interface _GCObjectList : GCObject +@end +@implementation _GCObjectList +- (void) dealloc +{ +} +@end + + +/** + * The GCObject class is both the base class for all garbage collected + * objects, and an infrastructure for handling garbage collection.
+ * It maintains a list of all garbage collectable objects and provides + * a method to run a garbage collection pass on those objects. + */ +@implementation GCObject + +static GCObject *allObjects = nil; +static BOOL isCollecting = NO; + ++ (id) allocWithZone: (NSZone*)zone +{ + GCObject *o = [super allocWithZone: zone]; + + o->gc.next = allObjects; + o->gc.previous = allObjects->gc.previous; + allObjects->gc.previous->gc.next = o; + allObjects->gc.previous = o; + o->gc.flags.refCount = 1; + + return o; +} + +/** + *

This method runs a garbage collection, causing unreferenced objects to + * be deallocated. This is done using a simple three pass algorithm - + *

+ * + * Pass 1 + * + * All the garbage collectable objects are sent a + * -gcDecrementRefCountOfContainedObjects message. + * + * Pass 2 + * + * All objects having a refCount greater than 0 are sent an + * -gcIncrementRefCountOfContainedObjects message. + * + * Pass 3 + * + * All the objects that still have the refCount of 0 + * are part of cyclic graphs and none of the objects from this graph + * are held by some object outside graph. These objects receive the + * -dealloc message. In this method they should send the -dealloc message + * to any garbage collectable (GCObject and subclass) instances they + * contain. + * + * + *

During garbage collection, the +gcIsCollecting method returns YES. + *

+ */ ++ (void) gcCollectGarbage +{ + GCObject *object; + GCObject *last; + + if (isCollecting == YES) + { + return; // Don't allow recursion. + } + isCollecting = YES; + + // Pass 1 + object = allObjects->gc.next; + while (object != allObjects) + { + [object gcDecrementRefCountOfContainedObjects]; + // object->gc.flags.visited = 0; + // object = object->gc.next; + [object gcSetVisited: NO]; + object = [object gcNextObject]; + } + + // Pass 2 + object = allObjects->gc.next; + while (object != allObjects) + { + if ([object retainCount] > 0) + { + [object gcIncrementRefCountOfContainedObjects]; + } + // object = object->gc.next; + object = [object gcNextObject]; + } + + last = allObjects; + object = last->gc.next; + while (object != allObjects) + { + if ([object retainCount] == 0) + { + GCObject *next; + + // next = object->gc.next; + // next->gc.previous = last; + // last->gc.next = next; + // object->gc.next = object; + // object->gc.previous = object; + next = [object gcNextObject]; + [next gcSetPreviousObject: last]; + [last gcSetNextObject: next]; + [object gcSetNextObject: object]; + [object gcSetPreviousObject: object]; + [object dealloc]; + object = next; + } + else + { + last = object; + // object = object->gc.next; + object = [object gcNextObject]; + } + } + isCollecting = NO; +} + ++ (void) initialize +{ + if (self == [GCObject class]) + { + allObjects = (_GCObjectList*) + NSAllocateObject([_GCObjectList class], 0, NSDefaultMallocZone()); + } +} + +/** + * Returns a flag to indicate whether a garbage collection is in progress. + */ ++ (BOOL) gcIsCollecting +{ + return isCollecting; +} + +/** + * Called to remove anObject from the list of garbage collectable objects. + * Subclasses should call this is their -dealloc methods. + */ ++ (void) gcObjectWillBeDeallocated: (GCObject*)anObject +{ + GCObject *p; + GCObject *n; + + // p = anObject->gc.previous; + // n = anObject->gc.next; + // p->gc.next = n; + // n->gc.previous = p; + p = [anObject gcPreviousObject]; + n = [anObject gcNextObject]; + [p gcSetNextObject: n]; + [n gcSetPreviousObject: p]; +} + +- (id) copyWithZone: (NSZone*)zone +{ + GCObject *o = (GCObject*)NSCopyObject(self, 0, zone); + + o->gc.next = allObjects; + o->gc.previous = allObjects->gc.previous; + allObjects->gc.previous->gc.next = o; + allObjects->gc.previous = o; + o->gc.flags.refCount = 1; + return o; +} + +/* + * Decrements the garbage collection reference count for the receiver.
+ */ +- (void) gcDecrementRefCount +{ + gc.flags.refCount--; +} + +/* + *

Marks the receiver as not having been visited in the current garbage + * collection process (first pass of collection). + *

+ *

All container subclasses should override this method to call the super + * implementation then decrement the ref counts of their contents as well as + * sending the -gcDecrementRefCountOfContainedObjects + * message to each of them. + *

+ */ +- (void) gcDecrementRefCountOfContainedObjects +{ + gc.flags.visited = 0; +} + +/* + * Increments the garbage collection reference count for the receiver.
+ */ +- (void) gcIncrementRefCount +{ + gc.flags.refCount++; +} + +/* + *

Checks to see if the receiver has already been visited in the + * current garbage collection process, and either marks the receiver as + * visited (and returns YES) or returns NO to indicate that it had already + * been visited. + *

+ *

All container subclasses should override this method to call the super + * implementation then, if the method returns YES, increment the reference + * count of any contained objects and send the + * -gcIncrementRefCountOfContainedObjects + * to each of the contained objects too. + *

+ */ +- (BOOL) gcIncrementRefCountOfContainedObjects +{ + if (gc.flags.visited == 1) + { + return NO; + } + gc.flags.visited = 1; + return YES; +} + +- (oneway void) release +{ + if (gc.flags.refCount > 0 && gc.flags.refCount-- == 1) + { + [GCObject gcObjectWillBeDeallocated: self]; + [self dealloc]; + } +} + +- (id) retain +{ + gc.flags.refCount++; + return self; +} + +- (unsigned int) retainCount +{ + return gc.flags.refCount; +} + +@end + +@implementation GCObject (Extra) + +- (BOOL) gcAlreadyVisited +{ + if (gc.flags.visited == 1) + { + return YES; + } + else + { + return NO; + } +} + +- (GCObject*) gcNextObject +{ + return gc.next; +} + +- (GCObject*) gcPreviousObject +{ + return gc.previous; +} + +- (GCObject*) gcSetNextObject: (GCObject*)anObject +{ + gc.next = anObject; + return self; +} + +- (GCObject*) gcSetPreviousObject: (GCObject*)anObject +{ + gc.previous = anObject; + return self; +} + +- (void) gcSetVisited: (BOOL)flag +{ + if (flag == YES) + { + gc.flags.visited = 1; + } + else + { + gc.flags.visited = 0; + } +} + +@end + diff --git a/Source/Additions/GNUmakefile b/Source/Additions/GNUmakefile index 8464f950f..6d5c95b77 100644 --- a/Source/Additions/GNUmakefile +++ b/Source/Additions/GNUmakefile @@ -31,8 +31,13 @@ include $(GNUSTEP_MAKEFILES)/common.make SUBPROJECT_NAME=Additions Additions_OBJC_FILES =\ + GCObject.m \ + GCArray.m \ + GCDictionary.m \ GSMime.m \ - GSXML.m + GSXML.m \ + Unicode.m \ + behavior.m -include Makefile.preamble diff --git a/Source/Unicode.m b/Source/Additions/Unicode.m similarity index 100% rename from Source/Unicode.m rename to Source/Additions/Unicode.m diff --git a/Source/behavior.m b/Source/Additions/behavior.m similarity index 100% rename from Source/behavior.m rename to Source/Additions/behavior.m diff --git a/Source/DocMakefile b/Source/DocMakefile index 74721e085..a89b2a5b7 100644 --- a/Source/DocMakefile +++ b/Source/DocMakefile @@ -36,6 +36,9 @@ Base_AGSDOC_FILES = \ ../Documentation/Base.gsdoc \ GSMime.h \ GSXML.h \ +behavior.h \ +Unicode.h \ +GCObject.h \ NSArchiver.h \ NSArray.h \ NSAttributedString.h \ diff --git a/Source/GNUmakefile b/Source/GNUmakefile index 5d2e9a59a..cae3ed5c7 100644 --- a/Source/GNUmakefile +++ b/Source/GNUmakefile @@ -88,8 +88,6 @@ endif GNU_MFILES = \ GSCompatibility.m \ GSLocale.m \ -Unicode.m \ -behavior.m \ preface.m \ mframe.m @@ -118,6 +116,7 @@ libgnustep-base.def GNU_HEADERS = \ DistributedObjects.h \ +GCObject.h \ GSFileHandle.h \ GSLocale.h \ GSUnion.h \