/** Copyright (C) 1995, 1996, 1997, 2000, 2002 Free Software Foundation, Inc. Written by: Andrew Kachites McCallum Date: March 1995 Rewritten by: Richard Frith-Macdonald Date: September 1997 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. NSData class reference $Date$ $Revision$ */ /* NOTES - Richard Frith-Macdonald 1997 * * Rewritten to use the class cluster architecture as in OPENSTEP. * * NB. In our implementaion we require an extra primitive for the * NSMutableData subclasses. This new primitive method is the * [-setCapacity:] method, and it differs from [-setLength:] * as follows - * * [-setLength:] * clears bytes when the allocated buffer grows * never shrinks the allocated buffer capacity * [-setCapacity:] * doesn't clear newly allocated bytes * sets the size of the allocated buffer. * * The actual class hierarchy is as follows - * * NSData Abstract base class. * NSDataStatic Concrete class static buffers. * NSDataEmpty Concrete class static buffers. * NSDataMalloc Concrete class. * NSDataMappedFile Memory mapped files. * NSDataShared Extension for shared memory. * NSDataFinalized For GC of non-GC data. * NSMutableData Abstract base class. * NSMutableDataMalloc Concrete class. * NSMutableDataShared Extension for shared memory. * NSDataMutableFinalized For GC of non-GC data. * * NSMutableDataMalloc MUST share it's initial instance variable layout * with NSDataMalloc so that it can use the 'behavior' code to inherit * methods from NSDataMalloc. * * Since all the other subclasses are based on NSDataMalloc or * NSMutableDataMalloc, we can put most methods in here and not * bother with duplicating them in the other classes. * */ #include "config.h" #include "GNUstepBase/preface.h" #include "GNUstepBase/GSObjCRuntime.h" #include "Foundation/NSObjCRuntime.h" #include "Foundation/NSByteOrder.h" #include "Foundation/NSCoder.h" #include "Foundation/NSData.h" #include "Foundation/NSString.h" #include "Foundation/NSException.h" #include "Foundation/NSDebug.h" #include "Foundation/NSFileManager.h" #include "Foundation/NSPathUtilities.h" #include "Foundation/NSRange.h" #include "Foundation/NSURL.h" #include "Foundation/NSValue.h" #include "Foundation/NSZone.h" #include "GSPrivate.h" #include #include /* for memset() */ #ifdef HAVE_UNISTD_H #include /* SEEK_* on SunOS 4 */ #endif #ifdef HAVE_MMAP #include #include #include #ifndef MAP_FAILED #define MAP_FAILED ((void*)-1) /* Failure address. */ #endif @class NSDataMappedFile; #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SHMCTL #include #include #define VM_RDONLY 0644 /* self read/write - other readonly */ #define VM_ACCESS 0666 /* read/write access for all */ @class NSDataShared; @class NSMutableDataShared; #endif @class NSDataMalloc; @class NSDataStatic; @class NSMutableDataMalloc; #if GS_WITH_GC @class NSDataFinalized; @class NSMutableDataFinalized; #endif /* * Some static variables to cache classes and methods for quick access - * these are set up at process startup or in [NSData +initialize] */ static Class dataStatic; static Class dataMalloc; static Class mutableDataMalloc; static Class NSDataAbstract; static Class NSMutableDataAbstract; #if GS_WITH_GC static Class dataFinalized; static Class mutableDataFinalized; #endif static SEL appendSel; static IMP appendImp; static BOOL readContentsOfFile(NSString* path, void** buf, unsigned int* len, NSZone* zone) { #if defined(__MINGW32__) const unichar *thePath = 0; #else const char *thePath = 0; #endif FILE *theFile = 0; void *tmp = 0; int c; long fileLength; #if defined(__MINGW32__) thePath = (const unichar*)[path fileSystemRepresentation]; #else thePath = [path fileSystemRepresentation]; #endif if (thePath == 0) { NSWarnFLog(@"Open (%@) attempt failed - bad path", path); return NO; } #if defined(__MINGW32__) theFile = _wfopen(thePath, L"rb"); #else theFile = fopen(thePath, "rb"); #endif if (theFile == 0) /* We failed to open the file. */ { NSDebugFLog(@"Open (%@) attempt failed - %@", path, [NSError _last]); goto failure; } /* * Seek to the end of the file. */ c = fseek(theFile, 0L, SEEK_END); if (c != 0) { NSWarnFLog(@"Seek to end of file (%@) failed - %@", path, [NSError _last]); goto failure; } /* * Determine the length of the file (having seeked to the end of the * file) by calling ftell(). */ fileLength = ftell(theFile); if (fileLength == -1) { NSWarnFLog(@"Ftell on %@ failed - %@", path, [NSError _last]); goto failure; } /* * Rewind the file pointer to the beginning, preparing to read in * the file. */ c = fseek(theFile, 0L, SEEK_SET); if (c != 0) { NSWarnFLog(@"Fseek to start of file (%@) failed - %@", path, [NSError _last]); goto failure; } if (fileLength == 0) { unsigned char buf[BUFSIZ]; /* * Special case ... a file of length zero may be a named pipe or some * file in the /proc filesystem, which will return us data if we read * from it ... so we try reading as much as we can. */ while ((c = fread(buf, 1, BUFSIZ, theFile)) != 0) { #if GS_WITH_GC if (tmp == 0) { tmp = NSAllocateCollectable(c, 0); } else { tmp = NSReallocateCollectable(tmp, fileLength + c, 0); } #else if (tmp == 0) { tmp = NSZoneMalloc(zone, c); } else { tmp = NSZoneRealloc(zone, tmp, fileLength + c); } #endif if (tmp == 0) { NSLog(@"Malloc failed for file (%@) of length %d - %@", path, fileLength + c, [NSError _last]); goto failure; } memcpy(tmp + fileLength, buf, c); fileLength += c; } } else { #if GS_WITH_GC tmp = NSAllocateCollectable(fileLength, 0); #else tmp = NSZoneMalloc(zone, fileLength); #endif if (tmp == 0) { NSLog(@"Malloc failed for file (%@) of length %d - %@", path, fileLength, [NSError _last]); goto failure; } c = fread(tmp, 1, fileLength, theFile); if (c != (int)fileLength) { NSWarnFLog(@"read of file (%@) contents failed - %@", path, [NSError _last]); goto failure; } } *buf = tmp; *len = fileLength; fclose(theFile); return YES; /* * Just in case the failure action needs to be changed. */ failure: #if !GS_WITH_GC if (tmp != 0) { NSZoneFree(zone, tmp); } #endif if (theFile != 0) { fclose(theFile); } return NO; } /* * NB, The start of the NSMutableDataMalloc instance variables must be * identical to that of NSDataMalloc in order to inherit its methods. */ @interface NSDataStatic : NSData { NSUInteger length; void *bytes; } @end @interface NSDataEmpty: NSDataStatic @end @interface NSDataMalloc : NSDataStatic @end @interface NSMutableDataMalloc : NSMutableData { NSUInteger length; void *bytes; #if GS_WITH_GC BOOL owned; #else NSZone *zone; #endif NSUInteger capacity; NSUInteger growth; } /* Increase capacity to at least the specified minimum value. */ - (void) _grow: (NSUInteger)minimum; @end #if GS_WITH_GC @interface NSDataFinalized : NSDataMalloc @end @interface NSMutableDataFinalized : NSMutableDataMalloc @end #endif #ifdef HAVE_MMAP @interface NSDataMappedFile : NSDataMalloc @end #endif #ifdef HAVE_SHMCTL @interface NSDataShared : NSDataMalloc { int shmid; } - (id) initWithShmID: (int)anId length: (NSUInteger)bufferSize; @end @interface NSMutableDataShared : NSMutableDataMalloc { int shmid; } - (id) initWithShmID: (int)anId length: (NSUInteger)bufferSize; @end #endif /** *

Class for storing a byte array. Methods for initializing from memory a * file, or the network are provided, as well as the ability to write to a * file or the network. If desired, object can take over management of a * pre-allocated buffer (with malloc or similar), free'ing it when deallocated. *

*

The data buffer at any given time has a capacity, which is the * size of its allocated memory area, in bytes, and a length, which * is the length of data it is currently storing.

*/ @implementation NSData + (void) initialize { if (self == [NSData class]) { NSDataAbstract = self; NSMutableDataAbstract = [NSMutableData class]; dataStatic = [NSDataStatic class]; dataMalloc = [NSDataMalloc class]; mutableDataMalloc = [NSMutableDataMalloc class]; #if GS_WITH_GC dataFinalized = [NSDataFinalized class]; mutableDataFinalized = [NSMutableDataFinalized class]; #endif appendSel = @selector(appendBytes:length:); appendImp = [mutableDataMalloc instanceMethodForSelector: appendSel]; } } + (id) allocWithZone: (NSZone*)z { if (self == NSDataAbstract) { return NSAllocateObject(dataMalloc, 0, z); } else { return NSAllocateObject(self, 0, z); } } /** * Returns an empty data object. */ + (id) data { static NSData *empty = nil; if (empty == nil) { empty = [dataStatic allocWithZone: NSDefaultMallocZone()]; empty = [empty initWithBytesNoCopy: 0 length: 0 freeWhenDone: NO]; } return empty; } /** * Returns an autoreleased data object containing data copied from bytes * and with the specified length. Invokes -initWithBytes:length: */ + (id) dataWithBytes: (const void*)bytes length: (NSUInteger)length { NSData *d; d = [dataMalloc allocWithZone: NSDefaultMallocZone()]; d = [d initWithBytes: bytes length: length]; return AUTORELEASE(d); } /** * Returns an autoreleased data object encapsulating the data at bytes * and with the specified length. Invokes * -initWithBytesNoCopy:length:freeWhenDone: with YES */ + (id) dataWithBytesNoCopy: (void*)bytes length: (NSUInteger)length { NSData *d; d = [dataMalloc allocWithZone: NSDefaultMallocZone()]; d = [d initWithBytesNoCopy: bytes length: length freeWhenDone: YES]; return AUTORELEASE(d); } /** * Returns an autoreleased data object encapsulating the data at bytes * and with the specified length. Invokes * -initWithBytesNoCopy:length:freeWhenDone: */ + (id) dataWithBytesNoCopy: (void*)aBuffer length: (NSUInteger)bufferSize freeWhenDone: (BOOL)shouldFree { NSData *d; if (shouldFree == YES) { d = [dataMalloc allocWithZone: NSDefaultMallocZone()]; } else { d = [dataStatic allocWithZone: NSDefaultMallocZone()]; } d = [d initWithBytesNoCopy: aBuffer length: bufferSize freeWhenDone: shouldFree]; return AUTORELEASE(d); } /** * Returns a data object encapsulating the contents of the specified file. * Invokes -initWithContentsOfFile: */ + (id) dataWithContentsOfFile: (NSString*)path { NSData *d; d = [dataMalloc allocWithZone: NSDefaultMallocZone()]; d = [d initWithContentsOfFile: path]; return AUTORELEASE(d); } /** * Returns a data object encapsulating the contents of the specified * file mapped directly into memory. * Invokes -initWithContentsOfMappedFile: */ + (id) dataWithContentsOfMappedFile: (NSString*)path { NSData *d; #ifdef HAVE_MMAP d = [NSDataMappedFile allocWithZone: NSDefaultMallocZone()]; d = [d initWithContentsOfMappedFile: path]; #else d = [dataMalloc allocWithZone: NSDefaultMallocZone()]; d = [d initWithContentsOfMappedFile: path]; #endif return AUTORELEASE(d); } /** * Retrieves the information at the specified url and returns an NSData * instance encapsulating it. */ + (id) dataWithContentsOfURL: (NSURL*)url { NSData *d; d = [url resourceDataUsingCache: YES]; return d; } /** * Returns an autoreleased instance initialised by copying the contents of data. */ + (id) dataWithData: (NSData*)data { NSData *d; d = [dataMalloc allocWithZone: NSDefaultMallocZone()]; d = [d initWithBytes: [data bytes] length: [data length]]; return AUTORELEASE(d); } /** * Returns a new empty data object. */ + (id) new { NSData *d; d = [dataMalloc allocWithZone: NSDefaultMallocZone()]; d = [d initWithBytesNoCopy: 0 length: 0 freeWhenDone: YES]; return d; } - (id) init { return [self initWithBytesNoCopy: 0 length: 0 freeWhenDone: YES]; } /** * Makes a copy of bufferSize bytes of data at aBuffer, and passes it to * -initWithBytesNoCopy:length:freeWhenDone: with a YES argument in order * to initialise the receiver. Returns the result. */ - (id) initWithBytes: (const void*)aBuffer length: (NSUInteger)bufferSize { void *ptr = 0; if (bufferSize > 0) { #if GS_WITH_GC ptr = NSAllocateCollectable(bufferSize, 0); #else ptr = NSZoneMalloc(NSDefaultMallocZone(), bufferSize); #endif if (ptr == 0) { DESTROY(self); return nil; } memcpy(ptr, aBuffer, bufferSize); } return [self initWithBytesNoCopy: ptr length: bufferSize freeWhenDone: YES]; } /** * Invokes -initWithBytesNoCopy:length:freeWhenDone: with the last argument * set to YES. Returns the resulting initialised data object (which may not * be the receiver). */ - (id) initWithBytesNoCopy: (void*)aBuffer length: (NSUInteger)bufferSize { return [self initWithBytesNoCopy: aBuffer length: bufferSize freeWhenDone: YES]; } /** * Initialises the receiver.
* The value of aBuffer is a pointer to something to be stored.
* The value of bufferSize is the number of bytes to use.
* The value of shouldFree specifies whether the receiver should * attempt to free the memory pointer to by aBuffer when the receiver * is deallocated ... ie. it says whether the receiver owns * the memory. Supplying the wrong value here will lead to memory * leaks or crashes. */ - (id) initWithBytesNoCopy: (void*)aBuffer length: (NSUInteger)bufferSize freeWhenDone: (BOOL)shouldFree { [self subclassResponsibility: _cmd]; return nil; } /** * Initialises the receiver with the contents of the specified file.
* Returns the resulting object.
* Returns nil if the file does not exist or can not be read for some reason. */ - (id) initWithContentsOfFile: (NSString*)path { void *fileBytes; unsigned fileLength; #if GS_WITH_GC if (readContentsOfFile(path, &fileBytes, &fileLength, 0) == NO) { return nil; } #else if (readContentsOfFile(path, &fileBytes, &fileLength, GSObjCZone(self)) == NO) { DESTROY(self); return nil; } #endif self = [self initWithBytesNoCopy: fileBytes length: fileLength freeWhenDone: YES]; return self; } /** * Initialize with data pointing to contents of file at path. Bytes are * only "swapped in" as needed. File should not be moved or deleted for * the life of this object. */ - (id) initWithContentsOfMappedFile: (NSString *)path { #ifdef HAVE_MMAP NSZone *z = GSObjCZone(self); RELEASE(self); self = [NSDataMappedFile allocWithZone: z]; return [self initWithContentsOfMappedFile: path]; #else return [self initWithContentsOfFile: path]; #endif } /** * Initialize with data pointing to contents of URL, which will be * retrieved immediately in a blocking manner. */ - (id) initWithContentsOfURL: (NSURL*)url { NSData *data = [url resourceDataUsingCache: YES]; return [self initWithData: data]; } /** * Initializes by copying data's bytes into a new buffer. */ - (id) initWithData: (NSData*)data { if (data == nil) { return [self initWithBytesNoCopy: 0 length: 0 freeWhenDone: YES]; } if ([data isKindOfClass: [NSData class]] == NO) { NSLog(@"-initWithData: passed a non-data object"); RELEASE(self); return nil; } return [self initWithBytes: [data bytes] length: [data length]]; } // Accessing Data /** * Returns a pointer to the data encapsulated by the receiver. */ - (const void*) bytes { [self subclassResponsibility: _cmd]; return 0; } /** * Returns a short description of this object. */ - (NSString*) description { extern void GSPropertyListMake(id,NSDictionary*,BOOL,BOOL,unsigned,id*); NSMutableString *result = nil; GSPropertyListMake(self, nil, NO, YES, 0, &result); return result; } /** * Copies the contents of the memory encapsulated by the receiver into * the specified buffer. The buffer must be large enough to contain * -length bytes of data ... if it isn't then a crash is likely to occur.
* Invokes -getBytes:range: with the range set to the whole of the receiver. */ - (void) getBytes: (void*)buffer { [self getBytes: buffer range: NSMakeRange(0, [self length])]; } /** * Copies length bytes of data from the memory encapsulated by the receiver * into the specified buffer. The buffer must be large enough to contain * length bytes of data ... if it isn't then a crash is likely to occur.
* If length is greater than the size of the receiver, only the available * bytes are copied. */ - (void) getBytes: (void*)buffer length: (NSUInteger)length { NSUInteger l = [self length]; [self getBytes: buffer range: NSMakeRange(0, l < length ? l : length)]; } /** * Copies data from the memory encapsulated by the receiver (in the range * specified by aRange) into the specified buffer.
* The buffer must be large enough to contain the data ... if it isn't then * a crash is likely to occur.
* If aRange specifies a range which does not entirely lie within the * receiver, an exception is raised. */ - (void) getBytes: (void*)buffer range: (NSRange)aRange { unsigned size = [self length]; GS_RANGE_CHECK(aRange, size); memcpy(buffer, [self bytes] + aRange.location, aRange.length); } - (id) replacementObjectForPortCoder: (NSPortCoder*)aCoder { return self; } /** * Returns an NSData instance encapsulating the memory from the receiver * specified by the range aRange.
* If aRange specifies a range which does not entirely lie within the * receiver, an exception is raised. */ - (NSData*) subdataWithRange: (NSRange)aRange { void *buffer; unsigned l = [self length]; GS_RANGE_CHECK(aRange, l); #if GS_WITH_GC buffer = NSAllocateCollectable(aRange.length, 0); #else buffer = NSZoneMalloc(NSDefaultMallocZone(), aRange.length); #endif if (buffer == 0) { [NSException raise: NSMallocException format: @"No memory for subdata of NSData object"]; } [self getBytes: buffer range: aRange]; return [NSData dataWithBytesNoCopy: buffer length: aRange.length]; } - (NSUInteger) hash { unsigned char buf[64]; unsigned l = [self length]; unsigned ret =0; l = MIN(l,64); /* * hash for empty data matches hash for empty string */ if (l == 0) { return 0xfffffffe; } [self getBytes: &buf range: NSMakeRange(0, l)]; while (l-- > 0) { ret = (ret << 5) + ret + buf[l]; } // Again, match NSString if (ret == 0) { ret = 0xffffffff; } return ret; } - (BOOL) isEqual: anObject { if ([anObject isKindOfClass: [NSData class]]) return [self isEqualToData: anObject]; return NO; } /** * Returns a boolean value indicating if the receiver and other contain * identical data (using a byte by byte comparison). Assumes that the * other object is an NSData instance ... may raise an exception if it isn't. */ - (BOOL) isEqualToData: (NSData*)other { NSUInteger len; if (other == self) { return YES; } if ((len = [self length]) != [other length]) { return NO; } return (memcmp([self bytes], [other bytes], len) ? NO : YES); } /** * Returns the number of bytes of data encapsulated by the receiver. */ - (NSUInteger) length { /* This is left to concrete subclasses to implement. */ [self subclassResponsibility: _cmd]; return 0; } /** *

Writes a copy of the data encapsulated by the receiver to a file * at path. If the useAuxiliaryFile flag is YES, this writes to a * temporary file and then renames that to the file at path, thus * ensuring that path exists and does not contain partially written * data at any point. *

*

On success returns YES, on failure returns NO. *

*/ - (BOOL) writeToFile: (NSString*)path atomically: (BOOL)useAuxiliaryFile { #if defined(__MINGW32__) NSUInteger length = [path length]; unichar wthePath[length + 100]; unichar wtheRealPath[length + 100]; #else char thePath[BUFSIZ*2+8]; char theRealPath[BUFSIZ*2]; #endif int c; FILE *theFile; BOOL error_BadPath = YES; #if defined(__MINGW32__) [path getCharacters: wtheRealPath]; wtheRealPath[length] = L'\0'; error_BadPath = (length <= 0); #else if ([path canBeConvertedToEncoding: [NSString defaultCStringEncoding]]) { const char *local_c_path = [path cString]; if (local_c_path != 0 && strlen(local_c_path) < (BUFSIZ*2)) { strcpy(theRealPath,local_c_path); error_BadPath = NO; } } #endif if (error_BadPath) { NSWarnMLog(@"Open (%@) attempt failed - bad path",path); return NO; } #ifdef HAVE_MKSTEMP if (useAuxiliaryFile) { int desc; int mask; strcpy(thePath, theRealPath); strcat(thePath, "XXXXXX"); if ((desc = mkstemp(thePath)) < 0) { NSWarnMLog(@"mkstemp (%s) failed - %@", thePath, [NSError _last]); goto failure; } mask = umask(0); umask(mask); fchmod(desc, 0644 & ~mask); if ((theFile = fdopen(desc, "w")) == 0) { close(desc); } } else { strcpy(thePath, theRealPath); theFile = fopen(thePath, "wb"); } #else if (useAuxiliaryFile) { /* Use the path name of the destination file as a prefix for the * mktemp() call so that we can be sure that both files are on * the same filesystem and the subsequent rename() will work. */ #if defined(__MINGW32__) wcscpy(wthePath, wtheRealPath); wcscat(wthePath, L"XXXXXX"); if (_wmktemp(wthePath) == 0) { NSWarnMLog(@"mktemp (%@) failed - %@", [NSString stringWithCharacters: wthePath length: wcslen(wthePath)], [NSError _last]); goto failure; } #else strcpy(thePath, theRealPath); strcat(thePath, "XXXXXX"); if (mktemp(thePath) == 0) { NSWarnMLog(@"mktemp (%s) failed - %@", thePath, [NSError _last]); goto failure; } #endif } else { #if defined(__MINGW32__) wcscpy(wthePath,wtheRealPath); #else strcpy(thePath, theRealPath); #endif } /* Open the file (whether temp or real) for writing. */ #if defined(__MINGW32__) theFile = _wfopen(wthePath, L"wb"); #else theFile = fopen(thePath, "wb"); #endif #endif if (theFile == 0) { /* Something went wrong; we weren't * even able to open the file. */ #if defined(__MINGW32__) NSWarnMLog(@"Open (%@) failed - %@", [NSString stringWithCharacters: wthePath length: wcslen(wthePath)], [NSError _last]); #else NSWarnMLog(@"Open (%s) failed - %@", thePath, [NSError _last]); #endif goto failure; } /* Now we try and write the NSData's bytes to the file. Here `c' is * the number of bytes which were successfully written to the file * in the fwrite() call. */ c = fwrite([self bytes], sizeof(char), [self length], theFile); if (c < (int)[self length]) /* We failed to write everything for * some reason. */ { #if defined(__MINGW32__) NSWarnMLog(@"Fwrite (%@) failed - %@", [NSString stringWithCharacters: wthePath length: wcslen(wthePath)], [NSError _last]); #else NSWarnMLog(@"Fwrite (%s) failed - %@", thePath, [NSError _last]); #endif goto failure; } /* We're done, so close everything up. */ c = fclose(theFile); if (c != 0) /* I can't imagine what went wrong * closing the file, but we got here, * so we need to deal with it. */ { #if defined(__MINGW32__) NSWarnMLog(@"Fclose (%@) failed - %@", [NSString stringWithCharacters: wthePath length: wcslen(wthePath)], [NSError _last]); #else NSWarnMLog(@"Fclose (%s) failed - %@", thePath, [NSError _last]); #endif goto failure; } /* If we used a temporary file, we still need to rename() it be the * real file. Also, we need to try to retain the file attributes of * the original file we are overwriting (if we are) */ if (useAuxiliaryFile) { NSFileManager *mgr = [NSFileManager defaultManager]; NSMutableDictionary *att = nil; #if defined(__MINGW32__) NSUInteger perm; #endif if ([mgr fileExistsAtPath: path]) { att = [[mgr fileAttributesAtPath: path traverseLink: YES] mutableCopy]; IF_NO_GC(TEST_AUTORELEASE(att)); } #if defined(__MINGW32__) /* To replace the existing file on windows, it must be writable. */ perm = [att filePosixPermissions]; if (perm != NSNotFound && (perm & 0200) == 0) { [mgr changeFileAttributes: [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithUnsignedInt: 0777], NSFilePosixPermissions, nil] atPath: path]; } /* * The windoze implementation of the POSIX rename() function is buggy * and doesn't work if the destination file already exists ... so we * try to use a windoze specific function instead. */ #if 0 if (ReplaceFile(theRealPath, thePath, 0, REPLACEFILE_IGNORE_MERGE_ERRORS, 0, 0) != 0) { c = 0; } else { c = -1; } #else if (MoveFileExW(wthePath, wtheRealPath, MOVEFILE_REPLACE_EXISTING) != 0) { c = 0; } /* Windows 9x does not support MoveFileEx */ else if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) { unichar secondaryFile[length + 100]; wcscpy(secondaryFile, wthePath); wcscat(secondaryFile, L"-delete"); // Delete the intermediate name just in case DeleteFileW(secondaryFile); // Move the existing file to the temp name if (MoveFileW(wtheRealPath, secondaryFile) != 0) { if (MoveFileW(wthePath, wtheRealPath) != 0) { c = 0; // Delete the old file if possible DeleteFileW(secondaryFile); } else { c = -1; // failure, restore the old file if possible MoveFileW(secondaryFile, wtheRealPath); } } else { c = -1; // failure } } else { c = -1; } #endif #else c = rename(thePath, theRealPath); #endif if (c != 0) /* Many things could go wrong, I guess. */ { #if defined(__MINGW32__) NSWarnMLog(@"Rename ('%@' to '%@') failed - %@", [NSString stringWithCharacters: wthePath length: wcslen(wthePath)], [NSString stringWithCharacters: wtheRealPath length: wcslen(wtheRealPath)], [NSError _last]); #else NSWarnMLog(@"Rename ('%s' to '%s') failed - %@", thePath, theRealPath, [NSError _last]); #endif goto failure; } if (att != nil) { /* * We have created a new file - so we attempt to make it's * attributes match that of the original. */ [att removeObjectForKey: NSFileSize]; [att removeObjectForKey: NSFileModificationDate]; [att removeObjectForKey: NSFileReferenceCount]; [att removeObjectForKey: NSFileSystemNumber]; [att removeObjectForKey: NSFileSystemFileNumber]; [att removeObjectForKey: NSFileDeviceIdentifier]; [att removeObjectForKey: NSFileType]; if ([mgr changeFileAttributes: att atPath: path] == NO) { NSWarnMLog(@"Unable to correctly set all attributes for '%@'", path); } } #ifndef __MINGW32__ else if (geteuid() == 0 && [@"root" isEqualToString: NSUserName()] == NO) { att = [NSDictionary dictionaryWithObjectsAndKeys: NSFileOwnerAccountName, NSUserName(), nil]; if ([mgr changeFileAttributes: att atPath: path] == NO) { NSWarnMLog(@"Unable to correctly set ownership for '%@'", path); } } #endif } /* success: */ return YES; /* Just in case the failure action needs to be changed. */ failure: /* * Attempt to tidy up by removing temporary file on failure. */ if (useAuxiliaryFile) { #if defined(__MINGW32__) _wunlink(wthePath); #else unlink(thePath); #endif } return NO; } /** * Writes a copy of the contents of the receiver to the specified URL. */ - (BOOL) writeToURL: (NSURL*)anURL atomically: (BOOL)flag { if ([anURL isFileURL] == YES) { return [self writeToFile: [anURL path] atomically: flag]; } else { return [anURL setResourceData: self]; } } // Deserializing Data /** * Copies data from buffer starting from cursor. Deprecated. * Use [-getBytes:] and related methods instead. */ - (unsigned int) deserializeAlignedBytesLengthAtCursor: (unsigned int*)cursor { return (unsigned)[self deserializeIntAtCursor: cursor]; } /** * Copies data from buffer starting from cursor. Deprecated. * Use [-getBytes:] and related methods instead. */ - (void) deserializeBytes: (void*)buffer length: (unsigned int)bytes atCursor: (unsigned int*)cursor { NSRange range = { *cursor, bytes }; [self getBytes: buffer range: range]; *cursor += bytes; } - (void) deserializeDataAt: (void*)data ofObjCType: (const char*)type atCursor: (unsigned int*)cursor context: (id )callback { if (!type || !data) return; switch (*type) { case _C_ID: { [callback deserializeObjectAt: data ofObjCType: type fromData: self atCursor: cursor]; return; } case _C_CHARPTR: { int32_t length; [self deserializeBytes: &length length: sizeof(length) atCursor: cursor]; length = GSSwapBigI32ToHost(length); if (length == -1) { *(const char**)data = 0; return; } else { unsigned len = (length+1)*sizeof(char); #if GS_WITH_GC *(char**)data = (char*)NSAllocateCollectable(len, 0); #else *(char**)data = (char*)NSZoneMalloc(NSDefaultMallocZone(), len); #endif if (*(char**)data == 0) { [NSException raise: NSMallocException format: @"out of memory to deserialize bytes"]; } } [self deserializeBytes: *(char**)data length: length atCursor: cursor]; (*(char**)data)[length] = '\0'; return; } case _C_ARY_B: { unsigned offset = 0; unsigned size; unsigned count = atoi(++type); unsigned i; while (isdigit(*type)) { type++; } size = objc_sizeof_type(type); for (i = 0; i < count; i++) { [self deserializeDataAt: (char*)data + offset ofObjCType: type atCursor: cursor context: callback]; offset += size; } return; } case _C_STRUCT_B: { struct objc_struct_layout layout; objc_layout_structure (type, &layout); while (objc_layout_structure_next_member (&layout)) { unsigned offset; unsigned align; const char *ftype; objc_layout_structure_get_info (&layout, &offset, &align, &ftype); [self deserializeDataAt: ((char*)data) + offset ofObjCType: ftype atCursor: cursor context: callback]; } return; } case _C_PTR: { unsigned len = objc_sizeof_type(++type); #if GS_WITH_GC *(char**)data = (char*)NSAllocateCollectable(len, 0); #else *(char**)data = (char*)NSZoneMalloc(NSDefaultMallocZone(), len); #endif if (*(char**)data == 0) { [NSException raise: NSMallocException format: @"out of memory to deserialize bytes"]; } [self deserializeDataAt: *(char**)data ofObjCType: type atCursor: cursor context: callback]; return; } case _C_CHR: case _C_UCHR: { [self deserializeBytes: data length: sizeof(unsigned char) atCursor: cursor]; return; } case _C_SHT: case _C_USHT: { unsigned short ns; [self deserializeBytes: &ns length: sizeof(unsigned short) atCursor: cursor]; *(unsigned short*)data = NSSwapBigShortToHost(ns); return; } case _C_INT: case _C_UINT: { unsigned ni; [self deserializeBytes: &ni length: sizeof(unsigned) atCursor: cursor]; *(unsigned*)data = NSSwapBigIntToHost(ni); return; } case _C_LNG: case _C_ULNG: { unsigned long nl; [self deserializeBytes: &nl length: sizeof(unsigned long) atCursor: cursor]; *(unsigned long*)data = NSSwapBigLongToHost(nl); return; } case _C_LNG_LNG: case _C_ULNG_LNG: { unsigned long long nl; [self deserializeBytes: &nl length: sizeof(unsigned long long) atCursor: cursor]; *(unsigned long long*)data = NSSwapBigLongLongToHost(nl); return; } case _C_FLT: { NSSwappedFloat nf; [self deserializeBytes: &nf length: sizeof(NSSwappedFloat) atCursor: cursor]; *(float*)data = NSSwapBigFloatToHost(nf); return; } case _C_DBL: { NSSwappedDouble nd; [self deserializeBytes: &nd length: sizeof(NSSwappedDouble) atCursor: cursor]; *(double*)data = NSSwapBigDoubleToHost(nd); return; } case _C_CLASS: { uint16_t ni; [self deserializeBytes: &ni length: sizeof(ni) atCursor: cursor]; ni = GSSwapBigI16ToHost(ni); if (ni == 0) { *(Class*)data = 0; } else { char name[ni+1]; Class c; [self deserializeBytes: name length: ni atCursor: cursor]; name[ni] = '\0'; c = GSClassFromName(name); if (c == 0) { NSLog(@"[%s %s] can't find class - %s", GSNameFromClass([self class]), GSNameFromSelector(_cmd), name); } *(Class*)data = c; } return; } case _C_SEL: { uint16_t ln; uint16_t lt; [self deserializeBytes: &ln length: sizeof(ln) atCursor: cursor]; ln = GSSwapBigI16ToHost(ln); [self deserializeBytes: < length: sizeof(lt) atCursor: cursor]; lt = GSSwapBigI16ToHost(lt); if (ln == 0) { *(SEL*)data = 0; } else { char name[ln+1]; char types[lt+1]; SEL sel; [self deserializeBytes: name length: ln atCursor: cursor]; name[ln] = '\0'; [self deserializeBytes: types length: lt atCursor: cursor]; types[lt] = '\0'; if (lt) { sel = sel_get_typed_uid(name, types); } else { sel = sel_get_any_typed_uid(name); } if (sel == 0) { if (lt) { sel = sel_register_typed_name(name, types); } else { sel = sel_register_name(name); } if (sel == 0) { [NSException raise: NSInternalInconsistencyException format: @"can't make sel with name '%s' " @"and types '%s'", name, types]; } } *(SEL*)data = sel; } return; } default: [NSException raise: NSGenericException format: @"Unknown type to deserialize - '%s'", type]; } } /** * Retrieve an int from this data, which is assumed to be in network * (big-endian) byte order. Cursor refers to byte position. */ - (int) deserializeIntAtCursor: (unsigned int*)cursor { unsigned ni, result; [self deserializeBytes: &ni length: sizeof(unsigned) atCursor: cursor]; result = NSSwapBigIntToHost(ni); return result; } /** * Retrieve an int from this data, which is assumed to be in network * (big-endian) byte order. Index refers to byte position. */ - (int) deserializeIntAtIndex: (unsigned int)index { unsigned ni, result; [self deserializeBytes: &ni length: sizeof(unsigned) atCursor: &index]; result = NSSwapBigIntToHost(ni); return result; } /** * Retrieve ints from intBuffer, which is assumed to be in network (big-endian) * byte order. Count refers to number of ints, but index refers to byte * position. */ - (void) deserializeInts: (int*)intBuffer count: (unsigned int)numInts atCursor: (unsigned int*)cursor { unsigned i; [self deserializeBytes: &intBuffer length: numInts * sizeof(unsigned) atCursor: cursor]; for (i = 0; i < numInts; i++) intBuffer[i] = NSSwapBigIntToHost(intBuffer[i]); } /** * Retrieve ints from intBuffer, which is assumed to be in network (big-endian) * byte order. Count refers to number of ints, but index refers to byte * position. */ - (void) deserializeInts: (int*)intBuffer count: (unsigned int)numInts atIndex: (unsigned int)index { unsigned i; [self deserializeBytes: &intBuffer length: numInts * sizeof(int) atCursor: &index]; for (i = 0; i < numInts; i++) { intBuffer[i] = NSSwapBigIntToHost(intBuffer[i]); } } - (id) copyWithZone: (NSZone*)z { if (NSShouldRetainWithZone(self, z) && [self isKindOfClass: [NSMutableData class]] == NO) return RETAIN(self); else return [[dataMalloc allocWithZone: z] initWithBytes: [self bytes] length: [self length]]; } - (id) mutableCopyWithZone: (NSZone*)zone { return [[mutableDataMalloc allocWithZone: zone] initWithBytes: [self bytes] length: [self length]]; } - (void) encodeWithCoder: (NSCoder*)coder { if ([coder allowsKeyedCoding]) { [coder encodeObject: self]; } else { [coder encodeDataObject: self]; } } - (id) initWithCoder: (NSCoder*)coder { id obj = nil; if ([coder allowsKeyedCoding]) { // FIXME obj = [coder decodeObject]; } else { obj = [coder decodeDataObject]; } if (obj != self) { ASSIGN(self, obj); } return self; } - (BOOL) writeToFile: (NSString *)path options: (NSUInteger)writeOptionsMask error: (NSError **)errorPtr { return NO; } - (BOOL) writeToURL: (NSURL *)url options: (NSUInteger)writeOptionsMask error: (NSError **)errorPtr { return NO; } @end /** * Provides some shared-memory extensions to [NSData]. */ @implementation NSData (GNUstepExtensions) /** * New instance with given shared memory ID. */ + (id) dataWithShmID: (int)anID length: (NSUInteger)length { #ifdef HAVE_SHMCTL NSDataShared *d; d = [NSDataShared allocWithZone: NSDefaultMallocZone()]; d = [d initWithShmID: anID length: length]; return AUTORELEASE(d); #else NSLog(@"[NSData -dataWithSmdID:length:] no shared memory support"); return nil; #endif } /** * New instance with given bytes in shared memory. */ + (id) dataWithSharedBytes: (const void*)bytes length: (NSUInteger)length { NSData *d; #ifdef HAVE_SHMCTL d = [NSDataShared allocWithZone: NSDefaultMallocZone()]; d = [d initWithBytes: bytes length: length]; #else d = [dataMalloc allocWithZone: NSDefaultMallocZone()]; d = [d initWithBytes: bytes length: length]; #endif return AUTORELEASE(d); } + (id) dataWithStaticBytes: (const void*)bytes length: (NSUInteger)length { NSDataStatic *d; d = [dataStatic allocWithZone: NSDefaultMallocZone()]; d = [d initWithBytesNoCopy: (void*)bytes length: length freeWhenDone: NO]; return AUTORELEASE(d); } - (void) deserializeTypeTag: (unsigned char*)tag andCrossRef: (unsigned int*)ref atCursor: (unsigned int*)cursor { [self deserializeDataAt: (void*)tag ofObjCType: @encode(uint8_t) atCursor: cursor context: nil]; if (*tag & _GSC_MAYX) { switch (*tag & _GSC_SIZE) { case _GSC_X_0: { return; } case _GSC_X_1: { uint8_t x; [self deserializeDataAt: (void*)&x ofObjCType: @encode(uint8_t) atCursor: cursor context: nil]; *ref = (unsigned int)x; return; } case _GSC_X_2: { uint16_t x; [self deserializeDataAt: (void*)&x ofObjCType: @encode(uint16_t) atCursor: cursor context: nil]; *ref = (unsigned int)x; return; } default: { uint32_t x; [self deserializeDataAt: (void*)&x ofObjCType: @encode(uint32_t) atCursor: cursor context: nil]; *ref = (unsigned int)x; return; } } } } @end /** * Mutable version of [NSData]. Methods are provided for appending and * replacing bytes in the buffer, which will be grown as needed. */ @implementation NSMutableData + (id) allocWithZone: (NSZone*)z { if (self == NSMutableDataAbstract) { return NSAllocateObject(mutableDataMalloc, 0, z); } else { return NSAllocateObject(self, 0, z); } } + (id) data { NSMutableData *d; d = [mutableDataMalloc allocWithZone: NSDefaultMallocZone()]; d = [d initWithCapacity: 0]; return AUTORELEASE(d); } + (id) dataWithBytes: (const void*)bytes length: (NSUInteger)length { NSData *d; d = [mutableDataMalloc allocWithZone: NSDefaultMallocZone()]; d = [d initWithBytes: bytes length: length]; return AUTORELEASE(d); } + (id) dataWithBytesNoCopy: (void*)bytes length: (NSUInteger)length { NSData *d; d = [mutableDataMalloc allocWithZone: NSDefaultMallocZone()]; d = [d initWithBytesNoCopy: bytes length: length]; return AUTORELEASE(d); } /** * New instance with buffer of given numBytes with length of valid data set * to zero. Note that capacity will be automatically increased as necessary. */ + (id) dataWithCapacity: (NSUInteger)numBytes { NSMutableData *d; d = [mutableDataMalloc allocWithZone: NSDefaultMallocZone()]; d = [d initWithCapacity: numBytes]; return AUTORELEASE(d); } + (id) dataWithContentsOfFile: (NSString*)path { NSMutableData *d; d = [mutableDataMalloc allocWithZone: NSDefaultMallocZone()]; d = [d initWithContentsOfFile: path]; return AUTORELEASE(d); } + (id) dataWithContentsOfMappedFile: (NSString*)path { NSMutableData *d; d = [mutableDataMalloc allocWithZone: NSDefaultMallocZone()]; d = [d initWithContentsOfMappedFile: path]; return AUTORELEASE(d); } + (id) dataWithContentsOfURL: (NSURL*)url { NSMutableData *d; NSData *data; d = [mutableDataMalloc allocWithZone: NSDefaultMallocZone()]; data = [url resourceDataUsingCache: YES]; d = [d initWithBytes: [data bytes] length: [data length]]; return AUTORELEASE(d); } + (id) dataWithData: (NSData*)data { NSMutableData *d; d = [mutableDataMalloc allocWithZone: NSDefaultMallocZone()]; d = [d initWithBytes: [data bytes] length: [data length]]; return AUTORELEASE(d); } /** * New instance with buffer of capacity and valid data size equal to given * length in bytes. The buffer contents are set to zero. The length of * valid data is set to zero. Note that buffer will be automatically * increased as necessary. */ + (id) dataWithLength: (NSUInteger)length { NSMutableData *d; d = [mutableDataMalloc allocWithZone: NSDefaultMallocZone()]; d = [d initWithLength: length]; return AUTORELEASE(d); } + (id) new { NSMutableData *d; d = [mutableDataMalloc allocWithZone: NSDefaultMallocZone()]; d = [d initWithCapacity: 0]; return d; } - (const void*) bytes { return [self mutableBytes]; } - (void) encodeWithCoder: (NSCoder*)aCoder { unsigned length = [self length]; void *bytes = [self mutableBytes]; if ([aCoder allowsKeyedCoding]) { [aCoder encodeBytes: bytes length: length forKey:@"NS.data"]; } else { [aCoder encodeValueOfObjCType: @encode(unsigned int) at: &length]; if (length) { [aCoder encodeArrayOfObjCType: @encode(unsigned char) count: length at: bytes]; } } } /** * Initialize with buffer capable of holding size bytes. The length of valid * data is initially set to zero. * */ - (id) initWithCapacity: (NSUInteger)capacity { [self subclassResponsibility: _cmd]; return nil; } - (id) initWithCoder: (NSCoder*)aCoder { if ([aCoder allowsKeyedCoding]) { const uint8_t *data; NSUInteger l; data = [aCoder decodeBytesForKey: @"NS.data" returnedLength: &l]; self = [self initWithBytes: data length: l]; } else { unsigned l; [aCoder decodeValueOfObjCType: @encode(unsigned int) at: &l]; if (l) { void *b; #if GS_WITH_GC b = NSAllocateCollectable(l, 0); #else b = NSZoneMalloc(GSObjCZone(self), l); #endif if (b == 0) { NSLog(@"[NSDataMalloc -initWithCoder:] unable to get %u bytes", l); RELEASE(self); return nil; } [aCoder decodeArrayOfObjCType: @encode(unsigned char) count: l at: b]; self = [self initWithBytesNoCopy: b length: l]; } else { self = [self initWithBytesNoCopy: 0 length: 0]; } } return self; } /** * Initialize with buffer of capacity equal to length, and with the length * of valid data set to length. Data is set to zero. */ - (id) initWithLength: (NSUInteger)length { [self subclassResponsibility: _cmd]; return nil; } // Adjusting Capacity /** * Increases buffer length by given number of bytes, filling the new space * with zeros. */ - (void) increaseLengthBy: (NSUInteger)extraLength { [self setLength: [self length]+extraLength]; } /** *

* Sets the length of the NSMutableData object. * If the length is increased, the newly allocated data area * is filled with zero bytes. *

*

* This is a 'primitive' method ... you need to implement it * if you write a subclass of NSMutableData. *

*/ - (void) setLength: (NSUInteger)size { [self subclassResponsibility: _cmd]; } /** *

* Returns a pointer to the data storage of the receiver.
* Modifications to the memory pointed to by this pointer will * change the contents of the object. It is important that * your code should not try to modify the memory beyond the * number of bytes given by the -length method. *

*

* NB. if the object is released, or any method that changes its * size or content is called, then the pointer previously returned * by this method may cease to be valid. *

*

* This is a 'primitive' method ... you need to implement it * if you write a subclass of NSMutableData. *

*/ - (void*) mutableBytes { [self subclassResponsibility: _cmd]; return 0; } // Appending Data /** * Appends bufferSize bytes from aBuffer to data, increasing capacity if * necessary. */ - (void) appendBytes: (const void*)aBuffer length: (NSUInteger)bufferSize { unsigned oldLength = [self length]; void* buffer; [self setLength: oldLength + bufferSize]; buffer = [self mutableBytes]; memcpy(buffer + oldLength, aBuffer, bufferSize); } /** * Copies and appends data from other to data, increasing capacity if * necessary. */ - (void) appendData: (NSData*)other { [self appendBytes: [other bytes] length: [other length]]; } /** * Replaces the bytes of data in the specified range with a * copy of the new bytes supplied.
* If the location of the range specified lies beyond the end * of the data ([self length] < range.location) * then a range exception is raised.
* Otherwise, if the range specified extends beyond the end * of the data, then the size of the data is increased to * accommodate the new bytes.
*/ - (void) replaceBytesInRange: (NSRange)aRange withBytes: (const void*)bytes { unsigned size = [self length]; unsigned need = NSMaxRange(aRange); if (aRange.location > size) { [NSException raise: NSRangeException format: @"location bad in replaceByteInRange:withBytes:"]; } if (aRange.length > 0) { if (need > size) { [self setLength: need]; } memmove([self mutableBytes] + aRange.location, bytes, aRange.length); } } /** * Replace the content of the receiver which lies in aRange with * the specified length of data from the buffer pointed to by bytes.
* The size of the receiver is adjusted to allow for the change. */ - (void) replaceBytesInRange: (NSRange)aRange withBytes: (const void*)bytes length: (NSUInteger)length { unsigned size = [self length]; unsigned end = NSMaxRange(aRange); int shift = length - aRange.length; unsigned need = size + shift; void *buf; if (aRange.location > size) { [NSException raise: NSRangeException format: @"location bad in replaceByteInRange:withBytes:"]; } if (need > size) { [self setLength: need]; } buf = [self mutableBytes]; if (shift < 0) { if (length > 0) { // Copy bytes into place. memmove(buf + aRange.location, bytes, length); } // Fill gap memmove(buf + end + shift, buf + end, size - end); } else { if (shift > 0) { // Open space memmove(buf + end + shift, buf + end, size - end); } if (length > 0) { // Copy bytes into place. memmove(buf + aRange.location, bytes, length); } } if (need < size) { [self setLength: need]; } } /** * Set bytes in aRange to 0. */ - (void) resetBytesInRange: (NSRange)aRange { unsigned size = [self length]; GS_RANGE_CHECK(aRange, size); memset((char*)[self bytes] + aRange.location, 0, aRange.length); } /** * Replaces contents of buffer with contents of data's buffer, increasing * or shrinking capacity to match. */ - (void) setData: (NSData*)data { NSRange r = NSMakeRange(0, [data length]); [self setCapacity: r.length]; [self replaceBytesInRange: r withBytes: [data bytes]]; } // Serializing Data /** * Does not act as the name suggests. Instead, serializes length itself * as an int into buffer. */ - (void) serializeAlignedBytesLength: (unsigned int)length { [self serializeInt: length]; } - (void) serializeDataAt: (const void*)data ofObjCType: (const char*)type context: (id )callback { if (!data || !type) return; switch (*type) { case _C_ID: [callback serializeObjectAt: (id*)data ofObjCType: type intoData: self]; return; case _C_CHARPTR: { uint32_t len; uint32_t ni; if (!*(void**)data) { ni = (uint32_t)-1; ni = GSSwapHostI32ToBig(ni); [self appendBytes: (void*)&ni length: sizeof(ni)]; return; } len = (uint32_t)strlen(*(void**)data); ni = GSSwapHostI32ToBig(len); [self appendBytes: (void*)&ni length: sizeof(ni)]; [self appendBytes: *(void**)data length: len]; return; } case _C_ARY_B: { unsigned offset = 0; unsigned size; unsigned count = atoi(++type); unsigned i; while (isdigit(*type)) { type++; } size = objc_sizeof_type(type); for (i = 0; i < count; i++) { [self serializeDataAt: (char*)data + offset ofObjCType: type context: callback]; offset += size; } return; } case _C_STRUCT_B: { struct objc_struct_layout layout; objc_layout_structure (type, &layout); while (objc_layout_structure_next_member (&layout)) { unsigned offset; unsigned align; const char *ftype; objc_layout_structure_get_info (&layout, &offset, &align, &ftype); [self serializeDataAt: ((char*)data) + offset ofObjCType: ftype context: callback]; } return; } case _C_PTR: [self serializeDataAt: *(char**)data ofObjCType: ++type context: callback]; return; case _C_CHR: case _C_UCHR: [self appendBytes: data length: sizeof(unsigned char)]; return; case _C_SHT: case _C_USHT: { unsigned short ns = NSSwapHostShortToBig(*(unsigned short*)data); [self appendBytes: &ns length: sizeof(unsigned short)]; return; } case _C_INT: case _C_UINT: { unsigned ni = NSSwapHostIntToBig(*(unsigned int*)data); [self appendBytes: &ni length: sizeof(unsigned)]; return; } case _C_LNG: case _C_ULNG: { unsigned long nl = NSSwapHostLongToBig(*(unsigned long*)data); [self appendBytes: &nl length: sizeof(unsigned long)]; return; } case _C_LNG_LNG: case _C_ULNG_LNG: { unsigned long long nl; nl = NSSwapHostLongLongToBig(*(unsigned long long*)data); [self appendBytes: &nl length: sizeof(unsigned long long)]; return; } case _C_FLT: { NSSwappedFloat nf = NSSwapHostFloatToBig(*(float*)data); [self appendBytes: &nf length: sizeof(NSSwappedFloat)]; return; } case _C_DBL: { NSSwappedDouble nd = NSSwapHostDoubleToBig(*(double*)data); [self appendBytes: &nd length: sizeof(NSSwappedDouble)]; return; } case _C_CLASS: { const char *name = *(Class*)data?GSNameFromClass(*(Class*)data):""; uint16_t ln = (uint16_t)strlen(name); uint16_t ni; ni = GSSwapHostI16ToBig(ln); [self appendBytes: &ni length: sizeof(ni)]; if (ln) { [self appendBytes: name length: ln]; } return; } case _C_SEL: { const char *name = *(SEL*)data?GSNameFromSelector(*(SEL*)data):""; uint16_t ln = (name == 0) ? 0 : (uint16_t)strlen(name); const char *types = *(SEL*)data?GSTypesFromSelector(*(SEL*)data):""; uint16_t lt = (types == 0) ? 0 : (uint16_t)strlen(types); uint16_t ni; ni = GSSwapHostI16ToBig(ln); [self appendBytes: &ni length: sizeof(ni)]; ni = GSSwapHostI16ToBig(lt); [self appendBytes: &ni length: sizeof(ni)]; if (ln) { [self appendBytes: name length: ln]; } if (lt) { [self appendBytes: types length: lt]; } return; } default: [NSException raise: NSGenericException format: @"Unknown type to serialize - '%s'", type]; } } /** * Serialize an int into this object's data buffer, swapping it to network * (big-endian) byte order first. */ - (void) serializeInt: (int)value { unsigned ni = NSSwapHostIntToBig(value); [self appendBytes: &ni length: sizeof(unsigned)]; } /** * Serialize an int into this object's data buffer at index (replacing * anything there currently), swapping it to network (big-endian) byte order * first. */ - (void) serializeInt: (int)value atIndex: (unsigned int)index { unsigned ni = NSSwapHostIntToBig(value); NSRange range = { index, sizeof(int) }; [self replaceBytesInRange: range withBytes: &ni]; } /** * Serialize one or more ints into this object's data buffer, swapping them to * network (big-endian) byte order first. */ - (void) serializeInts: (int*)intBuffer count: (unsigned int)numInts { unsigned i; SEL sel = @selector(serializeInt:); IMP imp = [self methodForSelector: sel]; for (i = 0; i < numInts; i++) { (*imp)(self, sel, intBuffer[i]); } } /** * Serialize one or more ints into this object's data buffer at index * (replacing anything there currently), swapping them to network (big-endian) * byte order first. */ - (void) serializeInts: (int*)intBuffer count: (unsigned int)numInts atIndex: (unsigned int)index { unsigned i; SEL sel = @selector(serializeInt:atIndex:); IMP imp = [self methodForSelector: sel]; for (i = 0; i < numInts; i++) { (*imp)(self, sel, intBuffer[i], index++); } } @end /** * Provides some additional methods to [NSData]. */ @implementation NSMutableData (GNUstepExtensions) /** * New instance with given shared memory ID. */ + (id) dataWithShmID: (int)anID length: (NSUInteger)length { #ifdef HAVE_SHMCTL NSDataShared *d; d = [NSMutableDataShared allocWithZone: NSDefaultMallocZone()]; d = [d initWithShmID: anID length: length]; return AUTORELEASE(d); #else NSLog(@"[NSMutableData -dataWithSmdID:length:] no shared memory support"); return nil; #endif } /** * New instance with given bytes in shared memory. */ + (id) dataWithSharedBytes: (const void*)bytes length: (NSUInteger)length { NSData *d; #ifdef HAVE_SHMCTL d = [NSMutableDataShared allocWithZone: NSDefaultMallocZone()]; d = [d initWithBytes: bytes length: length]; #else d = [mutableDataMalloc allocWithZone: NSDefaultMallocZone()]; d = [d initWithBytes: bytes length: length]; #endif return AUTORELEASE(d); } /** * Returns current capacity of data buffer. */ - (NSUInteger) capacity { [self subclassResponsibility: _cmd]; return 0; } /** * Sets current capacity of data buffer. Unlike [-setLength:], this will * shrink the buffer if requested. */ - (id) setCapacity: (NSUInteger)newCapacity { [self subclassResponsibility: _cmd]; return nil; } /** * Return shared memory ID, if using one, else -1. */ - (int) shmID { return -1; } - (void) serializeTypeTag: (unsigned char)tag { [self serializeDataAt: (void*)&tag ofObjCType: @encode(unsigned char) context: nil]; } - (void) serializeTypeTag: (unsigned char)tag andCrossRef: (unsigned int)xref { if (xref <= 0xff) { uint8_t x = (uint8_t)xref; tag = (tag & ~_GSC_SIZE) | _GSC_X_1; [self serializeDataAt: (void*)&tag ofObjCType: @encode(unsigned char) context: nil]; [self serializeDataAt: (void*)&x ofObjCType: @encode(uint8_t) context: nil]; } else if (xref <= 0xffff) { uint16_t x = (uint16_t)xref; tag = (tag & ~_GSC_SIZE) | _GSC_X_2; [self serializeDataAt: (void*)&tag ofObjCType: @encode(unsigned char) context: nil]; [self serializeDataAt: (void*)&x ofObjCType: @encode(uint16_t) context: nil]; } else { uint32_t x = (uint32_t)xref; tag = (tag & ~_GSC_SIZE) | _GSC_X_4; [self serializeDataAt: (void*)&tag ofObjCType: @encode(unsigned char) context: nil]; [self serializeDataAt: (void*)&x ofObjCType: @encode(uint32_t) context: nil]; } } @end /* * This is the top of the hierarchy of concrete implementations. * As such, it contains efficient implementations of most methods. */ @implementation NSDataStatic + (id) allocWithZone: (NSZone*)z { return (NSData*)NSAllocateObject(self, 0, z); } /* Creation and Destruction of objects. */ - (id) copy { return RETAIN(self); } - (id) copyWithZone: (NSZone*)z { return RETAIN(self); } - (id) mutableCopy { return [[mutableDataMalloc allocWithZone: NSDefaultMallocZone()] initWithBytes: bytes length: length]; } - (id) mutableCopyWithZone: (NSZone*)z { return [[mutableDataMalloc allocWithZone: z] initWithBytes: bytes length: length]; } - (void) dealloc { bytes = 0; length = 0; [super dealloc]; } - (id) initWithBytesNoCopy: (void*)aBuffer length: (NSUInteger)bufferSize freeWhenDone: (BOOL)shouldFree { bytes = aBuffer; length = bufferSize; return self; } - (Class) classForCoder { return NSDataAbstract; } /* Basic methods */ - (const void*) bytes { return bytes; } - (void) getBytes: (void*)buffer range: (NSRange)aRange { GS_RANGE_CHECK(aRange, length); memcpy(buffer, bytes + aRange.location, aRange.length); } - (NSUInteger) length { return length; } static inline void getBytes(void* dst, void* src, unsigned len, unsigned limit, unsigned *pos) { if (*pos > limit || len > limit || len+*pos > limit) { [NSException raise: NSRangeException format: @"Range: (%u, %u) Size: %d", *pos, len, limit]; } memcpy(dst, src + *pos, len); *pos += len; } - (void) deserializeDataAt: (void*)data ofObjCType: (const char*)type atCursor: (unsigned int*)cursor context: (id )callback { if (data == 0 || type == 0) { if (data == 0) { NSLog(@"attempt to deserialize to a null pointer"); } if (type == 0) { NSLog(@"attempt to deserialize with a null type encoding"); } return; } switch (*type) { case _C_ID: { [callback deserializeObjectAt: data ofObjCType: type fromData: self atCursor: cursor]; return; } case _C_CHARPTR: { int32_t len; [self deserializeBytes: &len length: sizeof(len) atCursor: cursor]; len = GSSwapBigI32ToHost(len); if (len == -1) { *(const char**)data = 0; return; } else { #if GS_WITH_GC *(char**)data = (char*)NSAllocateCollectable(len+1, 0); #else *(char**)data = (char*)NSZoneMalloc(NSDefaultMallocZone(), len+1); #endif if (*(char**)data == 0) { [NSException raise: NSMallocException format: @"out of memory to deserialize bytes"]; } } getBytes(*(void**)data, bytes, len, length, cursor); (*(char**)data)[len] = '\0'; return; } case _C_ARY_B: { unsigned offset = 0; unsigned size; unsigned count = atoi(++type); unsigned i; while (isdigit(*type)) { type++; } size = objc_sizeof_type(type); for (i = 0; i < count; i++) { [self deserializeDataAt: (char*)data + offset ofObjCType: type atCursor: cursor context: callback]; offset += size; } return; } case _C_STRUCT_B: { struct objc_struct_layout layout; objc_layout_structure (type, &layout); while (objc_layout_structure_next_member (&layout)) { unsigned offset; unsigned align; const char *ftype; objc_layout_structure_get_info (&layout, &offset, &align, &ftype); [self deserializeDataAt: ((char*)data) + offset ofObjCType: ftype atCursor: cursor context: callback]; } return; } case _C_PTR: { unsigned len = objc_sizeof_type(++type); #if GS_WITH_GC *(char**)data = (char*)NSAllocateCollectable(len, 0); #else *(char**)data = (char*)NSZoneMalloc(NSDefaultMallocZone(), len); #endif if (*(char**)data == 0) { [NSException raise: NSMallocException format: @"out of memory to deserialize bytes"]; } [self deserializeDataAt: *(char**)data ofObjCType: type atCursor: cursor context: callback]; return; } case _C_CHR: case _C_UCHR: { getBytes(data, bytes, sizeof(unsigned char), length, cursor); return; } case _C_SHT: case _C_USHT: { unsigned short ns; getBytes((void*)&ns, bytes, sizeof(ns), length, cursor); *(unsigned short*)data = NSSwapBigShortToHost(ns); return; } case _C_INT: case _C_UINT: { unsigned ni; getBytes((void*)&ni, bytes, sizeof(ni), length, cursor); *(unsigned*)data = NSSwapBigIntToHost(ni); return; } case _C_LNG: case _C_ULNG: { unsigned long nl; getBytes((void*)&nl, bytes, sizeof(nl), length, cursor); *(unsigned long*)data = NSSwapBigLongToHost(nl); return; } case _C_LNG_LNG: case _C_ULNG_LNG: { unsigned long long nl; getBytes((void*)&nl, bytes, sizeof(nl), length, cursor); *(unsigned long long*)data = NSSwapBigLongLongToHost(nl); return; } case _C_FLT: { NSSwappedFloat nf; getBytes((void*)&nf, bytes, sizeof(nf), length, cursor); *(float*)data = NSSwapBigFloatToHost(nf); return; } case _C_DBL: { NSSwappedDouble nd; getBytes((void*)&nd, bytes, sizeof(nd), length, cursor); *(double*)data = NSSwapBigDoubleToHost(nd); return; } case _C_CLASS: { uint16_t ni; getBytes((void*)&ni, bytes, sizeof(ni), length, cursor); ni = GSSwapBigI16ToHost(ni); if (ni == 0) { *(Class*)data = 0; } else { char name[ni+1]; Class c; getBytes((void*)name, bytes, ni, length, cursor); name[ni] = '\0'; c = GSClassFromName(name); if (c == 0) { NSLog(@"[%s %s] can't find class - %s", GSNameFromClass([self class]), GSNameFromSelector(_cmd), name); } *(Class*)data = c; } return; } case _C_SEL: { uint16_t ln; uint16_t lt; getBytes((void*)&ln, bytes, sizeof(ln), length, cursor); ln = GSSwapBigI16ToHost(ln); getBytes((void*)<, bytes, sizeof(lt), length, cursor); lt = GSSwapBigI16ToHost(lt); if (ln == 0) { *(SEL*)data = 0; } else { char name[ln+1]; char types[lt+1]; SEL sel; getBytes((void*)name, bytes, ln, length, cursor); name[ln] = '\0'; getBytes((void*)types, bytes, lt, length, cursor); types[lt] = '\0'; if (lt) { sel = sel_get_typed_uid(name, types); } else { sel = sel_get_any_typed_uid(name); } if (sel == 0) { if (lt) { sel = sel_register_typed_name(name, types); } else { sel = sel_register_name(name); } if (sel == 0) { [NSException raise: NSInternalInconsistencyException format: @"can't make sel with name '%s' " @"and types '%s'", name, types]; } } *(SEL*)data = sel; } return; } default: [NSException raise: NSGenericException format: @"Unknown type to deserialize - '%s'", type]; } } - (void) deserializeTypeTag: (unsigned char*)tag andCrossRef: (unsigned int*)ref atCursor: (unsigned int*)cursor { if (*cursor >= length) { [NSException raise: NSRangeException format: @"Range: (%u, 1) Size: %d", *cursor, length]; } *tag = *((unsigned char*)bytes + (*cursor)++); if (*tag & _GSC_MAYX) { switch (*tag & _GSC_SIZE) { case _GSC_X_0: { return; } case _GSC_X_1: { if (*cursor >= length) { [NSException raise: NSRangeException format: @"Range: (%u, 1) Size: %d", *cursor, length]; } *ref = (unsigned int)*((unsigned char*)bytes + (*cursor)++); return; } case _GSC_X_2: { uint16_t x; if (*cursor >= length-1) { [NSException raise: NSRangeException format: @"Range: (%u, 1) Size: %d", *cursor, length]; } #if NEED_WORD_ALIGNMENT if ((*cursor % __alignof__(uint16_t)) != 0) memcpy(&x, (bytes + *cursor), 2); else #endif x = *(uint16_t*)(bytes + *cursor); *cursor += 2; *ref = (unsigned int)GSSwapBigI16ToHost(x); return; } default: { uint32_t x; if (*cursor >= length-3) { [NSException raise: NSRangeException format: @"Range: (%u, 1) Size: %d", *cursor, length]; } #if NEED_WORD_ALIGNMENT if ((*cursor % __alignof__(uint32_t)) != 0) memcpy(&x, (bytes + *cursor), 4); else #endif x = *(uint32_t*)(bytes + *cursor); *cursor += 4; *ref = (unsigned int)GSSwapBigI32ToHost(x); return; } } } } @end @implementation NSDataEmpty - (void) dealloc { GSNOSUPERDEALLOC; } @end @implementation NSDataMalloc - (id) copy { if (NSShouldRetainWithZone(self, NSDefaultMallocZone())) return RETAIN(self); else return [[dataMalloc allocWithZone: NSDefaultMallocZone()] initWithBytes: bytes length: length]; } - (id) copyWithZone: (NSZone*)z { if (NSShouldRetainWithZone(self, z)) return RETAIN(self); else return [[dataMalloc allocWithZone: z] initWithBytes: bytes length: length]; } - (void) dealloc { if (bytes != 0) { NSZoneFree(NSZoneFromPointer(bytes), bytes); bytes = 0; } [super dealloc]; } - (id) initWithBytesNoCopy: (void*)aBuffer length: (NSUInteger)bufferSize freeWhenDone: (BOOL)shouldFree { if (shouldFree == NO) { GSPrivateSwizzle(self, dataStatic); } #if GS_WITH_GC else if (aBuffer != 0 && GSPrivateIsCollectable(aBuffer) == NO) { GSPrivateSwizzle(self, dataFinalized); } #endif bytes = aBuffer; length = bufferSize; return self; } @end #if GS_WITH_GC @implementation NSDataFinalized - (void) finalize { NSZoneFree(NSDefaultMallocZone(), bytes); [super finalize]; } @end #endif #ifdef HAVE_MMAP @implementation NSDataMappedFile + (id) allocWithZone: (NSZone*)z { return (NSData*)NSAllocateObject([NSDataMappedFile class], 0, z); } - (void) dealloc { [self finalize]; [super dealloc]; } - (void) finalize { if (bytes != 0) { munmap(bytes, length); bytes = 0; } [super finalize]; } /** * Initialize with data pointing to contents of file at path. Bytes are * only "swapped in" as needed. File should not be moved or deleted for * the life of this object. */ - (id) initWithContentsOfMappedFile: (NSString*)path { int fd; #if defined(__MINGW32__) const unichar *thePath = (const unichar*)[path filesystemRepresentation]; #else const char *thePath = [path fileSystemRepresentation]; #endif if (thePath == 0) { NSWarnMLog(@"Open (%@) attempt failed - bad path", path); RELEASE(self); return nil; } #if defined(__MINGW32__) fd = _wopen(thePath, _O_RDONLY); #else fd = open(thePath, O_RDONLY); #endif if (fd < 0) { NSWarnMLog(@"unable to open %@ - %@", path, [NSError _last]); RELEASE(self); return nil; } /* Find size of file to be mapped. */ length = lseek(fd, 0, SEEK_END); if (length < 0) { NSWarnMLog(@"unable to seek to eof %@ - %@", path, [NSError _last]); close(fd); RELEASE(self); return nil; } /* Position at start of file. */ if (lseek(fd, 0, SEEK_SET) != 0) { NSWarnMLog(@"unable to seek to sof %@ - %@", path, [NSError _last]); close(fd); RELEASE(self); return nil; } bytes = mmap(0, length, PROT_READ, MAP_SHARED, fd, 0); if (bytes == MAP_FAILED) { NSWarnMLog(@"mapping failed for %s - %@", path, [NSError _last]); close(fd); RELEASE(self); self = [dataMalloc allocWithZone: NSDefaultMallocZone()]; self = [self initWithContentsOfFile: path]; } close(fd); return self; } @end #endif /* HAVE_MMAP */ #ifdef HAVE_SHMCTL @implementation NSDataShared + (id) allocWithZone: (NSZone*)z { return (NSData*)NSAllocateObject([NSDataShared class], 0, z); } - (void) dealloc { if (bytes != 0) { struct shmid_ds buf; if (shmctl(shmid, IPC_STAT, &buf) < 0) NSLog(@"[NSDataShared -dealloc] shared memory control failed - %@", [NSError _last]); else if (buf.shm_nattch == 1) if (shmctl(shmid, IPC_RMID, &buf) < 0) /* Mark for deletion. */ NSLog(@"[NSDataShared -dealloc] shared memory delete failed - %@", [NSError _last]); if (shmdt(bytes) < 0) NSLog(@"[NSDataShared -dealloc] shared memory detach failed - %@", [NSError _last]); bytes = 0; length = 0; shmid = -1; } [super dealloc]; } - (id) initWithBytes: (const void*)aBuffer length: (NSUInteger)bufferSize { shmid = -1; if (aBuffer && bufferSize) { shmid = shmget(IPC_PRIVATE, bufferSize, IPC_CREAT|VM_RDONLY); if (shmid == -1) /* Created memory? */ { NSLog(@"[-initWithBytes:length:] shared mem get failed for %u - %@", bufferSize, [NSError _last]); RELEASE(self); self = [dataMalloc allocWithZone: NSDefaultMallocZone()]; return [self initWithBytes: aBuffer length: bufferSize]; } bytes = shmat(shmid, 0, 0); if (bytes == (void*)-1) { NSLog(@"[-initWithBytes:length:] shared mem attach failed for %u - %@", bufferSize, [NSError _last]); bytes = 0; RELEASE(self); self = [dataMalloc allocWithZone: NSDefaultMallocZone()]; return [self initWithBytes: aBuffer length: bufferSize]; } length = bufferSize; } return self; } - (id) initWithShmID: (int)anId length: (NSUInteger)bufferSize { struct shmid_ds buf; shmid = anId; if (shmctl(shmid, IPC_STAT, &buf) < 0) { NSLog(@"[NSDataShared -initWithShmID:length:] shared memory " @"control failed - %@", [NSError _last]); RELEASE(self); /* Unable to access memory. */ return nil; } if (buf.shm_segsz < bufferSize) { NSLog(@"[NSDataShared -initWithShmID:length:] shared memory " @"segment too small"); RELEASE(self); /* Memory segment too small. */ return nil; } bytes = shmat(shmid, 0, 0); if (bytes == (void*)-1) { NSLog(@"[NSDataShared -initWithShmID:length:] shared memory " @"attach failed - %s", [NSError _last]); bytes = 0; RELEASE(self); /* Unable to attach to memory. */ return nil; } length = bufferSize; return self; } - (int) shmID { return shmid; } @end #endif /* HAVE_SHMCTL */ @implementation NSMutableDataMalloc + (void) initialize { if (self == [NSMutableDataMalloc class]) { GSObjCAddClassBehavior(self, [NSDataMalloc class]); } } + (id) allocWithZone: (NSZone*)z { return (NSData*)NSAllocateObject(mutableDataMalloc, 0, z); } - (Class) classForCoder { return NSMutableDataAbstract; } - (id) copy { return [[dataMalloc allocWithZone: NSDefaultMallocZone()] initWithBytes: bytes length: length]; } - (id) copyWithZone: (NSZone*)z { return [[dataMalloc allocWithZone: z] initWithBytes: bytes length: length]; } - (void) dealloc { #if !GS_WITH_GC if (bytes != 0) { if (zone != 0) { NSZoneFree(zone, bytes); } bytes = 0; } #endif [super dealloc]; } - (id) initWithBytes: (const void*)aBuffer length: (NSUInteger)bufferSize { self = [self initWithCapacity: bufferSize]; if (self) { if (aBuffer && bufferSize > 0) { memcpy(bytes, aBuffer, bufferSize); length = bufferSize; } } return self; } - (id) initWithBytesNoCopy: (void*)aBuffer length: (NSUInteger)bufferSize freeWhenDone: (BOOL)shouldFree { if (aBuffer == 0) { self = [self initWithCapacity: bufferSize]; if (self != nil) { [self setLength: bufferSize]; } return self; } #if GS_WITH_GC if (shouldFree == YES && GSPrivateIsCollectable(aBuffer) == NO) { GSPrivateSwizzle(self, mutableDataFinalized); } #endif self = [self initWithCapacity: 0]; if (self) { #if GS_WITH_GC owned = shouldFree; // Free memory on finalisation. #else if (shouldFree == NO) { zone = 0; // Don't free this memory. } #endif bytes = aBuffer; length = bufferSize; capacity = bufferSize; growth = capacity/2; if (growth == 0) { growth = 1; } } return self; } // THIS IS THE DESIGNATED INITIALISER /** * Initialize with buffer capable of holding size bytes. * */ - (id) initWithCapacity: (NSUInteger)size { if (size) { #if GS_WITH_GC bytes = NSAllocateCollectable(size, 0); #else zone = GSObjCZone(self); bytes = NSZoneMalloc(zone, size); #endif if (bytes == 0) { NSLog(@"[NSMutableDataMalloc -initWithCapacity:] out of memory " @"for %u bytes - %@", size, [NSError _last]); RELEASE(self); return nil; } } capacity = size; growth = capacity/2; if (growth == 0) { growth = 1; } length = 0; return self; } /** * Initialize with buffer capable of holding size bytes. Buffer is zeroed * out. */ - (id) initWithLength: (NSUInteger)size { self = [self initWithCapacity: size]; if (self) { memset(bytes, '\0', size); length = size; } return self; } - (id) initWithContentsOfFile: (NSString *)path { self = [self initWithCapacity: 0]; #if GS_WITH_GC if (readContentsOfFile(path, &bytes, &length, 0) == NO) { return nil; } #else if (readContentsOfFile(path, &bytes, &length, zone) == NO) { [self release]; return nil; } #endif capacity = length; return self; } - (id) initWithContentsOfMappedFile: (NSString *)path { return [self initWithContentsOfFile: path]; } - (void) appendBytes: (const void*)aBuffer length: (NSUInteger)bufferSize { unsigned oldLength = length; unsigned minimum = length + bufferSize; if (minimum > capacity) { [self _grow: minimum]; } memcpy(bytes + oldLength, aBuffer, bufferSize); length = minimum; } - (NSUInteger) capacity { return capacity; } - (void) _grow: (NSUInteger)minimum { if (minimum > capacity) { unsigned nextCapacity = capacity + growth; unsigned nextGrowth = capacity ? capacity : 1; while (nextCapacity < minimum) { unsigned tmp = nextCapacity + nextGrowth; nextGrowth = nextCapacity; nextCapacity = tmp; } [self setCapacity: nextCapacity]; growth = nextGrowth; } } - (void*) mutableBytes { return bytes; } - (void) replaceBytesInRange: (NSRange)aRange withBytes: (const void*)moreBytes { unsigned need = NSMaxRange(aRange); if (aRange.location > length) { [NSException raise: NSRangeException format: @"location bad in replaceByteInRange:withBytes:"]; } if (aRange.length > 0) { if (need > length) { [self setCapacity: need]; length = need; } memcpy(bytes + aRange.location, moreBytes, aRange.length); } } - (void) serializeDataAt: (const void*)data ofObjCType: (const char*)type context: (id )callback { if (data == 0 || type == 0) { if (data == 0) { NSLog(@"attempt to serialize from a null pointer"); } if (type == 0) { NSLog(@"attempt to serialize with a null type encoding"); } return; } switch (*type) { case _C_ID: [callback serializeObjectAt: (id*)data ofObjCType: type intoData: self]; return; case _C_CHARPTR: { unsigned len; int32_t ni; uint32_t minimum; if (!*(void**)data) { ni = -1; ni = GSSwapHostI32ToBig(ni); [self appendBytes: (void*)&len length: sizeof(len)]; return; } len = strlen(*(void**)data); ni = GSSwapHostI32ToBig(len); minimum = length + len + sizeof(ni); if (minimum > capacity) { [self _grow: minimum]; } memcpy(bytes+length, &ni, sizeof(ni)); length += sizeof(ni); if (len) { memcpy(bytes+length, *(void**)data, len); length += len; } return; } case _C_ARY_B: { unsigned offset = 0; unsigned size; unsigned count = atoi(++type); unsigned i; uint32_t minimum; while (isdigit(*type)) { type++; } size = objc_sizeof_type(type); /* * Serialized objects are going to take up at least as much * space as the originals, so we can calculate a minimum space * we are going to need and make sure our buffer is big enough. */ minimum = length + size*count; if (minimum > capacity) { [self _grow: minimum]; } for (i = 0; i < count; i++) { [self serializeDataAt: (char*)data + offset ofObjCType: type context: callback]; offset += size; } return; } case _C_STRUCT_B: { struct objc_struct_layout layout; objc_layout_structure (type, &layout); while (objc_layout_structure_next_member (&layout)) { unsigned offset; unsigned align; const char *ftype; objc_layout_structure_get_info (&layout, &offset, &align, &ftype); [self serializeDataAt: ((char*)data) + offset ofObjCType: ftype context: callback]; } return; } case _C_PTR: [self serializeDataAt: *(char**)data ofObjCType: ++type context: callback]; return; case _C_CHR: case _C_UCHR: (*appendImp)(self, appendSel, data, sizeof(unsigned char)); return; case _C_SHT: case _C_USHT: { unsigned short ns = NSSwapHostShortToBig(*(unsigned short*)data); (*appendImp)(self, appendSel, &ns, sizeof(unsigned short)); return; } case _C_INT: case _C_UINT: { unsigned ni = NSSwapHostIntToBig(*(unsigned int*)data); (*appendImp)(self, appendSel, &ni, sizeof(unsigned)); return; } case _C_LNG: case _C_ULNG: { unsigned long nl = NSSwapHostLongToBig(*(unsigned long*)data); (*appendImp)(self, appendSel, &nl, sizeof(unsigned long)); return; } case _C_LNG_LNG: case _C_ULNG_LNG: { unsigned long long nl; nl = NSSwapHostLongLongToBig(*(unsigned long long*)data); (*appendImp)(self, appendSel, &nl, sizeof(unsigned long long)); return; } case _C_FLT: { NSSwappedFloat nf = NSSwapHostFloatToBig(*(float*)data); (*appendImp)(self, appendSel, &nf, sizeof(NSSwappedFloat)); return; } case _C_DBL: { NSSwappedDouble nd = NSSwapHostDoubleToBig(*(double*)data); (*appendImp)(self, appendSel, &nd, sizeof(NSSwappedDouble)); return; } case _C_CLASS: { const char *name = *(Class*)data?GSNameFromClass(*(Class*)data):""; uint16_t ln = (uint16_t)strlen(name); uint32_t minimum = length + ln + sizeof(uint16_t); uint16_t ni; if (minimum > capacity) { [self _grow: minimum]; } ni = GSSwapHostI16ToBig(ln); memcpy(bytes+length, &ni, sizeof(ni)); length += sizeof(ni); if (ln) { memcpy(bytes+length, name, ln); length += ln; } return; } case _C_SEL: { const char *name = *(SEL*)data?GSNameFromSelector(*(SEL*)data):""; uint16_t ln = (name == 0) ? 0 : (uint16_t)strlen(name); const char *types = *(SEL*)data?GSTypesFromSelector(*(SEL*)data):""; uint16_t lt = (types == 0) ? 0 : (uint16_t)strlen(types); uint32_t minimum = length + ln + lt + 2*sizeof(uint16_t); uint16_t ni; if (minimum > capacity) { [self _grow: minimum]; } ni = GSSwapHostI16ToBig(ln); memcpy(bytes+length, &ni, sizeof(ni)); length += sizeof(ni); ni = GSSwapHostI16ToBig(lt); memcpy(bytes+length, &ni, sizeof(ni)); length += sizeof(ni); if (ln) { memcpy(bytes+length, name, ln); length += ln; } if (lt) { memcpy(bytes+length, types, lt); length += lt; } return; } default: [NSException raise: NSMallocException format: @"Unknown type to serialize - '%s'", type]; } } - (void) serializeTypeTag: (unsigned char)tag { if (length == capacity) { [self _grow: length + 1]; } ((unsigned char*)bytes)[length++] = tag; } - (void) serializeTypeTag: (unsigned char)tag andCrossRef: (unsigned int)xref { if (xref <= 0xff) { tag = (tag & ~_GSC_SIZE) | _GSC_X_1; if (length + 2 >= capacity) { [self _grow: length + 2]; } *(uint8_t*)(bytes + length++) = tag; *(uint8_t*)(bytes + length++) = xref; } else if (xref <= 0xffff) { uint16_t x = (uint16_t)xref; tag = (tag & ~_GSC_SIZE) | _GSC_X_2; if (length + 3 >= capacity) { [self _grow: length + 3]; } *(uint8_t*)(bytes + length++) = tag; #if NEED_WORD_ALIGNMENT if ((length % __alignof__(uint16_t)) != 0) { x = GSSwapHostI16ToBig(x); memcpy((bytes + length), &x, 2); } else #endif *(uint16_t*)(bytes + length) = GSSwapHostI16ToBig(x); length += 2; } else { uint32_t x = (uint32_t)xref; tag = (tag & ~_GSC_SIZE) | _GSC_X_4; if (length + 5 >= capacity) { [self _grow: length + 5]; } *(uint8_t*)(bytes + length++) = tag; #if NEED_WORD_ALIGNMENT if ((length % __alignof__(uint32_t)) != 0) { x = GSSwapHostI32ToBig(x); memcpy((bytes + length), &x, 4); } else #endif *(uint32_t*)(bytes + length) = GSSwapHostI32ToBig(x); length += 4; } } - (id) setCapacity: (NSUInteger)size { if (size != capacity) { void *tmp; #if GS_WITH_GC tmp = NSAllocateCollectable(size, 0); #else tmp = NSZoneMalloc(zone, size); #endif if (tmp == 0) { [NSException raise: NSMallocException format: @"Unable to set data capacity to '%d'", size]; } if (bytes) { memcpy(tmp, bytes, capacity < size ? capacity : size); #if GS_WITH_GC if (owned == YES) { NSZoneFree(NSDefaultMallocZone(), bytes); owned = NO; } #else if (zone == 0) { zone = NSDefaultMallocZone(); } else { NSZoneFree(zone, bytes); } #endif } bytes = tmp; capacity = size; growth = capacity/2; if (growth == 0) { growth = 1; } } if (size < length) { length = size; } return self; } - (void) setData: (NSData*)data { unsigned l = [data length]; [self setCapacity: l]; length = l; memcpy(bytes, [data bytes], length); } - (void) setLength: (NSUInteger)size { if (size > capacity) { [self setCapacity: size]; } if (size > length) { memset(bytes + length, '\0', size - length); } length = size; } @end #if GS_WITH_GC @implementation NSMutableDataFinalized - (void) finalize { if (owned == YES) NSZoneFree(NSDefaultMallocZone(), bytes); [super finalize]; } @end #endif #ifdef HAVE_SHMCTL @implementation NSMutableDataShared + (id) allocWithZone: (NSZone*)z { return (NSData*)NSAllocateObject([NSMutableDataShared class], 0, z); } - (void) dealloc { [self finalize]; [super dealloc]; } - (void) finalize { if (bytes != 0) { struct shmid_ds buf; if (shmctl(shmid, IPC_STAT, &buf) < 0) { NSLog(@"[NSMutableDataShared -dealloc] shared memory " @"control failed - %@", [NSError _last]); } else if (buf.shm_nattch == 1) { if (shmctl(shmid, IPC_RMID, &buf) < 0) /* Mark for deletion. */ { NSLog(@"[NSMutableDataShared -dealloc] shared memory " @"delete failed - %@", [NSError _last]); } } if (shmdt(bytes) < 0) { NSLog(@"[NSMutableDataShared -dealloc] shared memory " @"detach failed - %@", [NSError _last]); } bytes = 0; length = 0; capacity = 0; shmid = -1; } [super finalize]; } - (id) initWithBytes: (const void*)aBuffer length: (NSUInteger)bufferSize { self = [self initWithCapacity: bufferSize]; if (self) { if (bufferSize && aBuffer) memcpy(bytes, aBuffer, bufferSize); length = bufferSize; } return self; } - (id) initWithCapacity: (NSUInteger)bufferSize { shmid = shmget(IPC_PRIVATE, bufferSize, IPC_CREAT|VM_ACCESS); if (shmid == -1) /* Created memory? */ { NSLog(@"[NSMutableDataShared -initWithCapacity:] shared memory " @"get failed for %u - %@", bufferSize, [NSError _last]); RELEASE(self); self = [mutableDataMalloc allocWithZone: NSDefaultMallocZone()]; return [self initWithCapacity: bufferSize]; } bytes = shmat(shmid, 0, 0); if (bytes == (void*)-1) { NSLog(@"[NSMutableDataShared -initWithCapacity:] shared memory " @"attach failed for %u - %@", bufferSize, [NSError _last]); bytes = 0; RELEASE(self); self = [mutableDataMalloc allocWithZone: NSDefaultMallocZone()]; return [self initWithCapacity: bufferSize]; } length = 0; capacity = bufferSize; return self; } - (id) initWithShmID: (int)anId length: (NSUInteger)bufferSize { struct shmid_ds buf; shmid = anId; if (shmctl(shmid, IPC_STAT, &buf) < 0) { NSLog(@"[NSMutableDataShared -initWithShmID:length:] shared memory " @"control failed - %@", [NSError _last]); RELEASE(self); /* Unable to access memory. */ return nil; } if (buf.shm_segsz < bufferSize) { NSLog(@"[NSMutableDataShared -initWithShmID:length:] shared memory " @"segment too small"); RELEASE(self); /* Memory segment too small. */ return nil; } bytes = shmat(shmid, 0, 0); if (bytes == (void*)-1) { NSLog(@"[NSMutableDataShared -initWithShmID:length:] shared memory " @"attach failed - %@", [NSError _last]); bytes = 0; RELEASE(self); /* Unable to attach to memory. */ return nil; } length = bufferSize; capacity = length; return self; } - (id) setCapacity: (NSUInteger)size { if (size != capacity) { void *tmp; int newid; newid = shmget(IPC_PRIVATE, size, IPC_CREAT|VM_ACCESS); if (newid == -1) /* Created memory? */ { [NSException raise: NSMallocException format: @"Unable to create shared memory segment (size:%u) - %@.", size, [NSError _last]]; } tmp = shmat(newid, 0, 0); if ((intptr_t)tmp == -1) /* Attached memory? */ { [NSException raise: NSMallocException format: @"Unable to attach to shared memory segment."]; } memcpy(tmp, bytes, length); if (bytes) { struct shmid_ds buf; if (shmctl(shmid, IPC_STAT, &buf) < 0) { NSLog(@"[NSMutableDataShared -setCapacity:] shared memory " @"control failed - %@", [NSError _last]); } else if (buf.shm_nattch == 1) { if (shmctl(shmid, IPC_RMID, &buf) < 0) /* Mark for deletion. */ { NSLog(@"[NSMutableDataShared -setCapacity:] shared memory " @"delete failed - %@", [NSError _last]); } } if (shmdt(bytes) < 0) /* Detach memory. */ { NSLog(@"[NSMutableDataShared -setCapacity:] shared memory " @"detach failed - %@", [NSError _last]); } } bytes = tmp; shmid = newid; capacity = size; } if (size < length) { length = size; } return self; } - (int) shmID { return shmid; } @end #endif /* HAVE_SHMCTL */