mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-20 23:11:59 +00:00
Corrected FSF location layout. All very cosmetic git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/branches/mswin-ng@23929 72102866-910b-0410-8b05-ffd578937521
635 lines
16 KiB
Objective-C
635 lines
16 KiB
Objective-C
/** NSValue.m - Object encapsulation for C types.
|
||
Copyright (C) 1993, 1994, 1996, 1999 Free Software Foundation, Inc.
|
||
|
||
Written by: Adam Fedor <fedor@boulder.colorado.edu>
|
||
Date: Mar 1995
|
||
Updated by: Richard Frith-Macdonald <rfm@gnu.org>
|
||
Date: Jan 2001
|
||
|
||
This file is part of the GNUstep Base Library.
|
||
|
||
This library is free software; you can redistribute it and/or
|
||
modify it under the terms of the GNU Library General Public
|
||
License as published by the Free Software Foundation; either
|
||
version 2 of the License, or (at your option) any later version.
|
||
|
||
This library is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
Library General Public License for more details.
|
||
|
||
You should have received a copy of the GNU Library General Public
|
||
License along with this library; if not, write to the Free
|
||
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||
Boston, MA 02111 USA.
|
||
|
||
<title>NSValue class reference</title>
|
||
$Date$ $Revision$
|
||
*/
|
||
|
||
#include "config.h"
|
||
#include "GNUstepBase/preface.h"
|
||
#include "Foundation/NSValue.h"
|
||
#include "Foundation/NSCoder.h"
|
||
#include "Foundation/NSDictionary.h"
|
||
#include "Foundation/NSZone.h"
|
||
#include "Foundation/NSException.h"
|
||
#include "Foundation/NSMapTable.h"
|
||
#include "Foundation/NSLock.h"
|
||
#include "Foundation/NSData.h"
|
||
#include "Foundation/NSDebug.h"
|
||
|
||
@interface GSPlaceholderValue : NSValue
|
||
@end
|
||
|
||
@class GSValue;
|
||
@interface GSValue : NSObject // Help the compiler
|
||
@end
|
||
@class GSNonretainedObjectValue;
|
||
@interface GSNonretainedObjectValue : NSObject // Help the compiler
|
||
@end
|
||
@class GSPointValue;
|
||
@interface GSPointValue : NSObject // Help the compiler
|
||
@end
|
||
@class GSPointerValue;
|
||
@interface GSPointerValue : NSObject // Help the compiler
|
||
@end
|
||
@class GSRangeValue;
|
||
@interface GSRangeValue : NSObject // Help the compiler
|
||
@end
|
||
@class GSRectValue;
|
||
@interface GSRectValue : NSObject // Help the compiler
|
||
@end
|
||
@class GSSizeValue;
|
||
@interface GSSizeValue : NSObject // Help the compiler
|
||
@end
|
||
@class NSDataStatic; // Needed for decoding.
|
||
@interface NSDataStatic : NSObject // Help the compiler
|
||
@end
|
||
|
||
|
||
static Class abstractClass;
|
||
static Class concreteClass;
|
||
static Class nonretainedObjectValueClass;
|
||
static Class pointValueClass;
|
||
static Class pointerValueClass;
|
||
static Class rangeValueClass;
|
||
static Class rectValueClass;
|
||
static Class sizeValueClass;
|
||
static Class GSPlaceholderValueClass;
|
||
|
||
|
||
static GSPlaceholderValue *defaultPlaceholderValue;
|
||
static NSMapTable *placeholderMap;
|
||
static NSLock *placeholderLock;
|
||
|
||
@implementation NSValue
|
||
|
||
+ (void) initialize
|
||
{
|
||
if (self == [NSValue class])
|
||
{
|
||
abstractClass = self;
|
||
[abstractClass setVersion: 2]; // Version 2
|
||
concreteClass = [GSValue class];
|
||
nonretainedObjectValueClass = [GSNonretainedObjectValue class];
|
||
pointValueClass = [GSPointValue class];
|
||
pointerValueClass = [GSPointerValue class];
|
||
rangeValueClass = [GSRangeValue class];
|
||
rectValueClass = [GSRectValue class];
|
||
sizeValueClass = [GSSizeValue class];
|
||
GSPlaceholderValueClass = [GSPlaceholderValue class];
|
||
|
||
/*
|
||
* Set up infrastructure for placeholder values.
|
||
*/
|
||
defaultPlaceholderValue = (GSPlaceholderValue*)
|
||
NSAllocateObject(GSPlaceholderValueClass, 0, NSDefaultMallocZone());
|
||
placeholderMap = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
|
||
NSNonRetainedObjectMapValueCallBacks, 0);
|
||
placeholderLock = [NSLock new];
|
||
}
|
||
}
|
||
|
||
+ (id) allocWithZone: (NSZone*)z
|
||
{
|
||
if (self == abstractClass)
|
||
{
|
||
if (z == NSDefaultMallocZone() || z == 0)
|
||
{
|
||
/*
|
||
* As a special case, we can return a placeholder for a value
|
||
* in the default malloc zone extremely efficiently.
|
||
*/
|
||
return defaultPlaceholderValue;
|
||
}
|
||
else
|
||
{
|
||
id obj;
|
||
|
||
/*
|
||
* For anything other than the default zone, we need to
|
||
* locate the correct placeholder in the (lock protected)
|
||
* table of placeholders.
|
||
*/
|
||
[placeholderLock lock];
|
||
obj = (id)NSMapGet(placeholderMap, (void*)z);
|
||
if (obj == nil)
|
||
{
|
||
/*
|
||
* There is no placeholder object for this zone, so we
|
||
* create a new one and use that.
|
||
*/
|
||
obj = (id)NSAllocateObject(GSPlaceholderValueClass, 0, z);
|
||
NSMapInsert(placeholderMap, (void*)z, (void*)obj);
|
||
}
|
||
[placeholderLock unlock];
|
||
return obj;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
return NSAllocateObject(self, 0, z);
|
||
}
|
||
}
|
||
|
||
// NSCopying - always a simple retain.
|
||
|
||
- (id) copy
|
||
{
|
||
return RETAIN(self);
|
||
}
|
||
|
||
- (id) copyWithZone: (NSZone *)zone
|
||
{
|
||
return RETAIN(self);
|
||
}
|
||
|
||
/* Returns the concrete class associated with the type encoding */
|
||
+ (Class) valueClassWithObjCType: (const char *)type
|
||
{
|
||
Class theClass = concreteClass;
|
||
|
||
/* Let someone else deal with this error */
|
||
if (!type)
|
||
return theClass;
|
||
|
||
if (strcmp(@encode(id), type) == 0)
|
||
theClass = nonretainedObjectValueClass;
|
||
else if (strcmp(@encode(NSPoint), type) == 0)
|
||
theClass = pointValueClass;
|
||
else if (strcmp(@encode(void *), type) == 0)
|
||
theClass = pointerValueClass;
|
||
else if (strcmp(@encode(NSRange), type) == 0)
|
||
theClass = rangeValueClass;
|
||
else if (strcmp(@encode(NSRect), type) == 0)
|
||
theClass = rectValueClass;
|
||
else if (strcmp(@encode(NSSize), type) == 0)
|
||
theClass = sizeValueClass;
|
||
|
||
return theClass;
|
||
}
|
||
|
||
// Allocating and Initializing
|
||
|
||
+ (NSValue*) value: (const void *)value
|
||
withObjCType: (const char *)type
|
||
{
|
||
Class theClass = [self valueClassWithObjCType: type];
|
||
NSValue *theObj;
|
||
|
||
theObj = [theClass allocWithZone: NSDefaultMallocZone()];
|
||
theObj = [theObj initWithBytes: value objCType: type];
|
||
return AUTORELEASE(theObj);
|
||
}
|
||
|
||
+ (NSValue*) valueWithBytes: (const void *)value
|
||
objCType: (const char *)type
|
||
{
|
||
Class theClass = [self valueClassWithObjCType: type];
|
||
NSValue *theObj;
|
||
|
||
theObj = [theClass allocWithZone: NSDefaultMallocZone()];
|
||
theObj = [theObj initWithBytes: value objCType: type];
|
||
return AUTORELEASE(theObj);
|
||
}
|
||
|
||
+ (NSValue*) valueWithNonretainedObject: (id)anObject
|
||
{
|
||
NSValue *theObj;
|
||
|
||
theObj = [nonretainedObjectValueClass allocWithZone: NSDefaultMallocZone()];
|
||
theObj = [theObj initWithBytes: &anObject objCType: @encode(id)];
|
||
return AUTORELEASE(theObj);
|
||
}
|
||
|
||
+ (NSValue*) valueWithPoint: (NSPoint)point
|
||
{
|
||
NSValue *theObj;
|
||
|
||
theObj = [pointValueClass allocWithZone: NSDefaultMallocZone()];
|
||
theObj = [theObj initWithBytes: &point objCType: @encode(NSPoint)];
|
||
return AUTORELEASE(theObj);
|
||
}
|
||
|
||
+ (NSValue*) valueWithPointer: (const void *)pointer
|
||
{
|
||
NSValue *theObj;
|
||
|
||
theObj = [pointerValueClass allocWithZone: NSDefaultMallocZone()];
|
||
theObj = [theObj initWithBytes: &pointer objCType: @encode(void*)];
|
||
return AUTORELEASE(theObj);
|
||
}
|
||
|
||
+ (NSValue*) valueWithRange: (NSRange)range
|
||
{
|
||
NSValue *theObj;
|
||
|
||
theObj = [rangeValueClass allocWithZone: NSDefaultMallocZone()];
|
||
theObj = [theObj initWithBytes: &range objCType: @encode(NSRange)];
|
||
return AUTORELEASE(theObj);
|
||
}
|
||
|
||
+ (NSValue*) valueWithRect: (NSRect)rect
|
||
{
|
||
NSValue *theObj;
|
||
|
||
theObj = [rectValueClass allocWithZone: NSDefaultMallocZone()];
|
||
theObj = [theObj initWithBytes: &rect objCType: @encode(NSRect)];
|
||
return AUTORELEASE(theObj);
|
||
}
|
||
|
||
+ (NSValue*) valueWithSize: (NSSize)size
|
||
{
|
||
NSValue *theObj;
|
||
|
||
theObj = [sizeValueClass allocWithZone: NSDefaultMallocZone()];
|
||
theObj = [theObj initWithBytes: &size objCType: @encode(NSSize)];
|
||
return AUTORELEASE(theObj);
|
||
}
|
||
|
||
+ (NSValue*) valueFromString: (NSString *)string
|
||
{
|
||
NSDictionary *dict = [string propertyList];
|
||
|
||
if (dict == nil)
|
||
return nil;
|
||
|
||
if ([dict objectForKey: @"location"])
|
||
{
|
||
NSRange range;
|
||
range = NSMakeRange([[dict objectForKey: @"location"] intValue],
|
||
[[dict objectForKey: @"length"] intValue]);
|
||
}
|
||
else if ([dict objectForKey: @"width"] && [dict objectForKey: @"x"])
|
||
{
|
||
NSRect rect;
|
||
rect = NSMakeRect([[dict objectForKey: @"x"] floatValue],
|
||
[[dict objectForKey: @"y"] floatValue],
|
||
[[dict objectForKey: @"width"] floatValue],
|
||
[[dict objectForKey: @"height"] floatValue]);
|
||
return [abstractClass valueWithRect: rect];
|
||
}
|
||
else if ([dict objectForKey: @"width"])
|
||
{
|
||
NSSize size;
|
||
size = NSMakeSize([[dict objectForKey: @"width"] floatValue],
|
||
[[dict objectForKey: @"height"] floatValue]);
|
||
return [abstractClass valueWithSize: size];
|
||
}
|
||
else if ([dict objectForKey: @"x"])
|
||
{
|
||
NSPoint point;
|
||
point = NSMakePoint([[dict objectForKey: @"x"] floatValue],
|
||
[[dict objectForKey: @"y"] floatValue]);
|
||
return [abstractClass valueWithPoint: point];
|
||
}
|
||
return nil;
|
||
}
|
||
|
||
- (id) initWithBytes: (const void*)data objCType: (const char*)type
|
||
{
|
||
[self subclassResponsibility: _cmd];
|
||
return nil;
|
||
}
|
||
|
||
// Accessing Data
|
||
/* All the rest of these methods must be implemented by a subclass */
|
||
- (void) getValue: (void *)value
|
||
{
|
||
[self subclassResponsibility: _cmd];
|
||
}
|
||
|
||
- (BOOL) isEqual: (id)other
|
||
{
|
||
if ([other isKindOfClass: [self class]])
|
||
{
|
||
return [self isEqualToValue: other];
|
||
}
|
||
return NO;
|
||
}
|
||
|
||
- (BOOL) isEqualToValue: (NSValue*)other
|
||
{
|
||
[self subclassResponsibility: _cmd];
|
||
return NO;
|
||
}
|
||
|
||
- (const char *) objCType
|
||
{
|
||
[self subclassResponsibility: _cmd];
|
||
return 0;
|
||
}
|
||
|
||
- (id) nonretainedObjectValue
|
||
{
|
||
[self subclassResponsibility: _cmd];
|
||
return 0;
|
||
}
|
||
|
||
- (void *) pointerValue
|
||
{
|
||
[self subclassResponsibility: _cmd];
|
||
return 0;
|
||
}
|
||
|
||
- (NSRange) rangeValue
|
||
{
|
||
[self subclassResponsibility: _cmd];
|
||
return NSMakeRange(0,0);
|
||
}
|
||
|
||
- (NSRect) rectValue
|
||
{
|
||
[self subclassResponsibility: _cmd];
|
||
return NSMakeRect(0,0,0,0);
|
||
}
|
||
|
||
- (NSSize) sizeValue
|
||
{
|
||
[self subclassResponsibility: _cmd];
|
||
return NSMakeSize(0,0);
|
||
}
|
||
|
||
- (NSPoint) pointValue
|
||
{
|
||
[self subclassResponsibility: _cmd];
|
||
return NSMakePoint(0,0);
|
||
}
|
||
|
||
- (Class) classForCoder
|
||
{
|
||
return abstractClass;
|
||
}
|
||
|
||
- (void) encodeWithCoder: (NSCoder *)coder
|
||
{
|
||
unsigned size;
|
||
const char *data;
|
||
const char *objctype = [self objCType];
|
||
NSMutableData *d;
|
||
|
||
size = strlen(objctype)+1;
|
||
[coder encodeValueOfObjCType: @encode(unsigned) at: &size];
|
||
[coder encodeArrayOfObjCType: @encode(signed char) count: size at: objctype];
|
||
size = objc_sizeof_type(objctype);
|
||
data = (void *)NSZoneMalloc(GSObjCZone(self), size);
|
||
[self getValue: (void*)data];
|
||
d = [NSMutableData new];
|
||
[d serializeDataAt: data ofObjCType: objctype context: nil];
|
||
size = [d length];
|
||
[coder encodeValueOfObjCType: @encode(unsigned) at: &size];
|
||
NSZoneFree(NSDefaultMallocZone(), (void*)data);
|
||
data = [d bytes];
|
||
[coder encodeArrayOfObjCType: @encode(unsigned char) count: size at: data];
|
||
RELEASE(d);
|
||
}
|
||
|
||
- (id) initWithCoder: (NSCoder *)coder
|
||
{
|
||
char type[64];
|
||
const char *objctype;
|
||
Class c;
|
||
id o;
|
||
unsigned size;
|
||
int ver;
|
||
|
||
[coder decodeValueOfObjCType: @encode(unsigned) at: &size];
|
||
/*
|
||
* For almost all type encodings, we can use space on the stack,
|
||
* but to handle exceptionally large ones (possibly some huge structs)
|
||
* we have a strategy of allocating and deallocating heap space too.
|
||
*/
|
||
if (size <= 64)
|
||
{
|
||
objctype = type;
|
||
}
|
||
else
|
||
{
|
||
objctype = (void*)NSZoneMalloc(NSDefaultMallocZone(), size);
|
||
}
|
||
[coder decodeArrayOfObjCType: @encode(signed char)
|
||
count: size
|
||
at: (void*)objctype];
|
||
c = [abstractClass valueClassWithObjCType: objctype];
|
||
o = [c allocWithZone: [coder objectZone]];
|
||
|
||
ver = [coder versionForClassName: @"NSValue"];
|
||
if (ver < 2)
|
||
{
|
||
if (ver < 1)
|
||
{
|
||
if (c == pointValueClass)
|
||
{
|
||
NSPoint v;
|
||
|
||
[coder decodeValueOfObjCType: @encode(NSPoint) at: &v];
|
||
o = [o initWithBytes: &v objCType: @encode(NSPoint)];
|
||
}
|
||
else if (c == sizeValueClass)
|
||
{
|
||
NSSize v;
|
||
|
||
[coder decodeValueOfObjCType: @encode(NSSize) at: &v];
|
||
o = [o initWithBytes: &v objCType: @encode(NSSize)];
|
||
}
|
||
else if (c == rangeValueClass)
|
||
{
|
||
NSRange v;
|
||
|
||
[coder decodeValueOfObjCType: @encode(NSRange) at: &v];
|
||
o = [o initWithBytes: &v objCType: @encode(NSRange)];
|
||
}
|
||
else if (c == rectValueClass)
|
||
{
|
||
NSRect v;
|
||
|
||
[coder decodeValueOfObjCType: @encode(NSRect) at: &v];
|
||
o = [o initWithBytes: &v objCType: @encode(NSRect)];
|
||
}
|
||
else
|
||
{
|
||
unsigned char *data;
|
||
|
||
[coder decodeValueOfObjCType: @encode(unsigned) at: &size];
|
||
data = (void *)NSZoneMalloc(NSDefaultMallocZone(), size);
|
||
[coder decodeArrayOfObjCType: @encode(unsigned char)
|
||
count: size
|
||
at: (void*)data];
|
||
o = [o initWithBytes: data objCType: objctype];
|
||
NSZoneFree(NSDefaultMallocZone(), data);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
NSData *d;
|
||
unsigned cursor = 0;
|
||
|
||
/*
|
||
* For performance, decode small values directly onto the stack,
|
||
* For larger values we allocate and deallocate heap space.
|
||
*/
|
||
size = objc_sizeof_type(objctype);
|
||
if (size <= 64)
|
||
{
|
||
unsigned char data[size];
|
||
|
||
[coder decodeValueOfObjCType: @encode(id) at: &d];
|
||
[d deserializeDataAt: data
|
||
ofObjCType: objctype
|
||
atCursor: &cursor
|
||
context: nil];
|
||
o = [o initWithBytes: data objCType: objctype];
|
||
RELEASE(d);
|
||
}
|
||
else
|
||
{
|
||
unsigned char *data;
|
||
|
||
data = (void *)NSZoneMalloc(NSDefaultMallocZone(), size);
|
||
[coder decodeValueOfObjCType: @encode(id) at: &d];
|
||
[d deserializeDataAt: data
|
||
ofObjCType: objctype
|
||
atCursor: &cursor
|
||
context: nil];
|
||
o = [o initWithBytes: data objCType: objctype];
|
||
RELEASE(d);
|
||
NSZoneFree(NSDefaultMallocZone(), data);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
static NSData *d = nil;
|
||
unsigned cursor = 0;
|
||
|
||
if (d == nil)
|
||
{
|
||
d = [NSDataStatic allocWithZone: NSDefaultMallocZone()];
|
||
}
|
||
/*
|
||
* For performance, decode small values directly onto the stack,
|
||
* For larger values we allocate and deallocate heap space.
|
||
*/
|
||
size = objc_sizeof_type(objctype);
|
||
if (size <= 64)
|
||
{
|
||
unsigned char data[size];
|
||
|
||
[coder decodeValueOfObjCType: @encode(unsigned) at: &size];
|
||
{
|
||
unsigned char serialized[size];
|
||
|
||
[coder decodeArrayOfObjCType: @encode(unsigned char)
|
||
count: size
|
||
at: (void*)serialized];
|
||
d = [d initWithBytesNoCopy: (void*)serialized length: size];
|
||
[d deserializeDataAt: data
|
||
ofObjCType: objctype
|
||
atCursor: &cursor
|
||
context: nil];
|
||
}
|
||
o = [o initWithBytes: data objCType: objctype];
|
||
}
|
||
else
|
||
{
|
||
void *data;
|
||
|
||
data = (void *)NSZoneMalloc(NSDefaultMallocZone(), size);
|
||
[coder decodeValueOfObjCType: @encode(unsigned) at: &size];
|
||
{
|
||
void *serialized;
|
||
|
||
serialized = (void *)NSZoneMalloc(NSDefaultMallocZone(), size);
|
||
[coder decodeArrayOfObjCType: @encode(unsigned char)
|
||
count: size
|
||
at: serialized];
|
||
d = [d initWithBytesNoCopy: serialized length: size];
|
||
[d deserializeDataAt: data
|
||
ofObjCType: objctype
|
||
atCursor: &cursor
|
||
context: nil];
|
||
NSZoneFree(NSDefaultMallocZone(), serialized);
|
||
}
|
||
o = [o initWithBytes: data objCType: objctype];
|
||
NSZoneFree(NSDefaultMallocZone(), data);
|
||
}
|
||
}
|
||
if (objctype != type)
|
||
{
|
||
NSZoneFree(NSDefaultMallocZone(), (void*)objctype);
|
||
}
|
||
RELEASE(self);
|
||
self = o;
|
||
return self;
|
||
}
|
||
|
||
@end
|
||
|
||
|
||
|
||
@implementation GSPlaceholderValue
|
||
|
||
- (id) autorelease
|
||
{
|
||
NSWarnLog(@"-autorelease sent to uninitialised value");
|
||
return self; // placeholders never get released.
|
||
}
|
||
|
||
- (void) dealloc
|
||
{
|
||
GSNOSUPERDEALLOC; // placeholders never get deallocated.
|
||
}
|
||
|
||
- (void) getData: (void*)data
|
||
{
|
||
[NSException raise: NSInternalInconsistencyException
|
||
format: @"attempt to use uninitialised value"];
|
||
}
|
||
|
||
- (id) initWithBytes: (const void*)data objCType: (const char*)type
|
||
{
|
||
Class c = [abstractClass valueClassWithObjCType: type];
|
||
|
||
self = (id)NSAllocateObject(c, 0, GSObjCZone(self));
|
||
return [self initWithBytes: data objCType: type];
|
||
}
|
||
|
||
- (const char*) objCType
|
||
{
|
||
[NSException raise: NSInternalInconsistencyException
|
||
format: @"attempt to use uninitialised value"];
|
||
return 0;
|
||
}
|
||
|
||
- (void) release
|
||
{
|
||
return; // placeholders never get released.
|
||
}
|
||
|
||
- (id) retain
|
||
{
|
||
return self; // placeholders never get retained.
|
||
}
|
||
@end
|
||
|