Author: thebeing
Date: Wed Jul 27 01:01:11 2016
New Revision: 40035
URL: http://svn.gna.org/viewcvs/gnustep?rev=40035&view=rev
Log:
Add [NSData initWithBytesNoCopy:length:deallocator:]
This new initializer allows customising the deallocation behaviour
through user-supplied blocks.
Modified:
libs/base/trunk/ChangeLog
libs/base/trunk/Headers/Foundation/NSData.h
libs/base/trunk/Source/NSData.m
libs/base/trunk/Tests/base/NSData/general.m
Modified: libs/base/trunk/ChangeLog
URL:
http://svn.gna.org/viewcvs/gnustep/libs/base/trunk/ChangeLog?rev=40035&r1=40034&r2=40035&view=diff
==============================================================================
--- libs/base/trunk/ChangeLog (original)
+++ libs/base/trunk/ChangeLog Wed Jul 27 01:01:11 2016
@@ -1,3 +1,12 @@
+2016-07-26 Niels Grewe <[email protected]>
+
+ * Headers/Foundation/NSData.h
+ * Source/NSData.m
+ * Tests/base/NSData/general.m:
+ Implement OS X 10.9 method -initWithBytesNoCopy:length:deallocator:
+ that allows customising data deallocation based on a caller
+ supplied block. Also adds test cases for this functionality.
+
2016-07-26 Richard Frith-Macdonald <[email protected]>
* Source/GSStream.m:
Modified: libs/base/trunk/Headers/Foundation/NSData.h
URL:
http://svn.gna.org/viewcvs/gnustep/libs/base/trunk/Headers/Foundation/NSData.h?rev=40035&r1=40034&r2=40035&view=diff
==============================================================================
--- libs/base/trunk/Headers/Foundation/NSData.h (original)
+++ libs/base/trunk/Headers/Foundation/NSData.h Wed Jul 27 01:01:11 2016
@@ -3,24 +3,24 @@
Written by: Andrew Kachites McCallum <[email protected]>
Date: 1995
-
+
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.
- */
+ */
#ifndef __NSData_h_GNUSTEP_BASE_INCLUDE
#define __NSData_h_GNUSTEP_BASE_INCLUDE
@@ -29,6 +29,7 @@
#import <Foundation/NSObject.h>
#import <Foundation/NSRange.h>
#import <Foundation/NSSerialization.h>
+#import <GNUstepBase/GSBlocks.h>
#if defined(__cplusplus)
extern "C" {
@@ -39,7 +40,7 @@
@class NSURL;
#endif
-#if OS_API_VERSION(MAC_OS_X_VERSION_10_9,GS_API_LATEST)
+#if OS_API_VERSION(MAC_OS_X_VERSION_10_9,GS_API_LATEST)
enum {
NSDataBase64DecodingIgnoreUnknownCharacters = (1UL << 0)
};
@@ -54,7 +55,7 @@
typedef NSUInteger NSDataBase64EncodingOptions;
#endif
-#if OS_API_VERSION(MAC_OS_X_VERSION_10_4,GS_API_LATEST)
+#if OS_API_VERSION(MAC_OS_X_VERSION_10_4,GS_API_LATEST)
enum {
NSMappedRead = 1,
NSUncachedRead = 2
@@ -66,6 +67,10 @@
/* The original name for this was NSAtomicWrite ... need for backward comapat
*/
#define NSAtomicWrite NSDataWritingAtomic
+#endif
+
+#if OS_API_VERSION(MAC_OS_X_VERSION_10_9,GS_API_LATEST)
+DEFINE_BLOCK_TYPE(GSDataDeallocatorBlock, void, void*, NSUInteger);
#endif
@interface NSData : NSObject <NSCoding, NSCopying, NSMutableCopying>
@@ -88,11 +93,20 @@
+ (id) dataWithContentsOfURL: (NSURL*)url;
#endif
+ (id) dataWithData: (NSData*)data;
-#if OS_API_VERSION(MAC_OS_X_VERSION_10_9,GS_API_LATEST)
+#if OS_API_VERSION(MAC_OS_X_VERSION_10_9,GS_API_LATEST)
- (id) initWithBase64EncodedData: (NSData*)base64Data
options: (NSDataBase64DecodingOptions)options;
- (id) initWithBase64EncodedString: (NSString*)base64String
options: (NSDataBase64DecodingOptions)options;
+/**
+ * <override-subclass/>
+ * 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
+ * supplied deallocator block.
+ */
+- (instancetype) initWithBytesNoCopy: (void*)bytes
+ length: (NSUInteger)length
+ deallocator: (GSDataDeallocatorBlock)deallocator;
#endif
- (id) initWithBytes: (const void*)aBuffer
length: (NSUInteger)bufferSize;
@@ -110,7 +124,7 @@
#endif
- (id) initWithData: (NSData*)data;
-// Accessing Data
+// Accessing Data
- (const void*) bytes;
- (NSString*) description;
@@ -122,11 +136,11 @@
- (NSData*) subdataWithRange: (NSRange)aRange;
// base64
-#if OS_API_VERSION(MAC_OS_X_VERSION_10_9,GS_API_LATEST)
+#if OS_API_VERSION(MAC_OS_X_VERSION_10_9,GS_API_LATEST)
- (NSData *) base64EncodedDataWithOptions:
(NSDataBase64EncodingOptions)options;
- (NSString *) base64EncodedStringWithOptions:
(NSDataBase64EncodingOptions)options;
#endif
-
+
// Querying a Data Object
- (BOOL) isEqualToData: (NSData*)other;
@@ -171,7 +185,7 @@
count: (unsigned int)numInts
atIndex: (unsigned int)index;
-#if OS_API_VERSION(MAC_OS_X_VERSION_10_4,GS_API_LATEST)
+#if OS_API_VERSION(MAC_OS_X_VERSION_10_4,GS_API_LATEST)
/**
* <p>Writes a copy of the data encapsulated by the receiver to a file
* at path. If the NSDataWritingAtomic option is set, this writes to a
Modified: libs/base/trunk/Source/NSData.m
URL:
http://svn.gna.org/viewcvs/gnustep/libs/base/trunk/Source/NSData.m?rev=40035&r1=40034&r2=40035&view=diff
==============================================================================
--- libs/base/trunk/Source/NSData.m (original)
+++ libs/base/trunk/Source/NSData.m Wed Jul 27 01:01:11 2016
@@ -52,10 +52,12 @@
* NSDataMappedFile Memory mapped files.
* NSDataShared Extension for shared memory.
* NSDataFinalized For GC of non-GC data.
+ * NSDataWithDeallocatorBlock Adds custom deallocation behaviour
* NSMutableData Abstract base class.
* NSMutableDataMalloc Concrete class.
* NSMutableDataShared Extension for shared memory.
* NSDataMutableFinalized For GC of non-GC data.
+ * NSMutableDataWithDeallocatorBlock Adds custom deallocation
behaviour
*
* NSMutableDataMalloc MUST share it's initial instance variable layout
* with NSDataMalloc so that it can use the 'behavior' code to inherit
@@ -126,6 +128,8 @@
static Class dataStatic;
static Class dataMalloc;
static Class mutableDataMalloc;
+static Class dataBlock;
+static Class mutableDataBlock;
static Class NSDataAbstract;
static Class NSMutableDataAbstract;
static SEL appendSel;
@@ -390,6 +394,13 @@
@interface NSDataMalloc : NSDataStatic
@end
+@interface NSDataWithDeallocatorBlock : NSDataMalloc
+{
+ @private
+ GSDataDeallocatorBlock _deallocator;
+}
+@end
+
@interface NSMutableDataMalloc : NSMutableData
{
NSUInteger length;
@@ -400,6 +411,13 @@
}
/* Increase capacity to at least the specified minimum value. */
- (void) _grow: (NSUInteger)minimum;
+@end
+
+@interface NSMutableDataWithDeallocatorBlock : NSMutableDataMalloc
+{
+ @private
+ GSDataDeallocatorBlock _deallocator;
+}
@end
#ifdef HAVE_MMAP
@@ -445,7 +463,9 @@
NSMutableDataAbstract = [NSMutableData class];
dataStatic = [NSDataStatic class];
dataMalloc = [NSDataMalloc class];
+ dataBlock = [NSDataWithDeallocatorBlock class];
mutableDataMalloc = [NSMutableDataMalloc class];
+ mutableDataBlock = [NSMutableDataWithDeallocatorBlock class];
appendSel = @selector(appendBytes:length:);
appendImp = [mutableDataMalloc instanceMethodForSelector: appendSel];
}
@@ -824,6 +844,14 @@
return nil;
}
+- (instancetype) initWithBytesNoCopy: (void*)bytes
+ length: (NSUInteger)length
+ deallocator: (GSDataDeallocatorBlock)deallocator
+{
+ [self subclassResponsibility: _cmd];
+ return nil;
+}
+
/**
* Initialises the receiver with the contents of the specified file.<br />
* Returns the resulting object.<br />
@@ -3300,6 +3328,37 @@
return self;
}
+- (instancetype) initWithBytesNoCopy: (void*)buf
+ length: (NSUInteger)len
+ deallocator: (GSDataDeallocatorBlock)deallocator
+{
+ if (buf == NULL && len > 0)
+ {
+ [self release];
+ [NSException raise: NSInvalidArgumentException
+ format: @"[%@-initWithBytesNoCopy:length:deallocator:] called with "
+ @"length but NULL bytes", NSStringFromClass([self class])];
+ }
+ else if (NULL == deallocator)
+ {
+ // For a nil deallocator we can just swizzle into a static data object
+ GSClassSwizzle(self, dataStatic);
+ bytes = buf;
+ length = len;
+ return self;
+ }
+
+ /* This is a bit unfortunate: Our implementation has no space to hold the
+ * deallocator block ivar in NSDataMalloc, so if we are invoked via this
+ * initialiser, we have to undo the previous allocation and reallocate
+ * ourselves as NSDataWithDeallocatorBlock.
+ */
+ [self release];
+ return [[dataBlock alloc] initWithBytesNoCopy: buf
+ length: len
+ deallocator: deallocator];
+}
+
- (NSUInteger) sizeInBytesExcluding: (NSHashTable*)exclude
{
NSUInteger size = GSPrivateMemorySize(self, exclude);
@@ -3311,6 +3370,38 @@
return size;
}
+@end
+
+@implementation NSDataWithDeallocatorBlock
+- (instancetype) initWithBytesNoCopy: (void*)buf
+ length: (NSUInteger)len
+ deallocator: (GSDataDeallocatorBlock)deallocator
+{
+ if (buf == NULL && len > 0)
+ {
+ [self release];
+ [NSException raise: NSInvalidArgumentException
+ format: @"[%@-initWithBytesNoCopy:length:deallocator:] called with "
+ @"length but NULL bytes", NSStringFromClass([self class])];
+ }
+
+ bytes = buf;
+ length = len;
+ ASSIGN(_deallocator, deallocator);
+ return self;
+}
+
+- (void) dealloc
+{
+ if (_deallocator != NULL)
+ {
+ CALL_BLOCK(_deallocator, bytes, length);
+ }
+ // Clear out the ivars so that super doesn't double free.
+ bytes = NULL;
+ length = 0;
+ [super dealloc];
+}
@end
@@ -3609,6 +3700,35 @@
return self;
}
+- (instancetype) initWithBytesNoCopy: (void*)buf
+ length: (NSUInteger)len
+ deallocator: (GSDataDeallocatorBlock)deallocator
+{
+ if (buf == NULL && len > 0)
+ {
+ [self release];
+ [NSException raise: NSInvalidArgumentException
+ format: @"[%@-initWithBytesNoCopy:length:deallocator:] called with "
+ @"length but NULL bytes", NSStringFromClass([self class])];
+ }
+ else if (NULL == deallocator)
+ {
+ // Can reuse this class.
+ return [self initWithBytesNoCopy: buf
+ length: len
+ freeWhenDone: NO];
+ }
+
+ /*
+ * Custom deallocator. Need to re-allocate as an instance of
+ * NSMutableDataWithDeallocatorBlock
+ */
+ [self release];
+ return [[mutableDataBlock alloc] initWithBytesNoCopy: buf
+ length: len
+ deallocator: deallocator];
+}
+
// THIS IS THE DESIGNATED INITIALISER
/**
* Initialize with buffer capable of holding size bytes.
@@ -4108,6 +4228,105 @@
@end
+@implementation NSMutableDataWithDeallocatorBlock
+
++ (id) allocWithZone: (NSZone*)z
+{
+ return NSAllocateObject(mutableDataBlock, 0, z);
+}
+
+- (instancetype) initWithBytesNoCopy: (void*)buf
+ length: (NSUInteger)len
+ deallocator: (GSDataDeallocatorBlock)deallocator
+{
+ if (buf == NULL && len > 0)
+ {
+ [self release];
+ [NSException raise: NSInvalidArgumentException
+ format: @"[%@-initWithBytesNoCopy:length:deallocator:] called with "
+ @"length but NULL bytes", NSStringFromClass([self class])];
+ }
+
+ /* The assumption here is that the superclass if fully concrete and will
+ * not return a different instance. This invariant holds for the current
+ * implementation of NSMutableDataMalloc, but not NSDataMalloc.
+ */
+ if (nil == (self = [super initWithBytesNoCopy: buf
+ length: len
+ freeWhenDone: NO]))
+ {
+ return nil;
+ }
+ ASSIGN(_deallocator, deallocator);
+ return self;
+}
+
+- (void) dealloc
+{
+ if (_deallocator != NULL)
+ {
+ CALL_BLOCK(_deallocator, bytes, capacity);
+ // Clear out the ivars so that super doesn't double free.
+ bytes = NULL;
+ length = 0;
+ DESTROY(_deallocator);
+ }
+
+ [super dealloc];
+}
+
+- (id) setCapacity: (NSUInteger)size
+{
+ /* We need to override capacity modification so that we correctly call the
+ * block when we are operating on the initial allocation, usual malloc/free
+ * machinery otherwise. */
+ if (size != capacity)
+ {
+ void *tmp;
+
+ tmp = NSZoneMalloc(zone, size);
+ if (tmp == 0)
+ {
+ [NSException raise: NSMallocException
+ format: @"Unable to set data capacity to '%"PRIuPTR"'", size];
+ }
+ if (bytes)
+ {
+ memcpy(tmp, bytes, capacity < size ? capacity : size);
+ if (_deallocator != NULL)
+ {
+ CALL_BLOCK(_deallocator, bytes, capacity);
+ DESTROY(_deallocator);
+ zone = NSDefaultMallocZone();
+ }
+ else
+ {
+ NSZoneFree(zone, bytes);
+ }
+ }
+ else if (_deallocator != NULL)
+ {
+ CALL_BLOCK(_deallocator, bytes, capacity);
+ DESTROY(_deallocator);
+ zone = NSDefaultMallocZone();
+ }
+ bytes = tmp;
+ capacity = size;
+ growth = capacity/2;
+ if (growth == 0)
+ {
+ growth = 1;
+ }
+ }
+ if (size < length)
+ {
+ length = size;
+ }
+ return self;
+}
+
+@end
+
#ifdef HAVE_SHMCTL
@implementation NSMutableDataShared
Modified: libs/base/trunk/Tests/base/NSData/general.m
URL:
http://svn.gna.org/viewcvs/gnustep/libs/base/trunk/Tests/base/NSData/general.m?rev=40035&r1=40034&r2=40035&view=diff
==============================================================================
--- libs/base/trunk/Tests/base/NSData/general.m (original)
+++ libs/base/trunk/Tests/base/NSData/general.m Wed Jul 27 01:01:11 2016
@@ -5,20 +5,20 @@
#import <Foundation/NSString.h>
int main()
-{
+{
NSAutoreleasePool *arp = [NSAutoreleasePool new];
char *str1,*str2;
NSData *data1, *data2;
NSMutableData *mutable;
char *hold;
-
+
str1 = "Test string for data classes";
- str2 = (char *) malloc(sizeof("Test string for data classes not copied"));
+ str2 = (char *) malloc(sizeof("Test string for data classes not copied"));
strcpy(str2,"Test string for data classes not copied");
-
+
mutable = [NSMutableData dataWithLength:100];
- hold = [mutable mutableBytes];
-
+ hold = [mutable mutableBytes];
+
/* hmpf is this correct */
data1 = [NSData dataWithBytes:str1 length:(strlen(str1) * sizeof(void*))];
PASS(data1 != nil &&
@@ -27,52 +27,106 @@
[data1 bytes] != str1 &&
strcmp(str1,[data1 bytes]) == 0,
"+dataWithBytes:length: works");
-
+
data2 = [NSData dataWithBytesNoCopy:str2 length:(strlen(str2) *
sizeof(void*))];
PASS(data2 != nil && [data2 isKindOfClass:[NSData class]] &&
[data2 length] == (strlen(str2) * sizeof(void*)) &&
[data2 bytes] == str2,
"+dataWithBytesNoCopy:length: works");
-
+
data1 = [NSData dataWithBytes:nil length:0];
- PASS(data1 != nil && [data1 isKindOfClass:[NSData class]] &&
- [data1 length] == 0,
+ PASS(data1 != nil && [data1 isKindOfClass:[NSData class]] &&
+ [data1 length] == 0,
"+dataWithBytes:length works with 0 length");
-
- [data2 getBytes:hold range:NSMakeRange(2,6)];
+
+ [data2 getBytes:hold range:NSMakeRange(2,6)];
PASS(strcmp(hold,"st str") == 0, "-getBytes:range works");
-
- PASS_EXCEPTION([data2 getBytes:hold
+
+ PASS_EXCEPTION([data2 getBytes:hold
range:NSMakeRange(strlen(str2)*sizeof(void*),1)];,
NSRangeException,
"getBytes:range: with bad location");
-
- PASS_EXCEPTION([data2 getBytes:hold
+
+ PASS_EXCEPTION([data2 getBytes:hold
range:NSMakeRange(1,(strlen(str2)*sizeof(void*)))];,
NSRangeException,
"getBytes:range: with bad length");
-
+
PASS_EXCEPTION([data2
subdataWithRange:NSMakeRange((strlen(str2)*sizeof(void*)),1)];,
NSRangeException,
"-subdataWithRange: with bad location");
-
+
PASS_EXCEPTION([data2
subdataWithRange:NSMakeRange(1,(strlen(str2)*sizeof(void*)))];,
NSRangeException,
"-subdataWithRange: with bad length");
-
- data2 = [NSData dataWithBytesNoCopy:str1
+
+ data2 = [NSData dataWithBytesNoCopy:str1
length:(strlen(str1) * sizeof(void*))
freeWhenDone:NO];
PASS(data2 != nil && [data2 isKindOfClass:[NSData class]] &&
[data2 length] == (strlen(str1) * sizeof(void*)) &&
- [data2 bytes] == str1,
+ [data2 bytes] == str1,
"+dataWithBytesNoCopy:length:freeWhenDone: works");
-
+
[arp release]; arp = nil;
-
- {
+
+ {
BOOL didNotSegfault = YES;
PASS(didNotSegfault, "+dataWithBytesNoCopy:length:freeWhenDone:NO doesn't
free memory");
}
+
+
+ START_SET("deallocator blocks")
+ # ifndef __has_feature
+ # define __has_feature(x) 0
+ # endif
+ # if __has_feature(blocks)
+ uint8_t stackBuf[4] = { 1, 2, 3, 5 };
+ __block NSUInteger called = 0;
+ NSData *immutable =
+ [[NSData alloc] initWithBytesNoCopy: stackBuf
+ length: 4
+ deallocator: ^(void* bytes, NSUInteger length) {
+ called++;
+ }];
+ PASS_RUNS([immutable release]; immutable = nil;,
+ "No free() error with custom deallocator");
+ PASS(called == 1, "Deallocator block called");
+ uint8_t *buf = malloc(4 * sizeof(uint8_t));
+ NSMutableData *mutable =
+ [[NSMutableData alloc] initWithBytesNoCopy: buf
+ length: 4
+ deallocator: ^(void *bytes, NSUInteger len)
+ {
+ free(bytes);
+ called++;
+ }
+ ];
+ PASS_RUNS([mutable release]; mutable = nil;,
+ "No free() error with custom deallocator on mutable data");
+ PASS(called == 2, "Deallocator block called on -dealloc of mutable data");
+ buf = malloc(4 * sizeof(uint8_t));
+ mutable =
+ [[NSMutableData alloc] initWithBytesNoCopy: buf
+ length: 4
+ deallocator: ^(void *bytes, NSUInteger len)
+ {
+ free(bytes);
+ called++;
+ }
+ ];
+ PASS_RUNS([mutable setCapacity: 10];,
+ "Can set capactiy with custom deallocator on mutable data");
+ PASS(called == 3,
+ "Deallocator block called on -setCapacity: of mutable data");
+ PASS_RUNS([mutable release]; mutable = nil;,
+ "No free() error with custom deallocator on mutable data "
+ "after capacity change");
+ PASS(called == 3, "Deallocator block not called on -dealloc of mutable data
"
+ "after its capacity has been changed");
+ # else
+ SKIP("No Blocks support in the compiler.")
+ # endif
+ END_SET("deallocator blocks")
return 0;
}
_______________________________________________
Gnustep-cvs mailing list
[email protected]
https://mail.gna.org/listinfo/gnustep-cvs