mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-20 15:00:59 +00:00
1173 lines
24 KiB
Objective-C
1173 lines
24 KiB
Objective-C
/** Implementation of NSNumber for GNUStep
|
|
Copyright (C) 2010 Free Software Foundation, Inc.
|
|
|
|
Written by: David Chisnall
|
|
Partial rewrite: Richard Frith-Macdonld <rfm@gnu.org>
|
|
(to compile on gnu/linux and mswindows,
|
|
to meet coding/style standards,
|
|
to restore lost functionality)
|
|
|
|
Date: February 2010
|
|
|
|
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
|
|
Lesser 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., 31 Milk Street #960789 Boston, MA 02196 USA.
|
|
|
|
*/
|
|
|
|
|
|
#import "common.h"
|
|
|
|
#if !defined(LLONG_MAX)
|
|
# if defined(__LONG_LONG_MAX__)
|
|
# define LLONG_MAX __LONG_LONG_MAX__
|
|
# define LLONG_MIN (-LLONG_MAX-1)
|
|
# define ULLONG_MAX (LLONG_MAX * 2ULL + 1)
|
|
# else
|
|
# error Neither LLONG_MAX nor __LONG_LONG_MAX__ found
|
|
# endif
|
|
#endif
|
|
|
|
|
|
#import "common.h"
|
|
#import "Foundation/NSCoder.h"
|
|
#import "Foundation/NSDecimalNumber.h"
|
|
#import "Foundation/NSException.h"
|
|
#import "Foundation/NSValue.h"
|
|
#if __has_include(<objc/runtime.h>)
|
|
# include <objc/runtime.h>
|
|
#endif
|
|
|
|
/*
|
|
* NSNumber implementation. This matches the behaviour of Apple's
|
|
* implementation. Values in the range -1 to 12 inclusive are mapped to
|
|
* singletons. All other values are mapped to the smallest signed value that
|
|
* will store them, unless they are greater than LLONG_MAX, in which case
|
|
* they are stored in an unsigned long long.
|
|
* Booleans are handled as a special case since some stuff (notably interface
|
|
* builder (nib) archives) needs to differentiate between booleans and integers.
|
|
*/
|
|
|
|
@interface NSSignedIntegerNumber : NSNumber
|
|
@end
|
|
|
|
@interface NSIntNumber : NSSignedIntegerNumber
|
|
{
|
|
@public
|
|
int value;
|
|
}
|
|
@end
|
|
|
|
/* Some code needs to differentiate between booleans and other NSNumber
|
|
* instances, so we need a special subclass to permit that.
|
|
*/
|
|
@interface NSBoolNumber : NSIntNumber
|
|
@end
|
|
|
|
@interface NSLongLongNumber : NSSignedIntegerNumber
|
|
{
|
|
@public
|
|
long long int value;
|
|
}
|
|
@end
|
|
|
|
@interface NSUnsignedLongLongNumber : NSNumber
|
|
{
|
|
@public
|
|
unsigned long long int value;
|
|
}
|
|
@end
|
|
|
|
// The value ivar in all of the concrete classes contains the real value.
|
|
#define VALUE value
|
|
#define COMPARE(value, other) \
|
|
if (value < other)\
|
|
{\
|
|
return NSOrderedAscending;\
|
|
}\
|
|
if (value > other)\
|
|
{\
|
|
return NSOrderedDescending;\
|
|
}\
|
|
return NSOrderedSame;
|
|
|
|
#define DCOMPARE(value, other) \
|
|
if (isnan(value)) \
|
|
{ \
|
|
if (isnan(other)) \
|
|
{ \
|
|
return NSOrderedSame; \
|
|
} \
|
|
else \
|
|
{ \
|
|
return NSOrderedAscending; \
|
|
} \
|
|
} \
|
|
else \
|
|
{ \
|
|
if (isnan(other)) \
|
|
{ \
|
|
if (value < 0.0) \
|
|
{ \
|
|
return NSOrderedAscending; \
|
|
} \
|
|
return NSOrderedDescending; \
|
|
} \
|
|
else if (value < other) \
|
|
{ \
|
|
return NSOrderedAscending; \
|
|
} \
|
|
else if (value > other) \
|
|
{ \
|
|
return NSOrderedDescending; \
|
|
} \
|
|
return NSOrderedSame; \
|
|
}
|
|
|
|
@implementation NSSignedIntegerNumber
|
|
- (NSComparisonResult) compare: (NSNumber*)aNumber
|
|
{
|
|
if (aNumber == self)
|
|
{
|
|
return NSOrderedSame;
|
|
}
|
|
if (aNumber == nil)
|
|
{
|
|
[NSException raise: NSInvalidArgumentException
|
|
format: @"nil argument for compare:"];
|
|
}
|
|
|
|
switch ([aNumber objCType][0])
|
|
{
|
|
/* For cases smaller than or equal to an int, we could get the int
|
|
* value and compare.
|
|
*/
|
|
case 'c':
|
|
case 'C':
|
|
case 's':
|
|
case 'S':
|
|
case 'i':
|
|
case 'I':
|
|
case 'l':
|
|
case 'L':
|
|
case 'q':
|
|
{
|
|
long long value = [self longLongValue];
|
|
long long other = [aNumber longLongValue];
|
|
|
|
COMPARE (value, other);
|
|
}
|
|
case 'Q':
|
|
{
|
|
unsigned long long other;
|
|
unsigned long long value;
|
|
long long v;
|
|
|
|
/* According to the C type promotion rules, we should cast this to
|
|
* an unsigned long long, however Apple's code does not do this.
|
|
* Instead, it performs a real comparison.
|
|
*/
|
|
v = [self longLongValue];
|
|
|
|
/* If this value is less than 0, then it is less than any value
|
|
* that can possibly be stored in an unsigned value.
|
|
*/
|
|
if (v < 0)
|
|
{
|
|
return NSOrderedAscending;
|
|
}
|
|
|
|
other = [aNumber unsignedLongLongValue];
|
|
value = (unsigned long long) v;
|
|
COMPARE (value, other);
|
|
}
|
|
case 'f':
|
|
case 'd':
|
|
{
|
|
double other = [aNumber doubleValue];
|
|
double value = [self doubleValue];
|
|
|
|
DCOMPARE(value, other)
|
|
}
|
|
default:
|
|
[NSException raise: NSInvalidArgumentException
|
|
format: @"unrecognised type for compare:"];
|
|
}
|
|
return 0; // Not reached.
|
|
}
|
|
@end
|
|
|
|
@implementation NSIntNumber
|
|
#define FORMAT @"%i"
|
|
#include "NSNumberMethods.h"
|
|
@end
|
|
|
|
@implementation NSBoolNumber
|
|
- (void) getValue: (void*)buffer
|
|
{
|
|
BOOL *ptr = (BOOL*)buffer;
|
|
*ptr = VALUE;
|
|
}
|
|
- (const char *) objCType
|
|
{
|
|
return @encode(BOOL);
|
|
}
|
|
@end
|
|
|
|
@implementation NSLongLongNumber
|
|
#define FORMAT @"%lli"
|
|
#include "NSNumberMethods.h"
|
|
@end
|
|
|
|
@implementation NSUnsignedLongLongNumber
|
|
#define FORMAT @"%llu"
|
|
#include "NSNumberMethods.h"
|
|
- (NSComparisonResult) compare: (NSNumber*)aNumber
|
|
{
|
|
if (aNumber == self)
|
|
{
|
|
return NSOrderedSame;
|
|
}
|
|
if (aNumber == nil)
|
|
{
|
|
[NSException raise: NSInvalidArgumentException
|
|
format: @"nil argument for compare:"];
|
|
}
|
|
|
|
switch ([aNumber objCType][0])
|
|
{
|
|
/* For cases smaller than or equal to an int, we could get the int
|
|
* value and compare.
|
|
*/
|
|
case 'c':
|
|
case 'C':
|
|
case 's':
|
|
case 'S':
|
|
case 'i':
|
|
case 'I':
|
|
case 'l':
|
|
case 'L':
|
|
case 'q':
|
|
{
|
|
long long other = [aNumber longLongValue];
|
|
|
|
if (other < 0)
|
|
{
|
|
return NSOrderedDescending;
|
|
}
|
|
COMPARE (value, ((unsigned long long) other));
|
|
}
|
|
case 'Q':
|
|
{
|
|
unsigned long long other = [aNumber unsignedLongLongValue];
|
|
|
|
COMPARE (value, other);
|
|
}
|
|
case 'f':
|
|
case 'd':
|
|
{
|
|
double other = [aNumber doubleValue];
|
|
double selfv = [self doubleValue];
|
|
|
|
DCOMPARE(selfv, other)
|
|
}
|
|
default:
|
|
[NSException raise: NSInvalidArgumentException
|
|
format: @"unrecognised type for compare:"];
|
|
}
|
|
return 0; // Not reached.
|
|
}
|
|
@end
|
|
|
|
/*
|
|
* Abstract superclass for floating point numbers.
|
|
*/
|
|
@interface NSFloatingPointNumber : NSNumber
|
|
@end
|
|
|
|
@implementation NSFloatingPointNumber
|
|
/* For floats, the type promotion rules say that we always promote to a
|
|
* floating point type, even if the other value is really an integer.
|
|
*/
|
|
- (BOOL) isEqualToNumber: (NSNumber*)aNumber
|
|
{
|
|
return ([self doubleValue] == [aNumber doubleValue]) ? YES : NO;
|
|
}
|
|
|
|
- (NSComparisonResult) compare: (NSNumber*)aNumber
|
|
{
|
|
double other;
|
|
double value;
|
|
|
|
if (aNumber == self)
|
|
{
|
|
return NSOrderedSame;
|
|
}
|
|
if (aNumber == nil)
|
|
{
|
|
[NSException raise: NSInvalidArgumentException
|
|
format: @"nil argument for compare:"];
|
|
}
|
|
|
|
other = [aNumber doubleValue];
|
|
value = [self doubleValue];
|
|
|
|
DCOMPARE(value, other)
|
|
}
|
|
@end
|
|
|
|
@interface NSFloatNumber : NSFloatingPointNumber
|
|
{
|
|
@public
|
|
float value;
|
|
}
|
|
@end
|
|
|
|
@implementation NSFloatNumber
|
|
#define FORMAT @"%0.7g"
|
|
#include "NSNumberMethods.h"
|
|
@end
|
|
|
|
@interface NSDoubleNumber : NSFloatingPointNumber
|
|
{
|
|
@public
|
|
double value;
|
|
}
|
|
@end
|
|
|
|
@implementation NSDoubleNumber
|
|
#define FORMAT @"%0.16g"
|
|
#include "NSNumberMethods.h"
|
|
@end
|
|
|
|
#ifdef OBJC_SMALL_OBJECT_SHIFT
|
|
static BOOL useSmallInt;
|
|
#if OBJC_SMALL_OBJECT_SHIFT == 3
|
|
static BOOL useSmallExtendedDouble;
|
|
static BOOL useSmallRepeatingDouble;
|
|
static BOOL useSmallFloat;
|
|
#endif
|
|
#define SMALL_INT_MASK 1
|
|
#define SMALL_EXTENDED_DOUBLE_MASK 2
|
|
#define SMALL_REPEATING_DOUBLE_MASK 3
|
|
// 4 is GSTinyString
|
|
#define SMALL_FLOAT_MASK 5
|
|
|
|
@interface NSSmallInt : NSSignedIntegerNumber
|
|
@end
|
|
|
|
@implementation NSSmallInt
|
|
#undef VALUE
|
|
#define VALUE (((intptr_t)self) >> OBJC_SMALL_OBJECT_SHIFT)
|
|
#define FORMAT @"%"PRIdPTR
|
|
#include "NSNumberMethods.h"
|
|
|
|
+ (void) load
|
|
{
|
|
useSmallInt = objc_registerSmallObjectClass_np(self, SMALL_INT_MASK);
|
|
}
|
|
|
|
+ (id) alloc
|
|
{
|
|
return (id)1;
|
|
}
|
|
|
|
+ (id) allocWithZone: (NSZone*)aZone
|
|
{
|
|
return (id)1;
|
|
}
|
|
|
|
- (id) copy
|
|
{
|
|
return self;
|
|
}
|
|
|
|
- (id) copyWithZone: (NSZone*)aZone
|
|
{
|
|
return self;
|
|
}
|
|
|
|
- (id) retain
|
|
{
|
|
return self;
|
|
}
|
|
|
|
- (NSUInteger) retainCount
|
|
{
|
|
return UINT_MAX;
|
|
}
|
|
|
|
- (id) autorelease
|
|
{
|
|
return self;
|
|
}
|
|
|
|
- (oneway void) release
|
|
{
|
|
return;
|
|
}
|
|
@end
|
|
|
|
#if OBJC_SMALL_OBJECT_SHIFT == 3
|
|
|
|
union BoxedDouble
|
|
{
|
|
id obj;
|
|
uintptr_t bits;
|
|
double d;
|
|
};
|
|
|
|
@interface NSSmallExtendedDouble : NSFloatingPointNumber
|
|
@end
|
|
|
|
@implementation NSSmallExtendedDouble
|
|
static double
|
|
unboxSmallExtendedDouble(uintptr_t boxed)
|
|
{
|
|
// The low bit of the mantissa
|
|
uintptr_t mask = boxed & 8;
|
|
union BoxedDouble ret;
|
|
// Clear the class pointer
|
|
boxed &= ~7;
|
|
ret.bits = boxed | (mask >> 1) | (mask >> 2) | (mask >> 3);
|
|
return ret.d;
|
|
}
|
|
|
|
static BOOL
|
|
isSmallExtendedDouble(double d)
|
|
{
|
|
union BoxedDouble b = {.d=d};
|
|
return unboxSmallExtendedDouble(b.bits) == d;
|
|
}
|
|
|
|
static double
|
|
unboxSmallRepeatingDouble(uintptr_t boxed)
|
|
{
|
|
// The low bit of the mantissa
|
|
uintptr_t mask = boxed & 56;
|
|
union BoxedDouble ret;
|
|
// Clear the class pointer
|
|
boxed &= ~7;
|
|
ret.bits = boxed | (mask >> 3);
|
|
return ret.d;
|
|
}
|
|
|
|
static BOOL
|
|
isSmallRepeatingDouble(double d)
|
|
{
|
|
union BoxedDouble b = {.d=d};
|
|
return unboxSmallRepeatingDouble(b.bits) == d;
|
|
}
|
|
|
|
static id
|
|
boxDouble(double d, uintptr_t mask)
|
|
{
|
|
union BoxedDouble b = {.d=d};
|
|
b.bits &= ~OBJC_SMALL_OBJECT_MASK;
|
|
b.bits |= mask;
|
|
return b.obj;
|
|
}
|
|
|
|
#undef VALUE
|
|
#define VALUE (unboxSmallExtendedDouble((uintptr_t)self))
|
|
#define FORMAT @"%0.16g"
|
|
#include "NSNumberMethods.h"
|
|
|
|
+ (void) load
|
|
{
|
|
useSmallExtendedDouble = objc_registerSmallObjectClass_np
|
|
(self, SMALL_EXTENDED_DOUBLE_MASK);
|
|
}
|
|
|
|
+ (id) alloc
|
|
{
|
|
return (id)SMALL_EXTENDED_DOUBLE_MASK;
|
|
}
|
|
|
|
+ (id) allocWithZone: (NSZone*)aZone
|
|
{
|
|
return (id)SMALL_EXTENDED_DOUBLE_MASK;
|
|
}
|
|
|
|
- (id) copy
|
|
{
|
|
return self;
|
|
}
|
|
|
|
- (id) copyWithZone: (NSZone*)aZone
|
|
{
|
|
return self;
|
|
}
|
|
|
|
- (id) retain
|
|
{
|
|
return self;
|
|
}
|
|
|
|
- (NSUInteger) retainCount
|
|
{
|
|
return UINT_MAX;
|
|
}
|
|
|
|
- (id) autorelease
|
|
{
|
|
return self;
|
|
}
|
|
|
|
- (oneway void) release
|
|
{
|
|
return;
|
|
}
|
|
@end
|
|
|
|
@interface NSSmallRepeatingDouble : NSFloatingPointNumber
|
|
@end
|
|
|
|
@implementation NSSmallRepeatingDouble
|
|
#undef VALUE
|
|
#define VALUE (unboxSmallRepeatingDouble((uintptr_t)self))
|
|
#define FORMAT @"%0.16g"
|
|
#include "NSNumberMethods.h"
|
|
|
|
+ (void) load
|
|
{
|
|
useSmallRepeatingDouble = objc_registerSmallObjectClass_np
|
|
(self, SMALL_REPEATING_DOUBLE_MASK);
|
|
}
|
|
|
|
+ (id) alloc
|
|
{
|
|
return (id)SMALL_REPEATING_DOUBLE_MASK;
|
|
}
|
|
|
|
+ (id) allocWithZone: (NSZone*)aZone
|
|
{
|
|
return (id)SMALL_REPEATING_DOUBLE_MASK;
|
|
}
|
|
|
|
- (id) copy
|
|
{
|
|
return self;
|
|
}
|
|
|
|
- (id) copyWithZone: (NSZone*)aZone
|
|
{
|
|
return self;
|
|
}
|
|
|
|
- (id) retain
|
|
{
|
|
return self;
|
|
}
|
|
|
|
- (NSUInteger) retainCount
|
|
{
|
|
return UINT_MAX;
|
|
}
|
|
|
|
- (id) autorelease
|
|
{
|
|
return self;
|
|
}
|
|
|
|
- (oneway void) release
|
|
{
|
|
return;
|
|
}
|
|
@end
|
|
|
|
|
|
/*
|
|
* Technically, all floats are small on 64bit and fit into a NSRepeatingDouble,
|
|
* but we want to get the description FORMAT right for floats (i.e. "%0.7g" and
|
|
* not "%0.16g".
|
|
*/
|
|
@interface NSSmallFloat : NSSmallRepeatingDouble
|
|
@end
|
|
@implementation NSSmallFloat
|
|
#undef VALUE
|
|
#define VALUE (unboxSmallRepeatingDouble((uintptr_t)self))
|
|
#define FORMAT @"%0.7g"
|
|
#include "NSNumberMethods.h"
|
|
|
|
+ (void) load
|
|
{
|
|
useSmallFloat = objc_registerSmallObjectClass_np
|
|
(self, SMALL_FLOAT_MASK);
|
|
}
|
|
|
|
+ (id) alloc
|
|
{
|
|
return (id)SMALL_FLOAT_MASK;
|
|
}
|
|
|
|
+ (id) allocWithZone: (NSZone*)aZone
|
|
{
|
|
return (id)SMALL_FLOAT_MASK;
|
|
}
|
|
@end
|
|
|
|
|
|
#endif
|
|
#endif
|
|
|
|
@implementation NSNumber
|
|
|
|
static Class NSNumberClass;
|
|
static Class NSBoolNumberClass;
|
|
static Class NSIntNumberClass;
|
|
static Class NSLongLongNumberClass;
|
|
static Class NSUnsignedLongLongNumberClass;
|
|
static Class NSFloatNumberClass;
|
|
static Class NSDoubleNumberClass;
|
|
|
|
/*
|
|
* Numbers from -1 to 12 inclusive that are reused.
|
|
*/
|
|
static NSNumber *ReusedInstances[14];
|
|
static NSBoolNumber *boolY; // Boolean YES (integer 1)
|
|
static NSBoolNumber *boolN; // Boolean NO (integer 0)
|
|
|
|
+ (void) initialize
|
|
{
|
|
int i;
|
|
|
|
if ([NSNumber class] != self)
|
|
{
|
|
return;
|
|
}
|
|
|
|
NSNumberClass = self;
|
|
NSBoolNumberClass = [NSBoolNumber class];
|
|
NSIntNumberClass = [NSIntNumber class];
|
|
NSLongLongNumberClass = [NSLongLongNumber class];
|
|
NSUnsignedLongLongNumberClass = [NSUnsignedLongLongNumber class];
|
|
NSFloatNumberClass = [NSFloatNumber class];
|
|
NSDoubleNumberClass = [NSDoubleNumber class];
|
|
|
|
boolY = NSAllocateObject(NSBoolNumberClass, 0, 0);
|
|
[[NSObject leakAt: &boolY] release];
|
|
boolY->value = 1;
|
|
boolN = NSAllocateObject(NSBoolNumberClass, 0, 0);
|
|
boolN->value = 0;
|
|
[[NSObject leakAt: &boolN] release];
|
|
|
|
for (i = 0; i < 14; i++)
|
|
{
|
|
NSIntNumber *n = NSAllocateObject(NSIntNumberClass, 0, 0);
|
|
|
|
n->value = i - 1;
|
|
ReusedInstances[i] = n;
|
|
[[NSObject leakAt: &ReusedInstances[i]] release];
|
|
}
|
|
}
|
|
|
|
- (const char *) objCType
|
|
{
|
|
/* All concrete NSNumber types must implement this so we know which one
|
|
* they are.
|
|
*/
|
|
[self subclassResponsibility: _cmd];
|
|
return NULL; // Not reached
|
|
}
|
|
|
|
- (BOOL) isEqualToNumber: (NSNumber*)aNumber
|
|
{
|
|
return ([self compare: aNumber] == NSOrderedSame) ? YES : NO;
|
|
}
|
|
|
|
- (BOOL) isEqual: (id)anObject
|
|
{
|
|
if ([anObject isKindOfClass: NSNumberClass])
|
|
{
|
|
return [self isEqualToNumber: anObject];
|
|
}
|
|
return [super isEqual: anObject];
|
|
}
|
|
|
|
- (BOOL) isEqualToValue: (NSValue*)aValue
|
|
{
|
|
if ([aValue isKindOfClass: NSNumberClass])
|
|
{
|
|
return [self isEqualToNumber: (NSNumber*)aValue];
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
- (NSUInteger) hash
|
|
{
|
|
return (unsigned)[self doubleValue];
|
|
}
|
|
|
|
- (NSString*) stringValue
|
|
{
|
|
return [self descriptionWithLocale: nil];
|
|
}
|
|
|
|
- (NSString*) descriptionWithLocale: (id)aLocale
|
|
{
|
|
[self subclassResponsibility: _cmd];
|
|
return nil; // Not reached
|
|
}
|
|
|
|
- (NSComparisonResult) compare: (NSNumber*)aNumber
|
|
{
|
|
[self subclassResponsibility: _cmd];
|
|
return 0; // Not reached
|
|
}
|
|
|
|
#define INTEGER_MACRO(encoding,type, ignored, name) \
|
|
- (id) initWith ## name: (type)aValue \
|
|
{\
|
|
DESTROY(self);\
|
|
return [[NSNumberClass numberWith ## name: aValue] retain];\
|
|
}
|
|
|
|
#include "GSNumberTypes.h"
|
|
|
|
- (id) initWithBool: (BOOL)aValue
|
|
{
|
|
DESTROY(self);
|
|
return [(aValue == 0 ? boolN : boolY) retain];\
|
|
}
|
|
|
|
/*
|
|
* Macro for checking whether this value is the same as one of the singleton
|
|
* instances.
|
|
*/
|
|
#define CHECK_SINGLETON(aValue) \
|
|
if (aValue >= -1 && aValue <= 12)\
|
|
{\
|
|
return ReusedInstances[aValue+1];\
|
|
}
|
|
|
|
+ (NSNumber *) numberWithBool: (BOOL)aValue
|
|
{
|
|
if (self != NSNumberClass)
|
|
{
|
|
return [[[self alloc] initWithBytes: (const void *)&aValue
|
|
objCType: @encode(BOOL)] autorelease];
|
|
}
|
|
if (0 == aValue)
|
|
{
|
|
return boolN;
|
|
}
|
|
return boolY;
|
|
}
|
|
|
|
+ (NSNumber *) numberWithChar: (signed char)aValue
|
|
{
|
|
if (self != NSNumberClass)
|
|
{
|
|
return [[[self alloc] initWithBytes: (const void *)&aValue
|
|
objCType: @encode(signed char)] autorelease];
|
|
}
|
|
return [self numberWithInt: aValue];
|
|
}
|
|
|
|
+ (NSNumber *) numberWithUnsignedChar: (unsigned char)aValue
|
|
{
|
|
if (self != NSNumberClass)
|
|
{
|
|
return [[[self alloc] initWithBytes: (const void *)&aValue
|
|
objCType: @encode(unsigned char)] autorelease];
|
|
}
|
|
return [self numberWithInt: aValue];
|
|
}
|
|
|
|
+ (NSNumber *) numberWithShort: (short)aValue
|
|
{
|
|
if (self != NSNumberClass)
|
|
{
|
|
return [[[self alloc] initWithBytes: (const void *)&aValue
|
|
objCType: @encode(short)] autorelease];
|
|
}
|
|
return [self numberWithInt: aValue];
|
|
}
|
|
|
|
+ (NSNumber *) numberWithUnsignedShort: (unsigned short)aValue
|
|
{
|
|
if (self != NSNumberClass)
|
|
{
|
|
return [[[self alloc] initWithBytes: (const void *)&aValue
|
|
objCType: @encode(unsigned short)] autorelease];
|
|
}
|
|
return [self numberWithInt: aValue];
|
|
}
|
|
|
|
+ (NSNumber *) numberWithInt: (int)aValue
|
|
{
|
|
NSIntNumber *n;
|
|
|
|
if (self != NSNumberClass)
|
|
{
|
|
return [[[self alloc] initWithBytes: (const void *)&aValue
|
|
objCType: @encode(int)] autorelease];
|
|
}
|
|
|
|
CHECK_SINGLETON(aValue);
|
|
#ifdef OBJC_SMALL_OBJECT_SHIFT
|
|
if (useSmallInt
|
|
&& (aValue < (INT_MAX>>OBJC_SMALL_OBJECT_SHIFT))
|
|
&& (aValue > -(INT_MAX>>OBJC_SMALL_OBJECT_SHIFT)))
|
|
{
|
|
return (id)((((NSInteger)aValue) << OBJC_SMALL_OBJECT_SHIFT)
|
|
| SMALL_INT_MASK);
|
|
}
|
|
#endif
|
|
n = NSAllocateObject(NSIntNumberClass, 0, 0);
|
|
n->value = aValue;
|
|
return AUTORELEASE(n);
|
|
}
|
|
|
|
+ (NSNumber *) numberWithUnsignedInt: (unsigned int)aValue
|
|
{
|
|
if (self != NSNumberClass)
|
|
{
|
|
return [[[self alloc] initWithBytes: (const void *)&aValue
|
|
objCType: @encode(unsigned int)] autorelease];
|
|
}
|
|
|
|
CHECK_SINGLETON(aValue);
|
|
if (aValue < (unsigned int) INT_MAX)
|
|
{
|
|
return [self numberWithInt: (int)aValue];
|
|
}
|
|
return [self numberWithLongLong: aValue];
|
|
}
|
|
|
|
+ (NSNumber *) numberWithLong: (long)aValue
|
|
{
|
|
if (self != NSNumberClass)
|
|
{
|
|
return [[[self alloc] initWithBytes: (const void *)&aValue
|
|
objCType: @encode(long)] autorelease];
|
|
}
|
|
return [self numberWithLongLong: aValue];
|
|
}
|
|
|
|
+ (NSNumber *) numberWithUnsignedLong: (unsigned long)aValue
|
|
{
|
|
if (self != NSNumberClass)
|
|
{
|
|
return [[[self alloc] initWithBytes: (const void *)&aValue
|
|
objCType: @encode(unsigned long)] autorelease];
|
|
}
|
|
return [self numberWithUnsignedLongLong: aValue];
|
|
}
|
|
|
|
+ (NSNumber *) numberWithLongLong: (long long)aValue
|
|
{
|
|
NSLongLongNumber *n;
|
|
|
|
if (self != NSNumberClass)
|
|
{
|
|
return [[[self alloc] initWithBytes: (const void *)&aValue
|
|
objCType: @encode(long long)] autorelease];
|
|
}
|
|
CHECK_SINGLETON(aValue);
|
|
if (aValue < (long long)INT_MAX && aValue > (long long)INT_MIN)
|
|
{
|
|
return [self numberWithInt: (int) aValue];
|
|
}
|
|
n = NSAllocateObject(NSLongLongNumberClass, 0, 0);
|
|
n->value = aValue;
|
|
return AUTORELEASE(n);
|
|
}
|
|
|
|
+ (NSNumber *) numberWithUnsignedLongLong: (unsigned long long)aValue
|
|
{
|
|
NSUnsignedLongLongNumber *n;
|
|
|
|
if (self != NSNumberClass)
|
|
{
|
|
return [[[self alloc] initWithBytes: (const void *)&aValue
|
|
objCType: @encode(unsigned long long)] autorelease];
|
|
}
|
|
if (aValue < (unsigned long long) LLONG_MAX)
|
|
{
|
|
return [self numberWithLongLong: (long long) aValue];
|
|
}
|
|
n = NSAllocateObject(NSUnsignedLongLongNumberClass, 0, 0);
|
|
n->value = aValue;
|
|
return AUTORELEASE(n);
|
|
}
|
|
|
|
+ (NSNumber *) numberWithFloat: (float)aValue
|
|
{
|
|
NSFloatNumber *n;
|
|
|
|
if (self != NSNumberClass)
|
|
{
|
|
return [[[self alloc] initWithBytes: (const void *)&aValue
|
|
objCType: @encode(float)] autorelease];
|
|
}
|
|
#if OBJC_SMALL_OBJECT_SHIFT == 3
|
|
if (useSmallFloat)
|
|
{
|
|
return boxDouble(aValue, SMALL_FLOAT_MASK);
|
|
}
|
|
#endif
|
|
n = NSAllocateObject(NSFloatNumberClass, 0, 0);
|
|
n->value = aValue;
|
|
return AUTORELEASE(n);
|
|
}
|
|
|
|
+ (NSNumber *) numberWithDouble: (double)aValue
|
|
{
|
|
NSDoubleNumber *n;
|
|
|
|
if (self != NSNumberClass)
|
|
{
|
|
return [[[self alloc] initWithBytes: (const void *)&aValue
|
|
objCType: @encode(double)] autorelease];
|
|
}
|
|
#if OBJC_SMALL_OBJECT_SHIFT == 3
|
|
if (useSmallRepeatingDouble && isSmallRepeatingDouble(aValue))
|
|
{
|
|
return boxDouble(aValue, SMALL_REPEATING_DOUBLE_MASK);
|
|
}
|
|
if (useSmallExtendedDouble && isSmallExtendedDouble(aValue))
|
|
{
|
|
return boxDouble(aValue, SMALL_EXTENDED_DOUBLE_MASK);
|
|
}
|
|
#endif
|
|
n = NSAllocateObject(NSDoubleNumberClass, 0, 0);
|
|
n->value = aValue;
|
|
return AUTORELEASE(n);
|
|
}
|
|
|
|
+ (NSNumber *) numberWithInteger: (NSInteger)aValue
|
|
{
|
|
if (self != NSNumberClass)
|
|
{
|
|
return [[[self alloc] initWithBytes: (const void *)&aValue
|
|
objCType: @encode(NSInteger)] autorelease];
|
|
}
|
|
// Compile time constant; the compiler will remove this conditional
|
|
if (sizeof (NSInteger) == sizeof (int))
|
|
{
|
|
return [self numberWithInt: aValue];
|
|
}
|
|
return [self numberWithLongLong: aValue];
|
|
}
|
|
|
|
+ (NSNumber *) numberWithUnsignedInteger: (NSUInteger)aValue
|
|
{
|
|
if (self != NSNumberClass)
|
|
{
|
|
return [[[self alloc] initWithBytes: (const void *)&aValue
|
|
objCType: @encode(NSUInteger)] autorelease];
|
|
}
|
|
// Compile time constant; the compiler will remove this conditional
|
|
if (sizeof (NSUInteger) == sizeof (unsigned int))
|
|
{
|
|
return [self numberWithUnsignedInt: aValue];
|
|
}
|
|
return [self numberWithUnsignedLongLong: aValue];
|
|
}
|
|
|
|
- (id) initWithBytes: (const void *)
|
|
value objCType: (const char *)type
|
|
{
|
|
switch (type[0])
|
|
{
|
|
case 'c':
|
|
return [self initWithInteger: *(signed char *) value];
|
|
case 'C':
|
|
return [self initWithInteger: *(unsigned char *) value];
|
|
case 's':
|
|
return [self initWithInteger: *(short *) value];
|
|
case 'S':
|
|
return [self initWithInteger: *(unsigned short *) value];
|
|
case 'i':
|
|
return [self initWithInteger: *(int *) value];
|
|
case 'I':
|
|
return [self initWithInteger: *(unsigned int *) value];
|
|
case 'l':
|
|
return [self initWithLong: *(long *) value];
|
|
case 'L':
|
|
return [self initWithUnsignedLong: *(unsigned long *) value];
|
|
case 'q':
|
|
return [self initWithLongLong: *(long long *) value];
|
|
case 'Q':
|
|
return [self initWithUnsignedLongLong: *(unsigned long long *) value];
|
|
case 'f':
|
|
return [self initWithFloat: *(float *) value];
|
|
case 'd':
|
|
return [self initWithDouble: *(double *) value];
|
|
}
|
|
return [super initWithBytes: value objCType: type];
|
|
}
|
|
|
|
- (void *) pointerValue
|
|
{
|
|
return (void *)[self unsignedIntegerValue];
|
|
}
|
|
|
|
- (id) replacementObjectForPortCoder: (NSPortCoder *) encoder
|
|
{
|
|
return self;
|
|
}
|
|
|
|
- (Class) classForCoder
|
|
{
|
|
return NSNumberClass;
|
|
}
|
|
|
|
- (void) encodeWithCoder: (NSCoder *) coder
|
|
{
|
|
const char *type = [self objCType];
|
|
unsigned char charbuf;
|
|
unsigned short shortbuf;
|
|
unsigned int intbuf;
|
|
unsigned long longbuf;
|
|
unsigned long long llongbuf;
|
|
float floatbuf;
|
|
double doublebuf;
|
|
void *buffer;
|
|
|
|
[coder encodeValueOfObjCType: @encode(char) at: type];
|
|
|
|
switch (type[0])
|
|
{
|
|
case 'c':
|
|
case 'C':
|
|
buffer = &charbuf; break;
|
|
case 's':
|
|
case 'S':
|
|
buffer = &shortbuf; break;
|
|
case 'i':
|
|
case 'I':
|
|
buffer = &intbuf; break;
|
|
case 'l':
|
|
case 'L':
|
|
buffer = &longbuf; break;
|
|
case 'q':
|
|
case 'Q':
|
|
buffer = &llongbuf; break;
|
|
case 'f':
|
|
buffer = &floatbuf; break;
|
|
case 'd':
|
|
buffer = &doublebuf; break;
|
|
default:
|
|
[NSException raise: NSInternalInconsistencyException
|
|
format: @"unknown NSNumber type '%s'", type];
|
|
return; // Avoid spurious compiler warning.
|
|
}
|
|
|
|
[self getValue: buffer];
|
|
[coder encodeValueOfObjCType: type at: buffer];
|
|
}
|
|
|
|
- (id) copyWithZone: (NSZone *) aZone
|
|
{
|
|
// OSX just returns the receive with no copy.
|
|
return RETAIN (self);
|
|
}
|
|
|
|
- (id) initWithCoder: (NSCoder *) coder
|
|
{
|
|
char type[2] = { 0 };
|
|
unsigned char charbuf;
|
|
unsigned short shortbuf;
|
|
unsigned int intbuf;
|
|
unsigned long longbuf;
|
|
unsigned long long llongbuf;
|
|
float floatbuf;
|
|
double doublebuf;
|
|
void *buffer;
|
|
|
|
[coder decodeValueOfObjCType: @encode(char) at: type];
|
|
switch (type[0])
|
|
{
|
|
case 'c':
|
|
case 'C':
|
|
buffer = &charbuf; break;
|
|
case 's':
|
|
case 'S':
|
|
buffer = &shortbuf; break;
|
|
case 'i':
|
|
case 'I':
|
|
buffer = &intbuf; break;
|
|
case 'l':
|
|
case 'L':
|
|
buffer = &longbuf; break;
|
|
case 'q':
|
|
case 'Q':
|
|
buffer = &llongbuf; break;
|
|
case 'f':
|
|
buffer = &floatbuf; break;
|
|
case 'd':
|
|
buffer = &doublebuf; break;
|
|
default:
|
|
[NSException raise: NSInternalInconsistencyException
|
|
format: @"unknown NSNumber type '%c'", type[0]];
|
|
return nil; // Avoid spurious compiler warning.
|
|
}
|
|
[coder decodeValueOfObjCType: type at: buffer];
|
|
return [self initWithBytes: buffer objCType: type];
|
|
}
|
|
|
|
- (NSString *) description
|
|
{
|
|
return [self stringValue];
|
|
}
|
|
|
|
/* Return nil for an NSNumber that is allocated and initialized without
|
|
* providing a real value. Yes, this seems weird, but it is actually what
|
|
* happens on OS X.
|
|
*/
|
|
- (id) init
|
|
{
|
|
if (object_getClass(self) != NSNumberClass)
|
|
{
|
|
return [super init];
|
|
}
|
|
DESTROY(self);
|
|
return nil;
|
|
}
|
|
|
|
/* Stop the compiler complaining about unimplemented methods. Throwing an
|
|
* exception here matches OS X behaviour, although they throw an invalid
|
|
* argument exception.
|
|
*/
|
|
#define INTEGER_MACRO(encoding, type, name, ignored) \
|
|
- (type) name ## Value\
|
|
{\
|
|
[self subclassResponsibility: _cmd];\
|
|
return (type)0;\
|
|
}
|
|
|
|
#include "GSNumberTypes.h"
|
|
|
|
- (BOOL) boolValue
|
|
{
|
|
[self subclassResponsibility: _cmd];
|
|
return NO;
|
|
}
|
|
|
|
- (NSDecimal) decimalValue
|
|
{
|
|
NSDecimalNumber *dn;
|
|
NSDecimal decimal;
|
|
|
|
dn = [[NSDecimalNumber alloc] initWithString: [self stringValue]];
|
|
decimal = [dn decimalValue];
|
|
[dn release];
|
|
return decimal;
|
|
}
|
|
|
|
@end
|