Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -188,10 +188,11 @@ OFConcreteDictionary.m \ OFConcreteMutableArray.m \ OFConcreteMutableData.m \ OFConcreteMutableDictionary.m \ OFConcreteMutableSet.m \ + OFConcreteNumber.m \ OFConcreteSet.m \ OFConcreteSubarray.m \ OFConcreteValue.m \ OFEmbeddedIRIHandler.m \ OFHuffmanTree.m \ @@ -204,10 +205,11 @@ OFStrPTime.m \ OFSubarray.m \ OFSubdata.m \ OFTaggedPointerColor.m \ OFTaggedPointerDate.m \ + OFTaggedPointerNumber.m \ OFUTF8String.m \ ${LIBBASES_M} \ ${RUNTIME_AUTORELEASE_M} \ ${RUNTIME_INSTANCE_M} \ ${UNICODE_M} ADDED src/OFConcreteNumber.h Index: src/OFConcreteNumber.h ================================================================== --- src/OFConcreteNumber.h +++ src/OFConcreteNumber.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2008-2023 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#import "OFNumber.h" + +OF_ASSUME_NONNULL_BEGIN + +@interface OFConcreteNumber: OFNumber +{ + union { + double float_; + long long signed_; + unsigned long long unsigned_; + } _value; + const char *_typeEncoding; +} +@end + +OF_ASSUME_NONNULL_END ADDED src/OFConcreteNumber.m Index: src/OFConcreteNumber.m ================================================================== --- src/OFConcreteNumber.m +++ src/OFConcreteNumber.m @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2008-2023 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include + +#import "OFConcreteNumber.h" + +#import "OFInvalidFormatException.h" + +static bool +isUnsigned(OFNumber *number) +{ + switch (*number.objCType) { + case 'B': + case 'C': + case 'S': + case 'I': + case 'L': + case 'Q': + return true; + default: + return false; + } +} + +static bool +isSigned(OFNumber *number) +{ + switch (*number.objCType) { + case 'c': + case 's': + case 'i': + case 'l': + case 'q': + return true; + default: + return false; + } +} + +static bool +isFloat(OFNumber *number) +{ + switch (*number.objCType) { + case 'f': + case 'd': + return true; + default: + return false; + } +} + +@implementation OFConcreteNumber +- (instancetype)initWithBytes: (const void *)bytes + objCType: (const char *)objCType +{ +#define CASE(type, method) \ + if (strcmp(objCType, @encode(type)) == 0) { \ + type value; \ + memcpy(&value, bytes, sizeof(type)); \ + return [self method value]; \ + } + + CASE(bool, initWithBool:) + CASE(signed char, initWithChar:) + CASE(short, initWithShort:) + CASE(int, initWithInt:) + CASE(long, initWithLong:) + CASE(long long, initWithLongLong:) + CASE(unsigned char, initWithUnsignedChar:) + CASE(unsigned short, initWithUnsignedShort:) + CASE(unsigned int, initWithUnsignedInt:) + CASE(unsigned long, initWithUnsignedLong:) + CASE(unsigned long long, initWithUnsignedLongLong:) + CASE(float, initWithFloat:) + CASE(double, initWithDouble:) + + [self release]; + @throw [OFInvalidFormatException exception]; +} + +- (instancetype)initWithBool: (bool)value +{ + self = [super initWithBytes: &value objCType: @encode(bool)]; + + _value.unsigned_ = value; + _typeEncoding = @encode(bool); + + return self; +} + +- (instancetype)initWithChar: (signed char)value +{ + self = [super initWithBytes: &value objCType: @encode(signed char)]; + + _value.signed_ = value; + _typeEncoding = @encode(signed char); + + return self; +} + +- (instancetype)initWithShort: (short)value +{ + self = [super initWithBytes: &value objCType: @encode(short)]; + + _value.signed_ = value; + _typeEncoding = @encode(short); + + return self; +} + +- (instancetype)initWithInt: (int)value +{ + self = [super initWithBytes: &value objCType: @encode(int)]; + + _value.signed_ = value; + _typeEncoding = @encode(int); + + return self; +} + +- (instancetype)initWithLong: (long)value +{ + self = [super initWithBytes: &value objCType: @encode(long)]; + + _value.signed_ = value; + _typeEncoding = @encode(long); + + return self; +} + +- (instancetype)initWithLongLong: (long long)value +{ + self = [super initWithBytes: &value objCType: @encode(long long)]; + + _value.signed_ = value; + _typeEncoding = @encode(long long); + + return self; +} + +- (instancetype)initWithUnsignedChar: (unsigned char)value +{ + self = [super initWithBytes: &value objCType: @encode(unsigned char)]; + + _value.unsigned_ = value; + _typeEncoding = @encode(unsigned long); + + return self; +} + +- (instancetype)initWithUnsignedShort: (unsigned short)value +{ + self = [super initWithBytes: &value objCType: @encode(unsigned short)]; + + _value.unsigned_ = value; + _typeEncoding = @encode(unsigned short); + + return self; +} + +- (instancetype)initWithUnsignedInt: (unsigned int)value +{ + self = [super initWithBytes: &value objCType: @encode(unsigned int)]; + + _value.unsigned_ = value; + _typeEncoding = @encode(unsigned int); + + return self; +} + +- (instancetype)initWithUnsignedLong: (unsigned long)value +{ + self = [super initWithBytes: &value objCType: @encode(unsigned long)]; + + _value.unsigned_ = value; + _typeEncoding = @encode(unsigned long); + + return self; +} + +- (instancetype)initWithUnsignedLongLong: (unsigned long long)value +{ + self = [super initWithBytes: &value + objCType: @encode(unsigned long long)]; + + _value.unsigned_ = value; + _typeEncoding = @encode(unsigned long long); + + return self; +} + +- (instancetype)initWithFloat: (float)value +{ + self = [super initWithBytes: &value objCType: @encode(float)]; + + _value.float_ = value; + _typeEncoding = @encode(float); + + return self; +} + +- (instancetype)initWithDouble: (double)value +{ + self = [super initWithBytes: &value objCType: @encode(double)]; + + _value.float_ = value; + _typeEncoding = @encode(double); + + return self; +} + +- (const char *)objCType +{ + return _typeEncoding; +} + +- (long long)longLongValue +{ + if (isFloat(self)) + return _value.float_; + else if (isSigned(self)) + return _value.signed_; + else if (isUnsigned(self)) + return _value.unsigned_; + else + @throw [OFInvalidFormatException exception]; +} + +- (unsigned long long)unsignedLongLongValue +{ + if (isFloat(self)) + return _value.float_; + else if (isSigned(self)) + return _value.signed_; + else if (isUnsigned(self)) + return _value.unsigned_; + else + @throw [OFInvalidFormatException exception]; +} + +- (double)doubleValue +{ + if (isFloat(self)) + return _value.float_; + else if (isSigned(self)) + return _value.signed_; + else if (isUnsigned(self)) + return _value.unsigned_; + else + @throw [OFInvalidFormatException exception]; +} +@end Index: src/OFNumber.h ================================================================== --- src/OFNumber.h +++ src/OFNumber.h @@ -37,24 +37,12 @@ /** * @class OFNumber OFNumber.h ObjFW/OFNumber.h * * @brief Provides a way to store a number in an object. */ -#ifndef OF_NUMBER_M -OF_SUBCLASSING_RESTRICTED -#endif @interface OFNumber: OFValue -{ - union { - double float_; - long long signed_; - unsigned long long unsigned_; - } _value; - const char *_typeEncoding; -} - /** * @brief The OFNumber as a `bool`. */ @property (readonly, nonatomic) bool boolValue; @@ -121,12 +109,10 @@ /** * @brief The OFNumber as a string. */ @property (readonly, nonatomic) OFString *stringValue; -+ (instancetype)valueWithBytes: (const void *)bytes - objCType: (const char *)objCType OF_UNAVAILABLE; + (instancetype)valueWithPointer: (const void *)pointer OF_UNAVAILABLE; + (instancetype)valueWithNonretainedObject: (id)object OF_UNAVAILABLE; + (instancetype)valueWithRange: (OFRange)range OF_UNAVAILABLE; + (instancetype)valueWithPoint: (OFPoint)point OF_UNAVAILABLE; + (instancetype)valueWithSize: (OFSize)size OF_UNAVAILABLE; @@ -234,14 +220,10 @@ * @param value The `double` value which the OFNumber should contain * @return A new autoreleased OFNumber */ + (instancetype)numberWithDouble: (double)value; -- (instancetype)init OF_UNAVAILABLE; -- (instancetype)initWithBytes: (const void *)bytes - objCType: (const char *)objCType OF_UNAVAILABLE; - /** * @brief Initializes an already allocated OFNumber with the specified `bool`. * * @param value The `bool` value which the OFNumber should contain * @return An initialized OFNumber Index: src/OFNumber.m ================================================================== --- src/OFNumber.m +++ src/OFNumber.m @@ -11,68 +11,47 @@ * Public License, either version 2 or 3, which can be found in the file * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this * file. */ -#define OF_NUMBER_M - #include "config.h" #include #import "OFNumber.h" +#import "OFConcreteNumber.h" #import "OFData.h" #import "OFString.h" +#import "OFTaggedPointerNumber.h" #import "OFInvalidArgumentException.h" #import "OFInvalidFormatException.h" #import "OFOutOfRangeException.h" @interface OFNumber () -+ (instancetype)of_alloc; - (OFString *) of_JSONRepresentationWithOptions: (OFJSONRepresentationOptions)options depth: (size_t)depth; @end @interface OFPlaceholderNumber: OFNumber @end -@interface OFNumberSingleton: OFNumber -@end - -#ifdef OF_OBJFW_RUNTIME -enum Tag { - tagChar, - tagShort, - tagInt, - tagLong, - tagLongLong, - tagUnsignedChar, - tagUnsignedShort, - tagUnsignedInt, - tagUnsignedLong, - tagUnsignedLongLong, -}; -static const uint_fast8_t tagBits = 4; -static const uintptr_t tagMask = 0xF; - -@interface OFTaggedPointerNumber: OFNumberSingleton -@end -#endif +@interface OFConcreteNumberSingleton: OFConcreteNumber +@end static struct { Class isa; } placeholder; -#define SINGLETON(var, sel, val) \ - static OFNumberSingleton *var; \ - \ - static void \ - var##Init(void) \ - { \ - var = [[OFNumberSingleton alloc] sel val]; \ +#define SINGLETON(var, sel, val) \ + static OFConcreteNumberSingleton *var; \ + \ + static void \ + var##Init(void) \ + { \ + var = [[OFConcreteNumberSingleton alloc] sel val]; \ } SINGLETON(falseNumber, initWithBool:, false) SINGLETON(trueNumber, initWithBool:, true) SINGLETON(charZeroNumber, initWithChar:, 0) SINGLETON(shortZeroNumber, initWithShort:, 0) @@ -86,28 +65,19 @@ SINGLETON(unsignedLongLongZeroNumber, initWithUnsignedLongLong:, 0) SINGLETON(floatZeroNumber, initWithFloat:, 0) SINGLETON(doubleZeroNumber, initWithDouble:, 0) #undef SINGLETON -#ifdef OF_OBJFW_RUNTIME -static int numberTag; -#endif - static bool isUnsigned(OFNumber *number) { switch (*number.objCType) { case 'B': - return true; case 'C': - return true; case 'S': - return true; case 'I': - return true; case 'L': - return true; case 'Q': return true; default: return false; } @@ -116,17 +86,13 @@ static bool isSigned(OFNumber *number) { switch (*number.objCType) { case 'c': - return true; case 's': - return true; case 'i': - return true; case 'l': - return true; case 'q': return true; default: return false; } @@ -135,11 +101,10 @@ static bool isFloat(OFNumber *number) { switch (*number.objCType) { case 'f': - return true; case 'd': return true; default: return false; } @@ -168,192 +133,187 @@ if (value == 0) { static OFOnceControl onceControl = OFOnceControlInitValue; OFOnce(&onceControl, charZeroNumberInit); return (id)charZeroNumber; #ifdef OF_OBJFW_RUNTIME - } else if ((unsigned char)value <= (UINTPTR_MAX >> tagBits)) { - id ret = objc_createTaggedPointer(numberTag, - ((uintptr_t)(unsigned char)value << tagBits) | tagChar); + } else if ((unsigned char)value <= + (UINTPTR_MAX >> OFTaggedPointerNumberTagBits)) { + id ret = [OFTaggedPointerNumber numberWithChar: value]; if (ret != nil) return ret; #endif } - return (id)[[OFNumber of_alloc] initWithChar: value]; + return (id)[[OFConcreteNumber alloc] initWithChar: value]; } - (instancetype)initWithShort: (short)value { if (value == 0) { static OFOnceControl onceControl = OFOnceControlInitValue; OFOnce(&onceControl, shortZeroNumberInit); return (id)shortZeroNumber; #ifdef OF_OBJFW_RUNTIME - } else if ((unsigned short)value <= (UINTPTR_MAX >> tagBits)) { - id ret = objc_createTaggedPointer(numberTag, - ((uintptr_t)(unsigned short)value << tagBits) | tagShort); + } else if ((unsigned short)value <= + (UINTPTR_MAX >> OFTaggedPointerNumberTagBits)) { + id ret = [OFTaggedPointerNumber numberWithShort: value]; if (ret != nil) return ret; #endif } - return (id)[[OFNumber of_alloc] initWithShort: value]; + return (id)[[OFConcreteNumber alloc] initWithShort: value]; } - (instancetype)initWithInt: (int)value { if (value == 0) { static OFOnceControl onceControl = OFOnceControlInitValue; OFOnce(&onceControl, intZeroNumberInit); return (id)intZeroNumber; #ifdef OF_OBJFW_RUNTIME - } else if ((unsigned int)value <= (UINTPTR_MAX >> tagBits)) { - id ret = objc_createTaggedPointer(numberTag, - ((uintptr_t)(unsigned int)value << tagBits) | tagInt); + } else if ((unsigned int)value <= + (UINTPTR_MAX >> OFTaggedPointerNumberTagBits)) { + id ret = [OFTaggedPointerNumber numberWithInt: value]; if (ret != nil) return ret; #endif } - return (id)[[OFNumber of_alloc] initWithInt: value]; + return (id)[[OFConcreteNumber alloc] initWithInt: value]; } - (instancetype)initWithLong: (long)value { if (value == 0) { static OFOnceControl onceControl = OFOnceControlInitValue; OFOnce(&onceControl, longZeroNumberInit); return (id)longZeroNumber; #ifdef OF_OBJFW_RUNTIME - } else if ((unsigned long)value <= (UINTPTR_MAX >> tagBits)) { - id ret = objc_createTaggedPointer(numberTag, - ((uintptr_t)(unsigned long)value << tagBits) | tagLong); + } else if ((unsigned long)value <= + (UINTPTR_MAX >> OFTaggedPointerNumberTagBits)) { + id ret = [OFTaggedPointerNumber numberWithLong: value]; if (ret != nil) return ret; #endif } - return (id)[[OFNumber of_alloc] initWithLong: value]; + return (id)[[OFConcreteNumber alloc] initWithLong: value]; } - (instancetype)initWithLongLong: (long long)value { if (value == 0) { static OFOnceControl onceControl = OFOnceControlInitValue; OFOnce(&onceControl, longLongZeroNumberInit); return (id)longLongZeroNumber; #ifdef OF_OBJFW_RUNTIME - } else if ((unsigned long long)value <= (UINTPTR_MAX >> tagBits)) { - id ret = objc_createTaggedPointer(numberTag, - ((uintptr_t)(unsigned long long)value << tagBits) | - tagLongLong); + } else if ((unsigned long long)value <= + (UINTPTR_MAX >> OFTaggedPointerNumberTagBits)) { + id ret = [OFTaggedPointerNumber numberWithLongLong: value]; if (ret != nil) return ret; #endif } - return (id)[[OFNumber of_alloc] initWithLongLong: value]; + return (id)[[OFConcreteNumber alloc] initWithLongLong: value]; } - (instancetype)initWithUnsignedChar: (unsigned char)value { if (value == 0) { static OFOnceControl onceControl = OFOnceControlInitValue; OFOnce(&onceControl, unsignedCharZeroNumberInit); return (id)unsignedCharZeroNumber; #ifdef OF_OBJFW_RUNTIME - } else if (value <= (UINTPTR_MAX >> tagBits)) { - id ret = objc_createTaggedPointer(numberTag, - ((uintptr_t)value << tagBits) | tagUnsignedChar); + } else if (value <= (UINTPTR_MAX >> OFTaggedPointerNumberTagBits)) { + id ret = [OFTaggedPointerNumber numberWithUnsignedChar: value]; if (ret != nil) return ret; #endif } - return (id)[[OFNumber of_alloc] initWithUnsignedChar: value]; + return (id)[[OFConcreteNumber alloc] initWithUnsignedChar: value]; } - (instancetype)initWithUnsignedShort: (unsigned short)value { if (value == 0) { static OFOnceControl onceControl = OFOnceControlInitValue; OFOnce(&onceControl, unsignedShortZeroNumberInit); return (id)unsignedShortZeroNumber; #ifdef OF_OBJFW_RUNTIME - } else if (value <= (UINTPTR_MAX >> tagBits)) { - id ret = objc_createTaggedPointer(numberTag, - ((uintptr_t)value << tagBits) | tagUnsignedShort); + } else if (value <= (UINTPTR_MAX >> OFTaggedPointerNumberTagBits)) { + id ret = [OFTaggedPointerNumber numberWithUnsignedShort: value]; if (ret != nil) return ret; #endif } - return (id)[[OFNumber of_alloc] initWithUnsignedShort: value]; + return (id)[[OFConcreteNumber alloc] initWithUnsignedShort: value]; } - (instancetype)initWithUnsignedInt: (unsigned int)value { if (value == 0) { static OFOnceControl onceControl = OFOnceControlInitValue; OFOnce(&onceControl, unsignedIntZeroNumberInit); return (id)unsignedIntZeroNumber; #ifdef OF_OBJFW_RUNTIME - } else if (value <= (UINTPTR_MAX >> tagBits)) { - id ret = objc_createTaggedPointer(numberTag, - ((uintptr_t)value << tagBits) | tagUnsignedInt); + } else if (value <= (UINTPTR_MAX >> OFTaggedPointerNumberTagBits)) { + id ret = [OFTaggedPointerNumber numberWithUnsignedInt: value]; if (ret != nil) return ret; #endif } - return (id)[[OFNumber of_alloc] initWithUnsignedInt: value]; + return (id)[[OFConcreteNumber alloc] initWithUnsignedInt: value]; } - (instancetype)initWithUnsignedLong: (unsigned long)value { if (value == 0) { static OFOnceControl onceControl = OFOnceControlInitValue; OFOnce(&onceControl, unsignedLongZeroNumberInit); return (id)unsignedLongZeroNumber; #ifdef OF_OBJFW_RUNTIME - } else if (value <= (UINTPTR_MAX >> tagBits)) { - id ret = objc_createTaggedPointer(numberTag, - ((uintptr_t)value << tagBits) | tagUnsignedLong); + } else if (value <= (UINTPTR_MAX >> OFTaggedPointerNumberTagBits)) { + id ret = [OFTaggedPointerNumber numberWithUnsignedLong: value]; if (ret != nil) return ret; #endif } - return (id)[[OFNumber of_alloc] initWithUnsignedLong: value]; + return (id)[[OFConcreteNumber alloc] initWithUnsignedLong: value]; } - (instancetype)initWithUnsignedLongLong: (unsigned long long)value { if (value == 0) { static OFOnceControl onceControl = OFOnceControlInitValue; OFOnce(&onceControl, unsignedLongLongZeroNumberInit); return (id)unsignedLongLongZeroNumber; #ifdef OF_OBJFW_RUNTIME - } else if (value <= (UINTPTR_MAX >> tagBits)) { - id ret = objc_createTaggedPointer(numberTag, - ((uintptr_t)value << tagBits) | tagUnsignedLongLong); + } else if (value <= (UINTPTR_MAX >> OFTaggedPointerNumberTagBits)) { + id ret = [OFTaggedPointerNumber + numberWithUnsignedLongLong: value]; if (ret != nil) return ret; #endif } - return (id)[[OFNumber of_alloc] initWithUnsignedLongLong: value]; + return (id)[[OFConcreteNumber alloc] initWithUnsignedLongLong: value]; } - (instancetype)initWithFloat: (float)value { if (value == 0) { @@ -360,11 +320,11 @@ static OFOnceControl onceControl = OFOnceControlInitValue; OFOnce(&onceControl, floatZeroNumberInit); return (id)floatZeroNumber; } - return (id)[[OFNumber of_alloc] initWithFloat: value]; + return (id)[[OFConcreteNumber alloc] initWithFloat: value]; } - (instancetype)initWithDouble: (double)value { if (value == 0) { @@ -371,117 +331,28 @@ static OFOnceControl onceControl = OFOnceControlInitValue; OFOnce(&onceControl, doubleZeroNumberInit); return (id)doubleZeroNumber; } - return (id)[[OFNumber of_alloc] initWithDouble: value]; + return (id)[[OFConcreteNumber alloc] initWithDouble: value]; } #ifdef __clang__ # pragma clang diagnostic pop #endif OF_SINGLETON_METHODS @end -@implementation OFNumberSingleton +@implementation OFConcreteNumberSingleton OF_SINGLETON_METHODS @end -#ifdef OF_OBJFW_RUNTIME -@implementation OFTaggedPointerNumber -- (const char *)objCType -{ - uintptr_t value = object_getTaggedPointerValue(self); - - switch (value & tagMask) { - case tagChar: - return @encode(signed char); - case tagShort: - return @encode(short); - case tagInt: - return @encode(int); - case tagLong: - return @encode(long); - case tagLongLong: - return @encode(long long); - case tagUnsignedChar: - return @encode(unsigned char); - case tagUnsignedShort: - return @encode(unsigned short); - case tagUnsignedInt: - return @encode(unsigned int); - case tagUnsignedLong: - return @encode(unsigned long); - case tagUnsignedLongLong: - return @encode(unsigned long long); - default: - @throw [OFInvalidArgumentException exception]; - } -} - -# define RETURN_VALUE \ - uintptr_t value = object_getTaggedPointerValue(self); \ - \ - switch (value & tagMask) { \ - case tagChar: \ - return (signed char)(unsigned char)(value >> tagBits); \ - case tagShort: \ - return (short)(unsigned short)(value >> tagBits); \ - case tagInt: \ - return (int)(unsigned int)(value >> tagBits); \ - case tagLong: \ - return (long)(unsigned long)(value >> tagBits); \ - case tagLongLong: \ - return (long long)(unsigned long long)(value >> tagBits); \ - case tagUnsignedChar: \ - return (unsigned char)(value >> tagBits); \ - case tagUnsignedShort: \ - return (unsigned short)(value >> tagBits); \ - case tagUnsignedInt: \ - return (unsigned int)(value >> tagBits); \ - case tagUnsignedLong: \ - return (unsigned long)(value >> tagBits); \ - case tagUnsignedLongLong: \ - return (unsigned long long)(value >> tagBits); \ - default: \ - @throw [OFInvalidArgumentException exception]; \ - } -- (long long)longLongValue -{ - RETURN_VALUE -} - -- (unsigned long long)unsignedLongLongValue -{ - RETURN_VALUE -} - -- (double)doubleValue -{ - RETURN_VALUE -} -@end -# undef RETURN_VALUE -#endif - @implementation OFNumber + (void)initialize { - if (self != [OFNumber class]) - return; - - object_setClass((id)&placeholder, [OFPlaceholderNumber class]); - -#ifdef OF_OBJFW_RUNTIME - numberTag = - objc_registerTaggedPointerClass([OFTaggedPointerNumber class]); -#endif -} - -+ (instancetype)of_alloc -{ - return [super alloc]; + if (self == [OFNumber class]) + object_setClass((id)&placeholder, [OFPlaceholderNumber class]); } + (instancetype)alloc { if (self == [OFNumber class]) @@ -488,16 +359,10 @@ return (id)&placeholder; return [super alloc]; } -+ (instancetype)valueWithBytes: (const void *)bytes - objCType: (const char *)objCType -{ - OF_UNRECOGNIZED_SELECTOR -} - + (instancetype)valueWithPointer: (const void *)pointer { OF_UNRECOGNIZED_SELECTOR } @@ -589,223 +454,89 @@ + (instancetype)numberWithDouble: (double)value { return [[[self alloc] initWithDouble: value] autorelease]; } -- (instancetype)init -{ - OF_INVALID_INIT_METHOD -} - -- (instancetype)initWithBytes: (const void *)bytes - objCType: (const char *)objCType -{ - OF_INVALID_INIT_METHOD -} - - (instancetype)initWithBool: (bool)value { - self = [super initWithBytes: &value objCType: @encode(bool)]; - - _value.unsigned_ = value; - _typeEncoding = @encode(bool); - - return self; + return [self initWithBytes: &value objCType: @encode(bool)]; } - (instancetype)initWithChar: (signed char)value { - self = [super initWithBytes: &value objCType: @encode(signed char)]; - - _value.signed_ = value; - _typeEncoding = @encode(signed char); - - return self; + return [self initWithBytes: &value objCType: @encode(signed char)]; } - (instancetype)initWithShort: (short)value { - self = [super initWithBytes: &value objCType: @encode(short)]; - - _value.signed_ = value; - _typeEncoding = @encode(short); - - return self; + return [self initWithBytes: &value objCType: @encode(short)]; } - (instancetype)initWithInt: (int)value { - self = [super initWithBytes: &value objCType: @encode(int)]; - - _value.signed_ = value; - _typeEncoding = @encode(int); - - return self; + return [self initWithBytes: &value objCType: @encode(int)]; } - (instancetype)initWithLong: (long)value { - self = [super initWithBytes: &value objCType: @encode(long)]; - - _value.signed_ = value; - _typeEncoding = @encode(long); - - return self; + return [self initWithBytes: &value objCType: @encode(long)]; } - (instancetype)initWithLongLong: (long long)value { - self = [super initWithBytes: &value objCType: @encode(long long)]; - - _value.signed_ = value; - _typeEncoding = @encode(long long); - - return self; + return [self initWithBytes: &value objCType: @encode(long long)]; } - (instancetype)initWithUnsignedChar: (unsigned char)value { - self = [super initWithBytes: &value objCType: @encode(unsigned char)]; - - _value.unsigned_ = value; - _typeEncoding = @encode(unsigned char); - - return self; + return [self initWithBytes: &value objCType: @encode(unsigned char)]; } - (instancetype)initWithUnsignedShort: (unsigned short)value { - self = [super initWithBytes: &value objCType: @encode(unsigned short)]; - - _value.unsigned_ = value; - _typeEncoding = @encode(unsigned short); - - return self; + return [self initWithBytes: &value objCType: @encode(unsigned short)]; } - (instancetype)initWithUnsignedInt: (unsigned int)value { - self = [super initWithBytes: &value objCType: @encode(unsigned int)]; - - _value.unsigned_ = value; - _typeEncoding = @encode(unsigned int); - - return self; + return [self initWithBytes: &value objCType: @encode(unsigned int)]; } - (instancetype)initWithUnsignedLong: (unsigned long)value { - self = [super initWithBytes: &value objCType: @encode(unsigned long)]; - - _value.unsigned_ = value; - _typeEncoding = @encode(unsigned long); - - return self; + return [self initWithBytes: &value objCType: @encode(unsigned long)]; } - (instancetype)initWithUnsignedLongLong: (unsigned long long)value { - self = [super initWithBytes: &value - objCType: @encode(unsigned long long)]; - - _value.unsigned_ = value; - _typeEncoding = @encode(unsigned long long); - - return self; + return [self initWithBytes: &value + objCType: @encode(unsigned long long)]; } - (instancetype)initWithFloat: (float)value { - self = [super initWithBytes: &value objCType: @encode(float)]; - - _value.float_ = value; - _typeEncoding = @encode(float); - - return self; + return [self initWithBytes: &value objCType: @encode(float)]; } - (instancetype)initWithDouble: (double)value { - self = [super initWithBytes: &value objCType: @encode(double)]; - - _value.float_ = value; - _typeEncoding = @encode(double); - - return self; -} - -- (const char *)objCType -{ - return _typeEncoding; -} - -- (void)getValue: (void *)value size: (size_t)size -{ - switch (*self.objCType) { -#define CASE(enc, type, property) \ - case enc: { \ - type tmp = (type)self.property; \ - \ - if (size != sizeof(type)) \ - @throw [OFOutOfRangeException exception]; \ - \ - memcpy(value, &tmp, size); \ - break; \ - } - CASE('B', bool, unsignedLongLongValue) - CASE('c', signed char, longLongValue) - CASE('s', short, longLongValue) - CASE('i', int, longLongValue) - CASE('l', long, longLongValue) - CASE('q', long long, longLongValue) - CASE('C', unsigned char, unsignedLongLongValue) - CASE('S', unsigned short, unsignedLongLongValue) - CASE('I', unsigned int, unsignedLongLongValue) - CASE('L', unsigned long, unsignedLongLongValue) - CASE('Q', unsigned long long, unsignedLongLongValue) - CASE('f', float, doubleValue) - CASE('d', double, doubleValue) -#undef CASE - default: - @throw [OFInvalidFormatException exception]; - } + return [self initWithBytes: &value objCType: @encode(double)]; } - (long long)longLongValue { - if (isFloat(self)) - return _value.float_; - else if (isSigned(self)) - return _value.signed_; - else if (isUnsigned(self)) - return _value.unsigned_; - else - @throw [OFInvalidFormatException exception]; + OF_UNRECOGNIZED_SELECTOR } - (unsigned long long)unsignedLongLongValue { - if (isFloat(self)) - return _value.float_; - else if (isSigned(self)) - return _value.signed_; - else if (isUnsigned(self)) - return _value.unsigned_; - else - @throw [OFInvalidFormatException exception]; + OF_UNRECOGNIZED_SELECTOR } - (double)doubleValue { - if (isFloat(self)) - return _value.float_; - else if (isSigned(self)) - return _value.signed_; - else if (isUnsigned(self)) - return _value.unsigned_; - else - @throw [OFInvalidFormatException exception]; + OF_UNRECOGNIZED_SELECTOR } - (bool)boolValue { return (bool)self.unsignedLongLongValue; @@ -965,11 +696,11 @@ return [self stringValue]; } - (OFString *)stringValue { - if (*self.objCType == 'B') + if (self.objCType[0] == 'B' && self.objCType[1] == '\0') return (self.boolValue ? @"true" : @"false"); if (isFloat(self)) return [OFString stringWithFormat: @"%g", self.doubleValue]; if (isSigned(self)) return [OFString stringWithFormat: @"%lld", self.longLongValue]; @@ -995,11 +726,11 @@ of_JSONRepresentationWithOptions: (OFJSONRepresentationOptions)options depth: (size_t)depth { double doubleValue; - if (*self.objCType == 'B') + if (self.objCType[0] == 'B' && self.objCType[1] == '\0') return (self.boolValue ? @"true" : @"false"); doubleValue = self.doubleValue; if (isinf(doubleValue)) { if (options & OFJSONRepresentationOptionJSON5) { @@ -1016,10 +747,13 @@ - (OFData *)messagePackRepresentation { OFMutableData *data; const char *typeEncoding = self.objCType; + + if (typeEncoding[0] == '\0' || typeEncoding[1] != '\0') + @throw [OFInvalidFormatException exception]; if (*typeEncoding == 'B') { uint8_t type = (self.boolValue ? 0xC3 : 0xC2); data = [OFMutableData dataWithItems: &type count: 1]; } else if (*typeEncoding == 'f') { ADDED src/OFTaggedPointerNumber.h Index: src/OFTaggedPointerNumber.h ================================================================== --- src/OFTaggedPointerNumber.h +++ src/OFTaggedPointerNumber.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2008-2023 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#import "OFNumber.h" + +OF_ASSUME_NONNULL_BEGIN + +#ifdef OF_OBJFW_RUNTIME +static const uint_fast8_t OFTaggedPointerNumberTagBits = 4; + +@interface OFTaggedPointerNumber: OFNumber +@end +#endif + +OF_ASSUME_NONNULL_END ADDED src/OFTaggedPointerNumber.m Index: src/OFTaggedPointerNumber.m ================================================================== --- src/OFTaggedPointerNumber.m +++ src/OFTaggedPointerNumber.m @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2008-2023 Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "OFTaggedPointerNumber.h" + +#import "OFInvalidFormatException.h" + +#ifdef OF_OBJFW_RUNTIME +enum Tag { + tagChar, + tagShort, + tagInt, + tagLong, + tagLongLong, + tagUnsignedChar, + tagUnsignedShort, + tagUnsignedInt, + tagUnsignedLong, + tagUnsignedLongLong, +}; +static const uintptr_t tagMask = (1 << OFTaggedPointerNumberTagBits) - 1; +static int numberTag; + +@implementation OFTaggedPointerNumber ++ (void)initialize +{ + if (self == [OFTaggedPointerNumber class]) + numberTag = objc_registerTaggedPointerClass(self); +} + ++ (OFTaggedPointerNumber *)numberWithChar: (signed char)value +{ + return objc_createTaggedPointer(numberTag, + ((uintptr_t)(unsigned char)value << OFTaggedPointerNumberTagBits) | + tagChar); +} + ++ (OFTaggedPointerNumber *)numberWithShort: (short)value +{ + return objc_createTaggedPointer(numberTag, + ((uintptr_t)(unsigned short)value << OFTaggedPointerNumberTagBits) | + tagShort); +} + ++ (OFTaggedPointerNumber *)numberWithInt: (int)value +{ + return objc_createTaggedPointer(numberTag, + ((uintptr_t)(unsigned int)value << OFTaggedPointerNumberTagBits) | + tagInt); +} + ++ (OFTaggedPointerNumber *)numberWithLong: (long)value +{ + return objc_createTaggedPointer(numberTag, + ((uintptr_t)(unsigned long)value << OFTaggedPointerNumberTagBits) | + tagLong); +} + ++ (OFTaggedPointerNumber *)numberWithLongLong: (long long)value +{ + return objc_createTaggedPointer(numberTag, + ((uintptr_t)(unsigned long long)value << + OFTaggedPointerNumberTagBits) | tagLongLong); +} + ++ (OFTaggedPointerNumber *)numberWithUnsignedChar: (unsigned char)value +{ + return objc_createTaggedPointer(numberTag, + ((uintptr_t)value << OFTaggedPointerNumberTagBits) | + tagUnsignedChar); +} + ++ (OFTaggedPointerNumber *)numberWithUnsignedShort: (unsigned short)value +{ + return objc_createTaggedPointer(numberTag, + ((uintptr_t)value << OFTaggedPointerNumberTagBits) | + tagUnsignedShort); +} + ++ (OFTaggedPointerNumber *)numberWithUnsignedInt: (unsigned int)value +{ + return objc_createTaggedPointer(numberTag, + ((uintptr_t)value << OFTaggedPointerNumberTagBits) | + tagUnsignedInt); +} + ++ (OFTaggedPointerNumber *)numberWithUnsignedLong: (unsigned long)value +{ + return objc_createTaggedPointer(numberTag, + ((uintptr_t)value << OFTaggedPointerNumberTagBits) | + tagUnsignedLong); +} + ++ (OFTaggedPointerNumber *)numberWithUnsignedLongLong: (unsigned long long)value +{ + return objc_createTaggedPointer(numberTag, + ((uintptr_t)value << OFTaggedPointerNumberTagBits) | + tagUnsignedLongLong); +} + +- (const char *)objCType +{ + uintptr_t value = object_getTaggedPointerValue(self); + + switch (value & tagMask) { + case tagChar: + return @encode(signed char); + case tagShort: + return @encode(short); + case tagInt: + return @encode(int); + case tagLong: + return @encode(long); + case tagLongLong: + return @encode(long long); + case tagUnsignedChar: + return @encode(unsigned char); + case tagUnsignedShort: + return @encode(unsigned short); + case tagUnsignedInt: + return @encode(unsigned int); + case tagUnsignedLong: + return @encode(unsigned long); + case tagUnsignedLongLong: + return @encode(unsigned long long); + default: + @throw [OFInvalidFormatException exception]; + } +} + +# define RETURN_VALUE \ + uintptr_t value = object_getTaggedPointerValue(self); \ + \ + switch (value & tagMask) { \ + case tagChar: \ + return (signed char)(unsigned char) \ + (value >> OFTaggedPointerNumberTagBits); \ + case tagShort: \ + return (short)(unsigned short) \ + (value >> OFTaggedPointerNumberTagBits); \ + case tagInt: \ + return (int)(unsigned int) \ + (value >> OFTaggedPointerNumberTagBits); \ + case tagLong: \ + return (long)(unsigned long) \ + (value >> OFTaggedPointerNumberTagBits); \ + case tagLongLong: \ + return (long long)(unsigned long long) \ + (value >> OFTaggedPointerNumberTagBits); \ + case tagUnsignedChar: \ + return (unsigned char) \ + (value >> OFTaggedPointerNumberTagBits); \ + case tagUnsignedShort: \ + return (unsigned short) \ + (value >> OFTaggedPointerNumberTagBits); \ + case tagUnsignedInt: \ + return (unsigned int) \ + (value >> OFTaggedPointerNumberTagBits); \ + case tagUnsignedLong: \ + return (unsigned long) \ + (value >> OFTaggedPointerNumberTagBits); \ + case tagUnsignedLongLong: \ + return (unsigned long long) \ + (value >> OFTaggedPointerNumberTagBits); \ + default: \ + @throw [OFInvalidFormatException exception]; \ + } +- (long long)longLongValue +{ + RETURN_VALUE +} + +- (unsigned long long)unsignedLongLongValue +{ + RETURN_VALUE +} + +- (double)doubleValue +{ + RETURN_VALUE +} +# undef RETURN_VALUE + +OF_SINGLETON_METHODS +@end +#endif