Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -86,10 +86,11 @@ OFThread.m \ OFTimer.m \ OFTriple.m \ OFURL.m \ OFURLHandler.m \ + OFValue.m \ OFXMLAttribute.m \ OFXMLCDATA.m \ OFXMLCharacters.m \ OFXMLComment.m \ OFXMLElement.m \ @@ -168,10 +169,11 @@ OFMutableDictionary_hashtable.m \ OFMutableSet_hashtable.m \ OFMutableString_UTF8.m \ OFSet_hashtable.m \ OFString_UTF8.m \ + OFValue_bytes.m \ ${AUTORELEASE_M} \ ${FOUNDATION_COMPAT_M} \ ${INSTANCE_M} SRCS_FILES += OFSettings_INIFile.m \ OFURLHandler_file.m ADDED src/OFValue.h Index: src/OFValue.h ================================================================== --- src/OFValue.h +++ src/OFValue.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018 + * 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 "OFObject.h" + +OF_ASSUME_NONNULL_BEGIN + +/*! + * @class OFValue OFValue.h ObjFW/OFValue.h + * + * @brief A class for storing arbitrary values in an object. + */ +@interface OFValue: OFObject +/*! + * @brief The ObjC type encoding of the value. + */ +@property (nonatomic, readonly) const char *objCType; + +/*! + * @brief Creates a new, autorelease OFValue with the specified bytes of the + * specified type. + * + * @param bytes The bytes containing the value + * @param objCType The ObjC type encoding for the value + * @return A new, autoreleased OFValue + */ ++ (instancetype)valueWithBytes: (const void *)bytes + objCType: (const char *)objCType; + +/*! + * @brief Initializes an already allocated OFValue with the specified bytes of + * the specified type. + * + * @param bytes The bytes containing the value + * @param objCType The ObjC type encoding for the value + * @return An initialized OFValue + */ +- (instancetype)initWithBytes: (const void *)bytes + objCType: (const char *)objCType; + +/*! + * @brief Gets the value. + * + * If the specified size does not match, this raises an + * @ref OFOutOfRangeException. + * + * @param value The buffer to copy the value into + * @param size The size of the value + */ +- (void)getValue: (void *)value + size: (size_t)size; +@end + +OF_ASSUME_NONNULL_END + +#if !defined(NSINTEGER_DEFINED) && !__has_feature(modules) +/* Required for array literals to work */ +@compatibility_alias NSValue OFValue; +#endif ADDED src/OFValue.m Index: src/OFValue.m ================================================================== --- src/OFValue.m +++ src/OFValue.m @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018 + * 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 "OFValue.h" +#import "OFValue_bytes.h" +#import "OFMethodSignature.h" + +#import "OFOutOfMemoryException.h" + +static struct { + Class isa; +} placeholder; + +@interface OFValue_placeholder: OFValue +@end + +@implementation OFValue_placeholder +- (instancetype)initWithBytes: (const void *)bytes + objCType: (const char *)objCType +{ + return (id)[[OFValue_bytes alloc] initWithBytes: bytes + objCType: objCType]; +} +@end + +@implementation OFValue ++ (void)initialize +{ + if (self == [OFValue class]) + placeholder.isa = [OFValue_placeholder class]; +} + ++ (instancetype)alloc +{ + if (self == [OFValue class]) + return (id)&placeholder; + + return [super alloc]; +} + ++ (instancetype)valueWithBytes: (const void *)bytes + objCType: (const char *)objCType +{ + return [[[self alloc] initWithBytes: bytes + objCType: objCType] autorelease]; +} + +- (instancetype)initWithBytes: (const void *)bytes + objCType: (const char *)objCType +{ + OF_INVALID_INIT_METHOD +} + +- (bool)isEqual: (id)object +{ + const char *objCType; + size_t size; + void *buffer, *otherBuffer; + + if (![object isKindOfClass: [OFValue class]]) + return false; + + objCType = [self objCType]; + + if (strcmp([object objCType], objCType) != 0) + return false; + + size = of_sizeof_type_encoding(objCType); + + if ((buffer = malloc(size)) == NULL) + @throw [OFOutOfMemoryException + exceptionWithRequestedSize: size]; + + if ((otherBuffer = malloc(size)) == NULL) + @throw [OFOutOfMemoryException + exceptionWithRequestedSize: size]; + + @try { + [self getValue: buffer + size: size]; + [object getValue: otherBuffer + size: size]; + + return (memcmp(buffer, otherBuffer, size) == 0); + } @finally { + free(buffer); + free(otherBuffer); + } +} + +- (uint32_t)hash +{ + uint32_t hash; + size_t size; + char *buffer; + + size = of_sizeof_type_encoding([self objCType]); + + if ((buffer = malloc(size)) == NULL) + @throw [OFOutOfMemoryException + exceptionWithRequestedSize: size]; + + [self getValue: buffer + size: size]; + + @try { + OF_HASH_INIT(hash); + + for (size_t i = 0; i < size; i++) + OF_HASH_ADD(hash, buffer[i]); + + OF_HASH_FINALIZE(hash); + } @finally { + free(buffer); + } + + return hash; +} + +- (const char *)objCType +{ + OF_UNRECOGNIZED_SELECTOR +} + +- (void)getValue: (void *)value + size: (size_t)size +{ + OF_UNRECOGNIZED_SELECTOR +} +@end ADDED src/OFValue_bytes.h Index: src/OFValue_bytes.h ================================================================== --- src/OFValue_bytes.h +++ src/OFValue_bytes.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018 * 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 "OFValue.h" + +OF_ASSUME_NONNULL_BEGIN + +@interface OFValue_bytes: OFValue +{ + size_t _size; + void *_bytes; + const char *_objCType; +} +@end + +OF_ASSUME_NONNULL_END ADDED src/OFValue_bytes.m Index: src/OFValue_bytes.m ================================================================== --- src/OFValue_bytes.m +++ src/OFValue_bytes.m @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018 + * 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 "OFValue_bytes.h" +#import "OFMethodSignature.h" + +#import "OFOutOfRangeException.h" + +@implementation OFValue_bytes +@synthesize objCType = _objCType; + +- (instancetype)initWithBytes: (const void *)bytes + objCType: (const char *)objCType +{ + self = [super init]; + + @try { + _size = of_sizeof_type_encoding(objCType); + _objCType = objCType; + _bytes = [self allocMemoryWithSize: _size]; + + memcpy(_bytes, bytes, _size); + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)getValue: (void *)value + size: (size_t)size +{ + if (size != _size) + @throw [OFOutOfRangeException exception]; + + memcpy(value, _bytes, _size); +} +@end Index: src/ObjFW.h ================================================================== --- src/ObjFW.h +++ src/ObjFW.h @@ -32,10 +32,11 @@ #import "OFMapTable.h" #import "OFSet.h" #import "OFCountedSet.h" +#import "OFValue.h" #import "OFPair.h" #import "OFTriple.h" #import "OFEnumerator.h" Index: tests/Makefile ================================================================== --- tests/Makefile +++ tests/Makefile @@ -26,10 +26,11 @@ OFObjectTests.m \ OFSetTests.m \ OFStreamTests.m \ OFStringTests.m \ OFURLTests.m \ + OFValueTests.m \ OFXMLElementBuilderTests.m \ OFXMLNodeTests.m \ OFXMLParserTests.m \ PBKDF2Tests.m \ RuntimeTests.m \ ADDED tests/OFValueTests.m Index: tests/OFValueTests.m ================================================================== --- tests/OFValueTests.m +++ tests/OFValueTests.m @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018 + * 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 "OFValue.h" +#import "OFAutoreleasePool.h" + +#import "OFOutOfRangeException.h" + +#import "TestsAppDelegate.h" + +static OFString *module = @"OFValue"; + +@implementation TestsAppDelegate (OFValueTests) +- (void)valueTests +{ + OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; + of_range_t range = of_range(1, 64), range2 = of_range(1, 64); + OFValue *value; + + TEST(@"+[valueWithBytes:objCType:]", + (value = [OFValue valueWithBytes: &range + objCType: @encode(of_range_t)])) + + TEST(@"-[objCType]", strcmp([value objCType], @encode(of_range_t)) == 0) + + range = of_range(OF_NOT_FOUND, 0); + TEST(@"-[getValue:size:]", + R([value getValue: &range + size: sizeof(of_range_t)]) && + memcmp(&range, &range2, sizeof(of_range_t)) == 0) + + EXPECT_EXCEPTION(@"-[getValue:size:] with wrong size throws", + OFOutOfRangeException, + [value getValue: &range + size: sizeof(of_range_t) - 1]) + + [pool drain]; +} +@end Index: tests/TestsAppDelegate.h ================================================================== --- tests/TestsAppDelegate.h +++ tests/TestsAppDelegate.h @@ -218,10 +218,14 @@ @end @interface TestsAppDelegate (OFURLTests) - (void)URLTests; @end + +@interface TestsAppDelegate (OFValueTests) +- (void)valueTests; +@end @interface TestsAppDelegate (OFXMLElementBuilderTests) - (void)XMLElementBuilderTests; @end Index: tests/TestsAppDelegate.m ================================================================== --- tests/TestsAppDelegate.m +++ tests/TestsAppDelegate.m @@ -396,10 +396,11 @@ [self arrayTests]; [self dictionaryTests]; [self listTests]; [self setTests]; [self dateTests]; + [self valueTests]; [self numberTests]; [self streamTests]; #ifdef OF_HAVE_FILES [self MD5HashTests]; [self RIPEMD160HashTests];