Avoid reallocating objects post-hoc based on the initializer used.

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@40038 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Niels Grewe 2016-07-27 07:02:42 +00:00
parent 1b82abd967
commit 2e18a14485
3 changed files with 47 additions and 45 deletions

View file

@ -1,3 +1,12 @@
2016-07-27 Niels Grewe <niels.grewe@halbordnung.de>
* Header/Foundation/NSData.h
* Source/NSData.m:
Make the deallocator block a direct ivar of NSDataMalloc
(or NSMutableDataMalloc respectively). Breaks binary
compatibility but avoids reallocating objects based on
the initializer used.
2016-07-26 Niels Grewe <niels.grewe@halbordnung.de> 2016-07-26 Niels Grewe <niels.grewe@halbordnung.de>
* Source/NSDictionary.m * Source/NSDictionary.m

View file

@ -102,11 +102,12 @@ DEFINE_BLOCK_TYPE(GSDataDeallocatorBlock, void, void*, NSUInteger);
* <override-subclass/> * <override-subclass/>
* Initialize the receiver to hold memory pointed to by bytes without copying. * Initialize the receiver to hold memory pointed to by bytes without copying.
* When the receiver is deallocated, the memory will be freed using the user * When the receiver is deallocated, the memory will be freed using the user
* supplied deallocator block. * supplied deallocBlock. Note that passing a block that (either directly or
* indirectly) holds a strong reference the receiver will cause a retain cycle.
*/ */
- (instancetype) initWithBytesNoCopy: (void*)bytes - (instancetype) initWithBytesNoCopy: (void*)bytes
length: (NSUInteger)length length: (NSUInteger)length
deallocator: (GSDataDeallocatorBlock)deallocator; deallocator: (GSDataDeallocatorBlock)deallocBlock;
#endif #endif
- (id) initWithBytes: (const void*)aBuffer - (id) initWithBytes: (const void*)aBuffer
length: (NSUInteger)bufferSize; length: (NSUInteger)bufferSize;

View file

@ -385,6 +385,7 @@ failure:
{ {
NSUInteger length; NSUInteger length;
__strong void *bytes; __strong void *bytes;
GSDataDeallocatorBlock deallocator;
} }
@end @end
@ -395,16 +396,13 @@ failure:
@end @end
@interface NSDataWithDeallocatorBlock : NSDataMalloc @interface NSDataWithDeallocatorBlock : NSDataMalloc
{
@private
GSDataDeallocatorBlock _deallocator;
}
@end @end
@interface NSMutableDataMalloc : NSMutableData @interface NSMutableDataMalloc : NSMutableData
{ {
NSUInteger length; NSUInteger length;
__strong void *bytes; __strong void *bytes;
GSDataDeallocatorBlock deallocator;
NSZone *zone; NSZone *zone;
NSUInteger capacity; NSUInteger capacity;
NSUInteger growth; NSUInteger growth;
@ -414,10 +412,6 @@ failure:
@end @end
@interface NSMutableDataWithDeallocatorBlock : NSMutableDataMalloc @interface NSMutableDataWithDeallocatorBlock : NSMutableDataMalloc
{
@private
GSDataDeallocatorBlock _deallocator;
}
@end @end
#ifdef HAVE_MMAP #ifdef HAVE_MMAP
@ -3330,7 +3324,7 @@ getBytes(void* dst, void* src, unsigned len, unsigned limit, unsigned *pos)
- (instancetype) initWithBytesNoCopy: (void*)buf - (instancetype) initWithBytesNoCopy: (void*)buf
length: (NSUInteger)len length: (NSUInteger)len
deallocator: (GSDataDeallocatorBlock)deallocator deallocator: (GSDataDeallocatorBlock)deallocBlock
{ {
if (buf == NULL && len > 0) if (buf == NULL && len > 0)
{ {
@ -3339,7 +3333,7 @@ getBytes(void* dst, void* src, unsigned len, unsigned limit, unsigned *pos)
format: @"[%@-initWithBytesNoCopy:length:deallocator:] called with " format: @"[%@-initWithBytesNoCopy:length:deallocator:] called with "
@"length but NULL bytes", NSStringFromClass([self class])]; @"length but NULL bytes", NSStringFromClass([self class])];
} }
else if (NULL == deallocator) else if (NULL == deallocBlock)
{ {
// For a nil deallocator we can just swizzle into a static data object // For a nil deallocator we can just swizzle into a static data object
GSClassSwizzle(self, dataStatic); GSClassSwizzle(self, dataStatic);
@ -3348,15 +3342,9 @@ getBytes(void* dst, void* src, unsigned len, unsigned limit, unsigned *pos)
return self; return self;
} }
/* This is a bit unfortunate: Our implementation has no space to hold the GSClassSwizzle(self, dataBlock);
* deallocator block ivar in NSDataMalloc, so if we are invoked via this ASSIGN(deallocator, deallocBlock);
* initialiser, we have to undo the previous allocation and reallocate return self;
* ourselves as NSDataWithDeallocatorBlock.
*/
[self release];
return [[dataBlock alloc] initWithBytesNoCopy: buf
length: len
deallocator: deallocator];
} }
- (NSUInteger) sizeInBytesExcluding: (NSHashTable*)exclude - (NSUInteger) sizeInBytesExcluding: (NSHashTable*)exclude
@ -3375,7 +3363,7 @@ getBytes(void* dst, void* src, unsigned len, unsigned limit, unsigned *pos)
@implementation NSDataWithDeallocatorBlock @implementation NSDataWithDeallocatorBlock
- (instancetype) initWithBytesNoCopy: (void*)buf - (instancetype) initWithBytesNoCopy: (void*)buf
length: (NSUInteger)len length: (NSUInteger)len
deallocator: (GSDataDeallocatorBlock)deallocator deallocator: (GSDataDeallocatorBlock)deallocBlock
{ {
if (buf == NULL && len > 0) if (buf == NULL && len > 0)
{ {
@ -3387,16 +3375,16 @@ getBytes(void* dst, void* src, unsigned len, unsigned limit, unsigned *pos)
bytes = buf; bytes = buf;
length = len; length = len;
ASSIGN(_deallocator, deallocator); ASSIGN(deallocator, deallocBlock);
return self; return self;
} }
- (void) dealloc - (void) dealloc
{ {
if (_deallocator != NULL) if (deallocator != NULL)
{ {
CALL_BLOCK(_deallocator, bytes, length); CALL_BLOCK(deallocator, bytes, length);
DESTROY(_deallocator); DESTROY(deallocator);
} }
// Clear out the ivars so that super doesn't double free. // Clear out the ivars so that super doesn't double free.
bytes = NULL; bytes = NULL;
@ -3703,7 +3691,7 @@ getBytes(void* dst, void* src, unsigned len, unsigned limit, unsigned *pos)
- (instancetype) initWithBytesNoCopy: (void*)buf - (instancetype) initWithBytesNoCopy: (void*)buf
length: (NSUInteger)len length: (NSUInteger)len
deallocator: (GSDataDeallocatorBlock)deallocator deallocator: (GSDataDeallocatorBlock)deallocBlock;
{ {
if (buf == NULL && len > 0) if (buf == NULL && len > 0)
{ {
@ -3712,7 +3700,7 @@ getBytes(void* dst, void* src, unsigned len, unsigned limit, unsigned *pos)
format: @"[%@-initWithBytesNoCopy:length:deallocator:] called with " format: @"[%@-initWithBytesNoCopy:length:deallocator:] called with "
@"length but NULL bytes", NSStringFromClass([self class])]; @"length but NULL bytes", NSStringFromClass([self class])];
} }
else if (NULL == deallocator) else if (NULL == deallocBlock)
{ {
// Can reuse this class. // Can reuse this class.
return [self initWithBytesNoCopy: buf return [self initWithBytesNoCopy: buf
@ -3721,13 +3709,17 @@ getBytes(void* dst, void* src, unsigned len, unsigned limit, unsigned *pos)
} }
/* /*
* Custom deallocator. Need to re-allocate as an instance of * Custom deallocator. swizzle to NSMutableDataWithDeallocatorBlock
* NSMutableDataWithDeallocatorBlock
*/ */
[self release]; GSClassSwizzle(self, mutableDataBlock);
return [[mutableDataBlock alloc] initWithBytesNoCopy: buf if (nil == (self = [self initWithBytesNoCopy: buf
length: len length: len
deallocator: deallocator]; freeWhenDone: NO]))
{
return nil;
}
ASSIGN(deallocator, deallocBlock);
return self;
} }
// THIS IS THE DESIGNATED INITIALISER // THIS IS THE DESIGNATED INITIALISER
@ -4238,7 +4230,7 @@ getBytes(void* dst, void* src, unsigned len, unsigned limit, unsigned *pos)
- (instancetype) initWithBytesNoCopy: (void*)buf - (instancetype) initWithBytesNoCopy: (void*)buf
length: (NSUInteger)len length: (NSUInteger)len
deallocator: (GSDataDeallocatorBlock)deallocator deallocator: (GSDataDeallocatorBlock)deallocBlock
{ {
if (buf == NULL && len > 0) if (buf == NULL && len > 0)
{ {
@ -4258,19 +4250,19 @@ getBytes(void* dst, void* src, unsigned len, unsigned limit, unsigned *pos)
{ {
return nil; return nil;
} }
ASSIGN(_deallocator, deallocator); ASSIGN(deallocator, deallocBlock);
return self; return self;
} }
- (void) dealloc - (void) dealloc
{ {
if (_deallocator != NULL) if (deallocator != NULL)
{ {
CALL_BLOCK(_deallocator, bytes, capacity); CALL_BLOCK(deallocator, bytes, capacity);
// Clear out the ivars so that super doesn't double free. // Clear out the ivars so that super doesn't double free.
bytes = NULL; bytes = NULL;
length = 0; length = 0;
DESTROY(_deallocator); DESTROY(deallocator);
} }
[super dealloc]; [super dealloc];
@ -4294,10 +4286,10 @@ getBytes(void* dst, void* src, unsigned len, unsigned limit, unsigned *pos)
if (bytes) if (bytes)
{ {
memcpy(tmp, bytes, capacity < size ? capacity : size); memcpy(tmp, bytes, capacity < size ? capacity : size);
if (_deallocator != NULL) if (deallocator != NULL)
{ {
CALL_BLOCK(_deallocator, bytes, capacity); CALL_BLOCK(deallocator, bytes, capacity);
DESTROY(_deallocator); DESTROY(deallocator);
zone = NSDefaultMallocZone(); zone = NSDefaultMallocZone();
} }
else else
@ -4305,10 +4297,10 @@ getBytes(void* dst, void* src, unsigned len, unsigned limit, unsigned *pos)
NSZoneFree(zone, bytes); NSZoneFree(zone, bytes);
} }
} }
else if (_deallocator != NULL) else if (deallocator != NULL)
{ {
CALL_BLOCK(_deallocator, bytes, capacity); CALL_BLOCK(deallocator, bytes, capacity);
DESTROY(_deallocator); DESTROY(deallocator);
zone = NSDefaultMallocZone(); zone = NSDefaultMallocZone();
} }
bytes = tmp; bytes = tmp;