Index: ObjFW.xcodeproj/project.pbxproj ================================================================== --- ObjFW.xcodeproj/project.pbxproj +++ ObjFW.xcodeproj/project.pbxproj @@ -266,10 +266,12 @@ 4B55A115133AC24600B58A93 /* OFReadOrWriteFailedException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B55A10F133AC24500B58A93 /* OFReadOrWriteFailedException.m */; }; 4B55A116133AC24600B58A93 /* OFWriteFailedException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B55A110133AC24500B58A93 /* OFWriteFailedException.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B55A117133AC24600B58A93 /* OFWriteFailedException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B55A111133AC24600B58A93 /* OFWriteFailedException.m */; }; 4B5CF8F914940BD2007AA324 /* OFString+JSONValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B5CF8F614940BD2007AA324 /* OFString+JSONValue.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B5CF8FA14940BD2007AA324 /* OFString+JSONValue.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B5CF8F714940BD2007AA324 /* OFString+JSONValue.m */; }; + 4B6330ED16D67C4400187B8E /* OFDataArray+BinaryPackValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B6330EB16D67C4400187B8E /* OFDataArray+BinaryPackValue.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4B6330EE16D67C4400187B8E /* OFDataArray+BinaryPackValue.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B6330EC16D67C4400187B8E /* OFDataArray+BinaryPackValue.m */; }; 4B64D6EF1425381E007BDFB1 /* OFStreamObserver_poll.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B64D6EB1425381E007BDFB1 /* OFStreamObserver_poll.h */; }; 4B64D6F01425381E007BDFB1 /* OFStreamObserver_poll.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B64D6EC1425381E007BDFB1 /* OFStreamObserver_poll.m */; }; 4B64D6F11425381E007BDFB1 /* OFStreamObserver_select.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B64D6ED1425381E007BDFB1 /* OFStreamObserver_select.h */; }; 4B64D6F21425381E007BDFB1 /* OFStreamObserver_select.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B64D6EE1425381E007BDFB1 /* OFStreamObserver_select.m */; }; 4B6743F1163C384A00EB1E59 /* OFLockFailedException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B6743EB163C384A00EB1E59 /* OFLockFailedException.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -608,10 +610,12 @@ 4B55A10F133AC24500B58A93 /* OFReadOrWriteFailedException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFReadOrWriteFailedException.m; path = src/exceptions/OFReadOrWriteFailedException.m; sourceTree = ""; }; 4B55A110133AC24500B58A93 /* OFWriteFailedException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFWriteFailedException.h; path = src/exceptions/OFWriteFailedException.h; sourceTree = ""; }; 4B55A111133AC24600B58A93 /* OFWriteFailedException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFWriteFailedException.m; path = src/exceptions/OFWriteFailedException.m; sourceTree = ""; }; 4B5CF8F614940BD2007AA324 /* OFString+JSONValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OFString+JSONValue.h"; path = "src/OFString+JSONValue.h"; sourceTree = ""; }; 4B5CF8F714940BD2007AA324 /* OFString+JSONValue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "OFString+JSONValue.m"; path = "src/OFString+JSONValue.m"; sourceTree = ""; }; + 4B6330EB16D67C4400187B8E /* OFDataArray+BinaryPackValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "OFDataArray+BinaryPackValue.h"; path = "src/OFDataArray+BinaryPackValue.h"; sourceTree = ""; }; + 4B6330EC16D67C4400187B8E /* OFDataArray+BinaryPackValue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "OFDataArray+BinaryPackValue.m"; path = "src/OFDataArray+BinaryPackValue.m"; sourceTree = ""; }; 4B64D6EB1425381E007BDFB1 /* OFStreamObserver_poll.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFStreamObserver_poll.h; path = src/OFStreamObserver_poll.h; sourceTree = ""; }; 4B64D6EC1425381E007BDFB1 /* OFStreamObserver_poll.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFStreamObserver_poll.m; path = src/OFStreamObserver_poll.m; sourceTree = ""; }; 4B64D6ED1425381E007BDFB1 /* OFStreamObserver_select.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFStreamObserver_select.h; path = src/OFStreamObserver_select.h; sourceTree = ""; }; 4B64D6EE1425381E007BDFB1 /* OFStreamObserver_select.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFStreamObserver_select.m; path = src/OFStreamObserver_select.m; sourceTree = ""; }; 4B6743EB163C384A00EB1E59 /* OFLockFailedException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFLockFailedException.h; path = src/exceptions/OFLockFailedException.h; sourceTree = ""; }; @@ -1080,10 +1084,12 @@ 4B45355213DCFE1E0037AB4D /* OFCountedSet.m */, 4BA85BC4140ECCE800E91D51 /* OFCountedSet_hashtable.h */, 4BA85BC5140ECCE800E91D51 /* OFCountedSet_hashtable.m */, 4B6799601099E7C50041064A /* OFDataArray.h */, 4B6799611099E7C50041064A /* OFDataArray.m */, + 4B6330EB16D67C4400187B8E /* OFDataArray+BinaryPackValue.h */, + 4B6330EC16D67C4400187B8E /* OFDataArray+BinaryPackValue.m */, 4BE17AD912FD746D002CEB0B /* OFDataArray+Hashing.h */, 4BE17ADA12FD746D002CEB0B /* OFDataArray+Hashing.m */, 4BE5F0D912DF4225005C7A0C /* OFDate.h */, 4BE5F0DA12DF4225005C7A0C /* OFDate.m */, 4B6799621099E7C50041064A /* OFDictionary.h */, @@ -1364,10 +1370,11 @@ 4B3D23C01337FC8300DD29B8 /* OFCollection.h in Headers */, 4B674400163C395900EB1E59 /* OFCondition.h in Headers */, 4B3D23C11337FC8300DD29B8 /* OFConstantString.h in Headers */, 4B45355313DCFE1E0037AB4D /* OFCountedSet.h in Headers */, 4B3D23C21337FC8300DD29B8 /* OFDataArray.h in Headers */, + 4B6330ED16D67C4400187B8E /* OFDataArray+BinaryPackValue.h in Headers */, 4B3D23C31337FC8300DD29B8 /* OFDataArray+Hashing.h in Headers */, 4B3D23C41337FC8300DD29B8 /* OFDate.h in Headers */, 4B3D23C51337FCB000DD29B8 /* OFDictionary.h in Headers */, 4B3D23C61337FCB000DD29B8 /* OFEnumerator.h in Headers */, 4B17FF74133A2AAB003E6DCD /* OFException.h in Headers */, @@ -1713,10 +1720,11 @@ 4B674401163C395900EB1E59 /* OFCondition.m in Sources */, 4B3D238F1337FC0D00DD29B8 /* OFConstantString.m in Sources */, 4B45355413DCFE1E0037AB4D /* OFCountedSet.m in Sources */, 4BA85BCB140ECCE800E91D51 /* OFCountedSet_hashtable.m in Sources */, 4B3D23901337FC0D00DD29B8 /* OFDataArray.m in Sources */, + 4B6330EE16D67C4400187B8E /* OFDataArray+BinaryPackValue.m in Sources */, 4B3D23911337FC0D00DD29B8 /* OFDataArray+Hashing.m in Sources */, 4B3D23921337FC0D00DD29B8 /* OFDate.m in Sources */, 4B3D23931337FC0D00DD29B8 /* OFDictionary.m in Sources */, 4B2B3E80140D430500EC2F7C /* OFDictionary_hashtable.m in Sources */, 4B3D23941337FC0D00DD29B8 /* OFEnumerator.m in Sources */, Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -13,10 +13,11 @@ OFAutoreleasePool.m \ OFBlock.m \ OFConstantString.m \ OFCountedSet.m \ OFDataArray.m \ + OFDataArray+BinaryPackValue.m \ OFDataArray+Hashing.m \ OFDate.m \ OFDictionary.m \ OFEnumerator.m \ OFFile.m \ ADDED src/OFDataArray+BinaryPackValue.h Index: src/OFDataArray+BinaryPackValue.h ================================================================== --- src/OFDataArray+BinaryPackValue.h +++ src/OFDataArray+BinaryPackValue.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 + * 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 "OFDataArray.h" + +#ifdef __cplusplus +extern "C" { +#endif +extern int _OFDataArray_BinaryPackValue_reference; +#ifdef __cplusplus +} +#endif + +@interface OFDataArray (BinaryPackValue) +- (id)binaryPackValue; +@end ADDED src/OFDataArray+BinaryPackValue.m Index: src/OFDataArray+BinaryPackValue.m ================================================================== --- src/OFDataArray+BinaryPackValue.m +++ src/OFDataArray+BinaryPackValue.m @@ -0,0 +1,396 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 + * 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 "OFDataArray+BinaryPackValue.h" +#import "OFNumber.h" +#import "OFNull.h" +#import "OFDataArray.h" +#import "OFString.h" +#import "OFArray.h" +#import "OFDictionary.h" + +#import "OFInvalidFormatException.h" + +#import "autorelease.h" +#import "macros.h" + +int _OFDataArray_BinaryPackValue_reference; + +static size_t parse_object(const uint8_t*, size_t, id*); + +static uint16_t +read_uint16(const uint8_t *buffer) +{ + return ((uint16_t)buffer[0] << 8) | buffer[1]; +} + +static uint32_t +read_uint32(const uint8_t *buffer) +{ + return ((uint32_t)buffer[0] << 24) | ((uint32_t)buffer[1] << 16) | + ((uint32_t)buffer[2] << 8) | buffer[3]; +} + +static uint64_t +read_uint64(const uint8_t *buffer) +{ + return ((uint64_t)buffer[0] << 56) | ((uint64_t)buffer[1] << 48) | + ((uint64_t)buffer[2] << 40) | ((uint64_t)buffer[3] << 32) | + ((uint64_t)buffer[4] << 24) | ((uint64_t)buffer[5] << 16) | + ((uint64_t)buffer[6] << 8) | buffer[7]; +} + +static size_t +parse_array(const uint8_t *buffer, size_t length, id *object, size_t count) +{ + void *pool; + size_t i, pos; + + /* + * Don't use capacity! For data and strings, this is safe, as we can + * check if we still have enough bytes left. For an array however, we + * can't know this, as every child can be more than one byte. + */ + *object = [OFMutableArray array]; + pos = 0; + + for (i = 0; i < count; i++) { + id child; + size_t childLength; + + pool = objc_autoreleasePoolPush(); + + childLength = parse_object(buffer + pos, length - pos, &child); + if (childLength == 0 || child == nil) { + objc_autoreleasePoolPop(pool); + + *object = nil; + return 0; + } + pos += childLength; + + [*object addObject: child]; + + objc_autoreleasePoolPop(pool); + } + + return pos; +} + +static size_t +parse_table(const uint8_t *buffer, size_t length, id *object, size_t count) +{ + void *pool; + size_t i, pos; + + /* + * Don't use capacity! For data and strings, this is safe, as we can + * check if we still have enough bytes left. For a dictionary however, + * we can't know this, as every key / value can be more than one byte. + */ + *object = [OFMutableDictionary dictionary]; + pos = 0; + + for (i = 0; i < count; i++) { + id key, value; + size_t keyLength, valueLength; + + pool = objc_autoreleasePoolPush(); + + keyLength = parse_object(buffer + pos, length - pos, &key); + if (keyLength == 0 || key == nil) { + objc_autoreleasePoolPop(pool); + + *object = nil; + return 0; + } + pos += keyLength; + + valueLength = parse_object(buffer + pos, length - pos, &value); + if (valueLength == 0 || value == nil) { + objc_autoreleasePoolPop(pool); + + *object = nil; + return 0; + } + pos += valueLength; + + [*object setObject: value + forKey: key]; + + objc_autoreleasePoolPop(pool); + } + + return pos; +} + +static size_t +parse_object(const uint8_t *buffer, size_t length, id *object) +{ + size_t i, count; + + if (length < 1) + goto error; + + /* Integers */ + if ((buffer[0] & 0x80) == 0) { + *object = [OFNumber numberWithUInt8: buffer[0] & 0x7F]; + return 1; + } + if ((buffer[0] & 0xE0) == 0xE0) { + *object = [OFNumber numberWithInt8: + ((int8_t)(buffer[0] & 0x1F)) - 32]; + return 1; + } + + /* Data */ + if ((buffer[0] & 0xF0) == 0xA0) { + count = buffer[0] & 0xF; + + if (length < count + 1) + goto error; + + *object = [OFDataArray dataArrayWithItemSize: 1 + capacity: count]; + [*object addItems: buffer + 1 + count: count]; + + return count + 1; + } + + /* String */ + if ((buffer[0] & 0xF0) == 0xB0) { + count = buffer[0] & 0xF; + + if (length < count + 1) + goto error; + + *object = [OFString + stringWithUTF8String: (const char*)buffer + 1 + length: count]; + return count + 1; + } + + /* Array */ + if ((buffer[0] & 0xF0) == 0x90) + return parse_array(buffer + 1, length - 1, object, + buffer[0] & 0xF) + 1; + + /* Table */ + if ((buffer[0] & 0xF0) == 0x80) + return parse_table(buffer + 1, length - 1, object, + buffer[0] & 0xF) + 1; + + /* Prefix byte */ + switch (*buffer) { + /* Unsigned integers */ + case 0xCC: + if (length < 2) + goto error; + + *object = [OFNumber numberWithUInt8: buffer[1]]; + return 2; + case 0xCD: + if (length < 3) + goto error; + + *object = [OFNumber numberWithUInt16: read_uint16(buffer + 1)]; + return 3; + case 0xCE: + if (length < 5) + goto error; + + *object = [OFNumber numberWithUInt32: read_uint32(buffer + 1)]; + return 5; + case 0xCF: + if (length < 9) + goto error; + + *object = [OFNumber numberWithUInt64: read_uint64(buffer + 1)]; + return 9; + /* Signed integers */ + case 0xD0: + if (length < 2) + goto error; + + *object = [OFNumber numberWithInt8: buffer[1]]; + return 2; + case 0xD1: + if (length < 3) + goto error; + + *object = [OFNumber numberWithInt16: read_uint16(buffer + 1)]; + return 3; + case 0xD2: + if (length < 5) + goto error; + + *object = [OFNumber numberWithInt32: read_uint32(buffer + 1)]; + return 5; + case 0xD3: + if (length < 9) + goto error; + + *object = [OFNumber numberWithInt64: read_uint64(buffer + 1)]; + return 9; + /* Float */ + case 0xCA:; + union { + uint8_t u8[4]; + float f; + } f; + + if (length < 5) + goto error; + + for (i = 0; i < 4; i++) + f.u8[i] = buffer[i + 1]; + + *object = [OFNumber numberWithFloat: OF_BSWAP_FLOAT_IF_LE(f.f)]; + return 5; + /* Double */ + case 0xCB:; + union { + uint8_t u8[8]; + double d; + } d; + + if (length < 9) + goto error; + + for (i = 0; i < 8; i++) + d.u8[i] = buffer[i + 1]; + + *object = [OFNumber numberWithDouble: + OF_BSWAP_DOUBLE_IF_LE(d.d)]; + return 9; + /* nil */ + case 0xC0: + *object = [OFNull null]; + return 1; + /* false */ + case 0xC2: + *object = [OFNumber numberWithBool: NO]; + return 1; + /* true */ + case 0xC3: + *object = [OFNumber numberWithBool: YES]; + return 1; + /* Data */ + case 0xDA: + if (length < 3) + goto error; + + count = read_uint16(buffer + 1); + + if (length < count + 3) + goto error; + + *object = [OFDataArray dataArrayWithItemSize: 1 + capacity: count]; + [*object addItems: buffer + 3 + count: count]; + + return count + 3; + case 0xDB: + if (length < 5) + goto error; + + count = read_uint32(buffer + 1); + + if (length < count + 5) + goto error; + + *object = [OFDataArray dataArrayWithItemSize: 1 + capacity: count]; + [*object addItems: buffer + 5 + count: count]; + + return count + 5; + /* Strings */ + case 0xD8: + if (length < 3) + goto error; + + count = read_uint16(buffer + 1); + + if (length < count + 3) + goto error; + + *object = [OFString + stringWithUTF8String: (const char*)buffer + 3 + length: count]; + return count + 3; + case 0xD9: + if (length < 5) + goto error; + + count = read_uint32(buffer + 1); + + if (length < count + 5) + goto error; + + *object = [OFString + stringWithUTF8String: (const char*)buffer + 5 + length: count]; + return count + 5; + /* Arrays */ + case 0xDC: + if (length < 3) + goto error; + + return parse_array(buffer + 3, length - 3, object, + read_uint16(buffer + 1)) + 3; + case 0xDD: + if (length < 5) + goto error; + + return parse_array(buffer + 5, length - 5, object, + read_uint32(buffer + 1)) + 5; + /* Tables */ + case 0xDE: + if (length < 3) + goto error; + + return parse_table(buffer + 3, length - 3, object, + read_uint16(buffer + 1)) + 3; + case 0xDF: + if (length < 5) + goto error; + + return parse_table(buffer + 5, length - 5, object, + read_uint32(buffer + 1)) + 5; + } + +error: + *object = nil; + return 0; +} + +@implementation OFDataArray (BinaryPackValue) +- (id)binaryPackValue +{ + size_t count = [self count]; + id object; + + if (parse_object([self items], count, &object) != count || + object == nil) + @throw [OFInvalidFormatException + exceptionWithClass: [self class]]; + + return object; +} +@end Index: src/OFDataArray.h ================================================================== --- src/OFDataArray.h +++ src/OFDataArray.h @@ -303,5 +303,6 @@ size_t _size; } @end #import "OFDataArray+Hashing.h" +#import "OFDataArray+BinaryPackValue.h" Index: src/OFDataArray.m ================================================================== --- src/OFDataArray.m +++ src/OFDataArray.m @@ -43,10 +43,11 @@ #import "macros.h" /* References for static linking */ void _references_to_categories_of_OFDataArray(void) { + _OFDataArray_BinaryPackValue_reference = 1; _OFDataArray_Hashing_reference = 1; } @implementation OFDataArray + (instancetype)dataArray