libs-base/Source/NSPointerArray.m
David Chisnall 1c2ce264aa Fix NSPointerArray so that it cleans up its array on dealloc. This is needed
to avoid memory leaks with strong references and heap corruption with weak
ones.



git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@35878 72102866-910b-0410-8b05-ffd578937521
2012-12-11 18:29:49 +00:00

610 lines
13 KiB
Objective-C

/**Implementation for NSPointerArray for GNUStep
Copyright (C) 2009 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald <rfm@gnu.org>
Date: 2009
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 Lesser 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 Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02111 USA.
*/
#import "common.h"
#import "Foundation/NSPointerArray.h"
#import "GNUstepBase/GSObjCRuntime.h"
#import "Foundation/NSDictionary.h"
#import "Foundation/NSEnumerator.h"
#import "Foundation/NSException.h"
#import "Foundation/NSValue.h"
#import "Foundation/NSKeyedArchiver.h"
#import "GNUstepBase/NSObject+GNUstepBase.h"
#import "GSPrivate.h"
#import "NSConcretePointerFunctions.h"
static Class abstractClass = Nil;
static Class concreteClass = Nil;
@interface NSConcretePointerArray : NSPointerArray
{
PFInfo _pf;
NSUInteger _count;
void **_contents;
unsigned _capacity;
unsigned _grow_factor;
}
@end
@implementation NSPointerArray
+ (id) allocWithZone: (NSZone*)z
{
if (abstractClass == self)
{
return NSAllocateObject(concreteClass, 0, z);
}
return [super allocWithZone: z];
}
+ (void) initialize
{
if (abstractClass == Nil)
{
abstractClass = [NSPointerArray class];
concreteClass = [NSConcretePointerArray class];
}
}
+ (id) pointerArrayWithOptions: (NSPointerFunctionsOptions)options
{
return AUTORELEASE([[self alloc] initWithOptions: options]);
}
+ (id) pointerArrayWithPointerFunctions: (NSPointerFunctions *)functions
{
return AUTORELEASE([[self alloc] initWithPointerFunctions: functions]);
}
+ (id) strongObjectsPointerArray
{
return [self pointerArrayWithOptions: NSPointerFunctionsObjectPersonality |
NSPointerFunctionsStrongMemory];
}
+ (id) weakObjectsPointerArray
{
return [self pointerArrayWithOptions: NSPointerFunctionsObjectPersonality |
NSPointerFunctionsWeakMemory];
}
- (void) compact
{
[self subclassResponsibility: _cmd];
}
- (id) copyWithZone: (NSZone*)zone
{
[self subclassResponsibility: _cmd];
return nil;
}
- (NSUInteger) count
{
[self subclassResponsibility: _cmd];
return 0;
}
- (void) encodeWithCoder: (NSCoder*)aCoder
{
[self subclassResponsibility: _cmd];
}
- (id) init
{
return [self initWithOptions: 0];
}
- (id) initWithCoder: (NSCoder*)aCoder
{
[self subclassResponsibility: _cmd];
return nil;
}
- (id) initWithOptions: (NSPointerFunctionsOptions)options
{
NSPointerFunctions *functions;
functions = [NSPointerFunctions pointerFunctionsWithOptions: options];
return [self initWithPointerFunctions: functions];
}
- (id) initWithPointerFunctions: (NSPointerFunctions*)functions
{
[self subclassResponsibility: _cmd];
return nil;
}
- (BOOL) isEqual: (id)other
{
NSUInteger count;
if (other == self)
{
return YES;
}
if ([other isKindOfClass: abstractClass] == NO)
{
return NO;
}
if ([other hash] != [self hash])
{
return NO;
}
count = [self count];
while (count-- > 0)
{
// FIXME
}
return YES;
}
- (void) addPointer: (void*)pointer
{
[self insertPointer: pointer atIndex: [self count]];
}
- (void) insertPointer: (void*)pointer atIndex: (NSUInteger)index
{
[self subclassResponsibility: _cmd];
}
- (void*) pointerAtIndex: (NSUInteger)index
{
[self subclassResponsibility: _cmd];
return 0;
}
- (NSPointerFunctions*) pointerFunctions
{
[self subclassResponsibility: _cmd];
return nil;
}
- (void) removePointerAtIndex: (NSUInteger)index
{
[self subclassResponsibility: _cmd];
}
- (void) replacePointerAtIndex: (NSUInteger)index withPointer: (void*)item
{
[self subclassResponsibility: _cmd];
}
- (void) setCount: (NSUInteger)count
{
[self subclassResponsibility: _cmd];
}
@end
@implementation NSPointerArray (NSArrayConveniences)
+ (id) pointerArrayWithStrongObjects
{
return [self pointerArrayWithOptions: NSPointerFunctionsStrongMemory];
}
+ (id) pointerArrayWithWeakObjects
{
return [self pointerArrayWithOptions: NSPointerFunctionsZeroingWeakMemory];
}
- (NSArray*) allObjects
{
[self subclassResponsibility: _cmd];
return nil;
}
@end
@implementation NSConcretePointerArray
- (void) _raiseRangeExceptionWithIndex: (NSUInteger)index from: (SEL)sel
{
NSDictionary *info;
NSException *exception;
NSString *reason;
info = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithUnsignedInt: index], @"Index",
[NSNumber numberWithUnsignedInt: _count], @"Count",
self, @"Array", nil, nil];
reason = [NSString stringWithFormat:
@"Index %d is out of range %d (in '%@')",
index, _count, NSStringFromSelector(sel)];
exception = [NSException exceptionWithName: NSRangeException
reason: reason
userInfo: info];
[exception raise];
}
- (NSArray*) allObjects
{
NSUInteger i;
NSUInteger c = 0;
for (i = 0; i < _count; i++)
{
if (pointerFunctionsRead(&_pf, &_contents[i]) != 0)
{
c++;
}
}
if (0 == c)
{
return [NSArray array];
}
else
{
GSMutableArray *a = [GSMutableArray arrayWithCapacity: c];
for (i = 0; i < _count; i++)
{
id obj = pointerFunctionsRead(&_pf, &_contents[i]);
if (obj != 0)
{
[a addObject: obj];
}
}
return [a makeImmutableCopyOnFail: NO];
}
}
- (void) compact
{
NSUInteger insert = 0;
NSUInteger i;
// We can't use memmove here for __weak pointers, because that would omit the
// required read barriers. We could use objc_memmoveCollectable() for strong
// pointers, but we may as well use the same code path for everything
for (i=0 ; i<_count ; i++)
{
id obj = pointerFunctionsRead(&_pf, &_contents[i]);
// If this object is not nil, but at least one before it has been, then
// move it back to the correct location.
if (nil != obj && i != insert)
{
pointerFunctionsAssign(&_pf, &_contents[insert++], obj);
}
}
_count = insert;
}
- (id) copyWithZone: (NSZone*)zone
{
NSConcretePointerArray *c;
unsigned i;
c = (NSConcretePointerArray*)NSCopyObject(self, 0, NSDefaultMallocZone());
c->_capacity = c->_count;
c->_grow_factor = c->_capacity/2;
#if GS_WITH_GC
if (_pf.options & NSPointerFunctionsZeroingWeakMemory)
{
c->_contents = NSAllocateCollectable(sizeof(id) * _count, 0);
}
else
{
c->_contents = NSAllocateCollectable(sizeof(id) * _count,
NSScannedOption);
}
#else
c->_contents = NSZoneCalloc([self zone], _count, sizeof(id));
#endif
for (i = 0; i < _count; i++)
{
NSLog(@"Copying %d, %p", i, _contents[i]);
pointerFunctionsAcquire(&_pf, &c->_contents[i],
pointerFunctionsRead(&_pf, &_contents[i]));
}
return c;
}
- (NSUInteger) count
{
return _count;
}
- (void) dealloc
{
[self finalize];
// For weak memory, we must zero all of the elements, or the runtime will
// keep pointers to them lying around. For strong memory, we must release
// things or they will leak.
for (int i=0 ; i<_count ; i++)
{
pointerFunctionsAssign(&_pf, &_contents[i], 0);
}
if (_contents != 0)
{
NSZoneFree([self zone], _contents);
}
[super dealloc];
}
- (void) encodeWithCoder: (NSCoder*)aCoder
{
/* FIXME ... how can we meaningfully encode the pointer functions???
*/
[self notImplemented: _cmd];
if ([aCoder allowsKeyedCoding])
{
[super encodeWithCoder: aCoder];
}
else
{
/* For performace we encode directly ... must exactly match the
* superclass implemenation. */
[aCoder encodeValueOfObjCType: @encode(NSUInteger)
at: &_count];
if (_count > 0)
{
[aCoder encodeArrayOfObjCType: @encode(id)
count: _count
at: _contents];
}
}
}
- (NSUInteger) hash
{
return _count;
}
- (id) initWithCoder: (NSCoder*)aCoder
{
/* FIXME ... how can we meaningfully encode the pointer functions???
*/
[self notImplemented: _cmd];
if ([aCoder allowsKeyedCoding])
{
self = [super initWithCoder: aCoder];
}
else
{
/* for performance, we decode directly into memory rather than
* using the superclass method. Must exactly match superclass. */
[aCoder decodeValueOfObjCType: @encode(NSUInteger)
at: &_count];
if (_count > 0)
{
#if GS_WITH_GC
if (_pf.options & NSPointerFunctionsZeroingWeakMemory)
{
_contents = NSAllocateCollectable(sizeof(id) * _count, 0);
}
else
{
_contents = NSAllocateCollectable(sizeof(id) * _count,
NSScannedOption);
}
#else
_contents = NSZoneCalloc([self zone], _count, sizeof(id));
#endif
if (_contents == 0)
{
[NSException raise: NSMallocException
format: @"Unable to make array"];
}
[aCoder decodeArrayOfObjCType: @encode(id)
count: _count
at: _contents];
}
}
return self;
}
- (id) initWithOptions: (NSPointerFunctionsOptions)options
{
NSConcretePointerFunctions *f;
f = [[NSConcretePointerFunctions alloc] initWithOptions: options];
self = [self initWithPointerFunctions: f];
[f release];
return self;
}
- (id) initWithPointerFunctions: (NSPointerFunctions*)functions
{
if (![functions isKindOfClass: [NSConcretePointerFunctions class]])
{
static NSConcretePointerFunctions *defaultFunctions = nil;
if (defaultFunctions == nil)
{
defaultFunctions
= [[NSConcretePointerFunctions alloc] initWithOptions: 0];
}
functions = defaultFunctions;
}
memcpy(&_pf, &((NSConcretePointerFunctions*)functions)->_x, sizeof(_pf));
return self;
}
- (void) insertPointer: (void*)pointer atIndex: (NSUInteger)index
{
NSUInteger i;
if (index > _count)
{
[self _raiseRangeExceptionWithIndex: index from: _cmd];
}
i = _count;
[self setCount: _count + 1];
while (i > index)
{
pointerFunctionsMove(&_pf, _contents+i, _contents + i-1);
i--;
}
pointerFunctionsAcquire(&_pf, &_contents[index], pointer);
}
- (BOOL) isEqual: (id)other
{
NSUInteger count;
if (other == self)
{
return YES;
}
if ([other isKindOfClass: abstractClass] == NO)
{
return NO;
}
if ([other hash] != [self hash])
{
return NO;
}
count = [self count];
while (count-- > 0)
{
if (pointerFunctionsEqual(&_pf,
pointerFunctionsRead(&_pf, &_contents[count]),
[other pointerAtIndex: count]) == NO)
return NO;
}
return YES;
}
- (void*) pointerAtIndex: (NSUInteger)index
{
if (index >= _count)
{
[self _raiseRangeExceptionWithIndex: index from: _cmd];
}
return pointerFunctionsRead(&_pf, &_contents[index]);
}
- (NSPointerFunctions*) pointerFunctions
{
NSConcretePointerFunctions *pf = [NSConcretePointerFunctions new];
pf->_x = _pf;
return [pf autorelease];
}
- (void) removePointerAtIndex: (NSUInteger)index
{
if (index >= _count)
{
[self _raiseRangeExceptionWithIndex: index from: _cmd];
}
pointerFunctionsRelinquish(&_pf, &_contents[index]);
while (++index < _count)
{
pointerFunctionsMove(&_pf, &_contents[index-1], &_contents[index]);
}
[self setCount: _count - 1];
}
- (void) replacePointerAtIndex: (NSUInteger)index withPointer: (void*)item
{
if (index >= _count)
{
[self _raiseRangeExceptionWithIndex: index from: _cmd];
}
pointerFunctionsReplace(&_pf, &_contents[index], item);
}
- (void) setCount: (NSUInteger)count
{
if (count > _count)
{
_count = count;
if (_count >= _capacity)
{
void **ptr;
size_t size;
size_t new_cap = _capacity;
size_t new_gf = _grow_factor ? _grow_factor : 2;
while (new_cap + new_gf < _count)
{
new_cap += new_gf;
new_gf = new_cap/2;
}
size = (new_cap + new_gf)*sizeof(void*);
new_cap += new_gf;
new_gf = new_cap / 2;
if (_contents == 0)
{
#if GS_WITH_GC
ptr = (void**)NSZoneMalloc([self zone], size);
#else
if (_pf.options & NSPointerFunctionsZeroingWeakMemory)
{
ptr = (void**)NSAllocateCollectable(size, 0);
}
else
{
ptr = (void**)NSAllocateCollectable(size, NSScannedOption);
}
#endif
}
else
{
#if GS_WITH_GC
ptr = (void**)NSZoneRealloc([self zone], _contents, size);
#else
if (_pf.options & NSPointerFunctionsZeroingWeakMemory)
{
ptr = (void**)NSReallocateCollectable(
_contents, size, 0);
}
else
{
ptr = (void**)NSReallocateCollectable(
_contents, size, NSScannedOption);
}
#endif
}
if (ptr == 0)
{
[NSException raise: NSMallocException
format: @"Unable to grow array"];
}
_contents = ptr;
_capacity = new_cap;
_grow_factor = new_gf;
}
}
else
{
while (count < _count)
{
_count--;
pointerFunctionsRelinquish(&_pf, &_contents[_count]);
}
}
}
@end