Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -80,25 +80,26 @@ OFThread.m \ OFThreadPool.m \ OFTLSKey.m INCLUDES_THREADS = threading.h -INCLUDES := ${SRCS:.m=.h} \ - OFCollection.h \ - OFHash.h \ - OFJSONRepresentation.h \ - OFLocking.h \ - OFSerialization.h \ - OFTLSSocket.h \ - ObjFW.h \ - asprintf.h \ - autorelease.h \ - ${ATOMIC_H} \ - block.h \ - instance.h \ - macros.h \ - objfw-defs.h \ +INCLUDES := ${SRCS:.m=.h} \ + OFBinaryPackRepresentation.h \ + OFCollection.h \ + OFHash.h \ + OFJSONRepresentation.h \ + OFLocking.h \ + OFSerialization.h \ + OFTLSSocket.h \ + ObjFW.h \ + asprintf.h \ + autorelease.h \ + ${ATOMIC_H} \ + block.h \ + instance.h \ + macros.h \ + objfw-defs.h \ ${USE_INCLUDES_THREADS} SRCS += OFArray_adjacent.m \ OFArray_adjacentSubarray.m \ ${AUTORELEASE_M} \ Index: src/OFArray.h ================================================================== --- src/OFArray.h +++ src/OFArray.h @@ -26,10 +26,11 @@ #import "OFObject.h" #import "OFCollection.h" #import "OFEnumerator.h" #import "OFSerialization.h" #import "OFJSONRepresentation.h" +#import "OFBinaryPackRepresentation.h" @class OFString; enum { OF_SORT_OPTIONS_DESCENDING = 1 @@ -45,11 +46,11 @@ /*! * @brief An abstract class for storing objects in an array. */ @interface OFArray: OFObject + OFSerialization, OFJSONRepresentation, OFBinaryPackRepresentation> #ifdef OF_HAVE_PROPERTIES @property (readonly) size_t count; #endif /*! Index: src/OFArray.m ================================================================== --- src/OFArray.m +++ src/OFArray.m @@ -17,15 +17,18 @@ #include "config.h" #include #include +#include + #import "OFArray.h" #import "OFArray_subarray.h" #import "OFArray_adjacent.h" #import "OFString.h" #import "OFXMLElement.h" +#import "OFDataArray.h" #import "OFEnumerationMutationException.h" #import "OFInvalidArgumentException.h" #import "OFOutOfRangeException.h" @@ -557,10 +560,65 @@ objc_autoreleasePoolPop(pool); return [JSON autorelease]; } + +- (OFDataArray*)binaryPackRepresentation +{ + OFDataArray *data; + size_t i, count; + void *pool; + OFEnumerator *enumerator; + id object; + + data = [OFDataArray dataArray]; + count = [self count]; + + if (count <= 15) { + uint8_t tmp = 0x90 | ((uint8_t)count & 0xF); + [data addItem: &tmp]; + } else if (count <= UINT16_MAX) { + uint8_t type = 0xDC; + uint16_t tmp = OF_BSWAP16_IF_LE((uint16_t)count); + + [data addItem: &type]; + [data addItems: &tmp + count: sizeof(tmp)]; + } else if (count <= UINT32_MAX) { + uint8_t type = 0xDC; + uint32_t tmp = OF_BSWAP32_IF_LE((uint32_t)count); + + [data addItem: &type]; + [data addItems: &tmp + count: sizeof(tmp)]; + } else + @throw [OFOutOfRangeException exceptionWithClass: [self class]]; + + pool = objc_autoreleasePoolPush(); + + i = 0; + enumerator = [self objectEnumerator]; + while ((object = [enumerator nextObject]) != nil) { + void *pool2 = objc_autoreleasePoolPush(); + OFDataArray *child; + + i++; + + child = [object binaryPackRepresentation]; + [data addItems: [child items] + count: [child count]]; + + objc_autoreleasePoolPop(pool2); + } + + assert(i == count); + + objc_autoreleasePoolPop(pool); + + return data; +} - (void)makeObjectsPerformSelector: (SEL)selector { id *objects = [self objects]; size_t i, count = [self count]; ADDED src/OFBinaryPackRepresentation.h Index: src/OFBinaryPackRepresentation.h ================================================================== --- src/OFBinaryPackRepresentation.h +++ src/OFBinaryPackRepresentation.h @@ -0,0 +1,30 @@ +/* + * 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. + */ + +@class OFDataArray; + +/*! + * @brief A protocol implemented by classes that support encoding to a + * BinaryPack representation. + */ +@protocol OFBinaryPackRepresentation +/*! + * @brief Returns the BinaryPack representation of the object as an OFDataArray. + * + * @return The BinaryPack representation of the object as an OFDataArray. + */ +- (OFDataArray*)binaryPackRepresentation; +@end Index: src/OFDataArray.h ================================================================== --- src/OFDataArray.h +++ src/OFDataArray.h @@ -14,10 +14,11 @@ * file. */ #import "OFObject.h" #import "OFSerialization.h" +#import "OFBinaryPackRepresentation.h" @class OFString; @class OFURL; /*! @@ -27,11 +28,12 @@ * OFBigDataArray, which allocates the memory in pages rather than in bytes. * * For security reasons, serialization and deserialization is only implemented * for OFDataArrays with item size 1. */ -@interface OFDataArray: OFObject +@interface OFDataArray: OFObject { uint8_t *_items; size_t _count, _itemSize, _capacity; } Index: src/OFDataArray.m ================================================================== --- src/OFDataArray.m +++ src/OFDataArray.m @@ -614,10 +614,55 @@ objc_autoreleasePoolPop(pool); return [element autorelease]; } + +- (OFDataArray*)binaryPackRepresentation +{ + OFDataArray *data; + + if (_itemSize != 1) + @throw [OFInvalidArgumentException + exceptionWithClass: [self class] + selector: _cmd]; + + if (_count <= 15) { + uint8_t tmp = 0xA0 | ((uint8_t)_count & 0xF); + + data = [OFDataArray dataArrayWithItemSize: 1 + capacity: _count + 1]; + + [data addItem: &tmp]; + } else if (_count <= UINT16_MAX) { + uint8_t type = 0xDA; + uint16_t tmp = OF_BSWAP16_IF_LE((uint16_t)_count); + + data = [OFDataArray dataArrayWithItemSize: 1 + capacity: _count + 3]; + + [data addItem: &type]; + [data addItems: &tmp + count: sizeof(tmp)]; + } else if (_count <= UINT32_MAX) { + uint8_t type = 0xDB; + uint32_t tmp = OF_BSWAP32_IF_LE((uint32_t)_count); + + data = [OFDataArray dataArrayWithItemSize: 1 + capacity: _count + 5]; + + [data addItem: &type]; + [data addItems: &tmp + count: sizeof(tmp)]; + } else + @throw [OFOutOfRangeException exceptionWithClass: [self class]]; + + [data addItems: _items + count: _count]; + + return data; +} @end @implementation OFBigDataArray - initWithItemSize: (size_t)itemSize capacity: (size_t)capacity Index: src/OFDictionary.h ================================================================== --- src/OFDictionary.h +++ src/OFDictionary.h @@ -26,10 +26,11 @@ #import "OFObject.h" #import "OFCollection.h" #import "OFEnumerator.h" #import "OFSerialization.h" #import "OFJSONRepresentation.h" +#import "OFBinaryPackRepresentation.h" @class OFArray; #ifdef OF_HAVE_BLOCKS typedef void (^of_dictionary_enumeration_block_t)(id key, id object, @@ -45,11 +46,11 @@ * * Note: Fast enumeration on a dictionary enumerates through the keys of the * dictionary. */ @interface OFDictionary: OFObject + OFSerialization, OFJSONRepresentation, OFBinaryPackRepresentation> /*! * @brief Creates a new OFDictionary. * * @return A new autoreleased OFDictionary */ Index: src/OFDictionary.m ================================================================== --- src/OFDictionary.m +++ src/OFDictionary.m @@ -23,14 +23,17 @@ #import "OFDictionary.h" #import "OFDictionary_hashtable.h" #import "OFArray.h" #import "OFString.h" #import "OFXMLElement.h" +#import "OFDataArray.h" #import "OFInvalidArgumentException.h" +#import "OFOutOfRangeException.h" #import "autorelease.h" +#import "macros.h" static struct { Class isa; } placeholder; @@ -662,6 +665,67 @@ objc_autoreleasePoolPop(pool); return JSON; } + +- (OFDataArray*)binaryPackRepresentation +{ + OFDataArray *data; + size_t i, count; + void *pool; + OFEnumerator *keyEnumerator, *objectEnumerator; + id key, object; + + data = [OFDataArray dataArray]; + count = [self count]; + + if (count <= 15) { + uint8_t tmp = 0x80 | ((uint8_t)count & 0xF); + [data addItem: &tmp]; + } else if (count <= UINT16_MAX) { + uint8_t type = 0xDE; + uint16_t tmp = OF_BSWAP16_IF_LE((uint16_t)count); + + [data addItem: &type]; + [data addItems: &tmp + count: sizeof(tmp)]; + } else if (count <= UINT32_MAX) { + uint8_t type = 0xDF; + uint32_t tmp = OF_BSWAP32_IF_LE((uint32_t)count); + + [data addItem: &type]; + [data addItems: &tmp + count: sizeof(tmp)]; + } else + @throw [OFOutOfRangeException exceptionWithClass: [self class]]; + + pool = objc_autoreleasePoolPush(); + + i = 0; + keyEnumerator = [self keyEnumerator]; + objectEnumerator = [self objectEnumerator]; + while ((key = [keyEnumerator nextObject]) != nil && + (object = [objectEnumerator nextObject]) != nil) { + void *pool2 = objc_autoreleasePoolPush(); + OFDataArray *child; + + i++; + + child = [key binaryPackRepresentation]; + [data addItems: [child items] + count: [child count]]; + + child = [object binaryPackRepresentation]; + [data addItems: [child items] + count: [child count]]; + + objc_autoreleasePoolPop(pool2); + } + + assert(i == count); + + objc_autoreleasePoolPop(pool); + + return data; +} @end Index: src/OFNull.h ================================================================== --- src/OFNull.h +++ src/OFNull.h @@ -15,17 +15,19 @@ */ #import "OFObject.h" #import "OFSerialization.h" #import "OFJSONRepresentation.h" +#import "OFBinaryPackRepresentation.h" /*! * @brief A class for representing null values in collections. */ -@interface OFNull: OFObject +@interface OFNull: OFObject /*! * @brief Returns an OFNull singleton. * * @return An OFNull singleton */ + (OFNull*)null; @end Index: src/OFNull.m ================================================================== --- src/OFNull.m +++ src/OFNull.m @@ -19,10 +19,11 @@ #include #import "OFNull.h" #import "OFString.h" #import "OFXMLElement.h" +#import "OFDataArray.h" #import "OFInvalidArgumentException.h" #import "autorelease.h" @@ -85,10 +86,21 @@ - (OFString*)JSONRepresentation { return @"null"; } + +- (OFDataArray*)binaryPackRepresentation +{ + OFDataArray *data = [OFDataArray dataArrayWithItemSize: 1 + capacity: 1]; + uint8_t type = 0xC0; + + [data addItem: &type]; + + return data; +} - autorelease { return self; } Index: src/OFNumber.h ================================================================== --- src/OFNumber.h +++ src/OFNumber.h @@ -24,10 +24,11 @@ #include #import "OFObject.h" #import "OFSerialization.h" #import "OFJSONRepresentation.h" +#import "OFBinaryPackRepresentation.h" /*! @file */ /*! * @brief The C type of a number stored in an OFNumber. @@ -90,11 +91,11 @@ /*! * @brief Provides a way to store a number in an object. */ @interface OFNumber: OFObject + OFJSONRepresentation, OFBinaryPackRepresentation> { union of_number_value { BOOL bool_; signed char schar; signed short sshort; Index: src/OFNumber.m ================================================================== --- src/OFNumber.m +++ src/OFNumber.m @@ -22,13 +22,15 @@ #import "OFNumber.h" #import "OFString.h" #import "OFXMLElement.h" #import "OFXMLAttribute.h" +#import "OFDataArray.h" #import "OFInvalidArgumentException.h" #import "OFInvalidFormatException.h" +#import "OFOutOfRangeException.h" #import "autorelease.h" #import "macros.h" #define RETURN_AS(t) \ @@ -1408,6 +1410,151 @@ return @"-Infinity"; } return [self description]; } + +- (OFDataArray*)binaryPackRepresentation +{ + OFDataArray *data; + + if (_type == OF_NUMBER_BOOL) { + uint8_t type; + + data = [OFDataArray dataArrayWithItemSize: 1 + capacity: 1]; + + if (_value.bool_) + type = 0xC3; + else + type = 0xC2; + + [data addItem: &type]; + } else if (_type == OF_NUMBER_FLOAT) { + uint8_t type = 0xCA; + float tmp = OF_BSWAP_FLOAT_IF_LE(_value.float_); + + data = [OFDataArray dataArrayWithItemSize: 1 + capacity: 5]; + + [data addItem: &type]; + [data addItems: &tmp + count: sizeof(tmp)]; + } else if (_type == OF_NUMBER_DOUBLE) { + uint8_t type = 0xCB; + double tmp = OF_BSWAP_DOUBLE_IF_LE(_value.double_); + + data = [OFDataArray dataArrayWithItemSize: 1 + capacity: 9]; + + [data addItem: &type]; + [data addItems: &tmp + count: sizeof(tmp)]; + } else if (_type & OF_NUMBER_SIGNED) { + intmax_t value = [self intMaxValue]; + + if (value >= -32 && value < 0) { + uint8_t tmp = 0xE0 | ((uint8_t)(value - 32) & 0x1F); + + data = [OFDataArray dataArrayWithItemSize: 1 + capacity: 1]; + + [data addItem: &tmp]; + } else if (value >= INT8_MIN && value <= INT8_MAX) { + uint8_t type = 0xD0; + int8_t tmp = (int8_t)value; + + data = [OFDataArray dataArrayWithItemSize: 1 + capacity: 2]; + + [data addItem: &type]; + [data addItem: &tmp]; + } else if (value >= INT16_MIN && value <= INT16_MAX) { + uint8_t type = 0xD1; + int16_t tmp = OF_BSWAP16_IF_LE((int16_t)value); + + data = [OFDataArray dataArrayWithItemSize: 1 + capacity: 3]; + + [data addItem: &type]; + [data addItems: &tmp + count: sizeof(tmp)]; + } else if (value >= INT32_MIN && value <= INT32_MAX) { + uint8_t type = 0xD2; + int32_t tmp = OF_BSWAP32_IF_LE((int32_t)value); + + data = [OFDataArray dataArrayWithItemSize: 1 + capacity: 5]; + + [data addItem: &type]; + [data addItems: &tmp + count: sizeof(tmp)]; + } else if (value >= INT64_MIN && value <= INT64_MAX) { + uint8_t type = 0xD3; + int64_t tmp = OF_BSWAP64_IF_LE((int64_t)value); + + data = [OFDataArray dataArrayWithItemSize: 1 + capacity: 9]; + + [data addItem: &type]; + [data addItems: &tmp + count: sizeof(tmp)]; + } else + @throw [OFOutOfRangeException + exceptionWithClass: [self class]]; + } else { + uintmax_t value = [self uIntMaxValue]; + + if (value <= 127) { + uint8_t tmp = ((uint8_t)value & 0x7F); + + data = [OFDataArray dataArrayWithItemSize: 1 + capacity: 1]; + + [data addItem: &tmp]; + } else if (value <= UINT8_MAX) { + uint8_t type = 0xCC; + uint8_t tmp = (uint8_t)value; + + data = [OFDataArray dataArrayWithItemSize: 1 + capacity: 2]; + + [data addItem: &type]; + [data addItem: &tmp]; + } else if (value <= UINT16_MAX) { + uint8_t type = 0xCD; + uint16_t tmp = OF_BSWAP16_IF_LE((uint16_t)value); + + data = [OFDataArray dataArrayWithItemSize: 1 + capacity: 3]; + + [data addItem: &type]; + [data addItems: &tmp + count: sizeof(tmp)]; + } else if (value <= UINT32_MAX) { + uint8_t type = 0xCE; + uint32_t tmp = OF_BSWAP32_IF_LE((uint32_t)value); + + data = [OFDataArray dataArrayWithItemSize: 1 + capacity: 5]; + + [data addItem: &type]; + [data addItems: &tmp + count: sizeof(tmp)]; + } else if (value <= UINT64_MAX) { + uint8_t type = 0xCF; + uint64_t tmp = OF_BSWAP64_IF_LE((uint64_t)value); + + data = [OFDataArray dataArrayWithItemSize: 1 + capacity: 9]; + + [data addItem: &type]; + [data addItems: &tmp + count: sizeof(tmp)]; + } else + @throw [OFOutOfRangeException + exceptionWithClass: [self class]]; + } + + return data; +} @end Index: src/OFString.h ================================================================== --- src/OFString.h +++ src/OFString.h @@ -25,10 +25,11 @@ #include #import "OFObject.h" #import "OFSerialization.h" #import "OFJSONRepresentation.h" +#import "OFBinaryPackRepresentation.h" @class OFConstantString; #if defined(__cplusplus) && __cplusplus >= 201103L typedef char16_t of_char16_t; @@ -76,11 +77,11 @@ /*! * @brief A class for handling strings. */ @interface OFString: OFObject + OFSerialization, OFJSONRepresentation, OFBinaryPackRepresentation> #ifdef OF_HAVE_PROPERTIES @property (readonly) size_t length; #endif /*! Index: src/OFString.m ================================================================== --- src/OFString.m +++ src/OFString.m @@ -1586,10 +1586,53 @@ [JSON makeImmutable]; return JSON; } + +- (OFDataArray*)binaryPackRepresentation +{ + OFDataArray *data; + size_t length; + + length = [self UTF8StringLength]; + + if (length <= 15) { + uint8_t tmp = 0xB0 | ((uint8_t)length & 0xF); + + data = [OFDataArray dataArrayWithItemSize: 1 + capacity: length + 1]; + + [data addItem: &tmp]; + } else if (length <= UINT16_MAX) { + uint8_t type = 0xD8; + uint16_t tmp = OF_BSWAP16_IF_LE((uint16_t)length); + + data = [OFDataArray dataArrayWithItemSize: 1 + capacity: length + 3]; + + [data addItem: &type]; + [data addItems: &tmp + count: sizeof(tmp)]; + } else if (length <= UINT32_MAX) { + uint8_t type = 0xD9; + uint32_t tmp = OF_BSWAP32_IF_LE((uint32_t)length); + + data = [OFDataArray dataArrayWithItemSize: 1 + capacity: length + 5]; + + [data addItem: &type]; + [data addItems: &tmp + count: sizeof(tmp)]; + } else + @throw [OFOutOfRangeException exceptionWithClass: [self class]]; + + [data addItems: [self UTF8String] + count: length]; + + return data; +} - (of_range_t)rangeOfString: (OFString*)string { return [self rangeOfString: string options: 0