/*
* Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
*
* All rights reserved.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 3.0 only,
* as published by the Free Software Foundation.
*
* This program 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
* version 3.0 for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* version 3.0 along with this program. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#import "ObjFW.h"
#import "ObjFWTest.h"
@interface OFMessagePackTests: OTTestCase
@end
const char *smallDictionaryRepresentation =
"\xDE\x00\x10\x00\x01\x01\x02\x02\x03\x03\x04\x04\x05\x05\x06\x06"
"\x07\x07\x08\x08\x09\x09\x0A\x0A\x0B\x0B\x0C\x0C\x0D\x0D\x0E\x0E"
"\x0F\x0F\x10";
@implementation OFMessagePackTests
- (void)testMessagePackRepresentationForNull
{
OTAssertEqualObjects([[OFNull null] messagePackRepresentation],
[OFData dataWithItems: "\xC0" count: 1]);
}
- (void)testObjectByParsingMessagePackForNull
{
OTAssertEqualObjects([[OFData dataWithItems: "\xC0" count: 1]
objectByParsingMessagePack], [OFNull null]);
}
- (void)testMessagePackRepresentationForNumber
{
OTAssertEqualObjects([[OFNumber numberWithChar: -30]
messagePackRepresentation],
[OFData dataWithItems: "\xE2" count: 1]);
OTAssertEqualObjects([[OFNumber numberWithChar: -33]
messagePackRepresentation],
[OFData dataWithItems: "\xD0\xDF" count: 2]);
OTAssertEqualObjects([[OFNumber numberWithUnsignedChar: 127]
messagePackRepresentation],
[OFData dataWithItems: "\x7F" count: 1]);
OTAssertEqualObjects([[OFNumber numberWithUnsignedChar: 128]
messagePackRepresentation],
[OFData dataWithItems: "\xCC\x80" count: 2]);
OTAssertEqualObjects([[OFNumber numberWithShort: -129]
messagePackRepresentation],
[OFData dataWithItems: "\xD1\xFF\x7F" count: 3]);
OTAssertEqualObjects([[OFNumber numberWithUnsignedShort: 256]
messagePackRepresentation],
[OFData dataWithItems: "\xCD\x01\x00" count: 3]);
OTAssertEqualObjects([[OFNumber numberWithLong: -32769]
messagePackRepresentation],
[OFData dataWithItems: "\xD2\xFF\xFF\x7F\xFF" count: 5]);
OTAssertEqualObjects([[OFNumber numberWithUnsignedLong: 65536]
messagePackRepresentation],
[OFData dataWithItems: "\xCE\x00\x01\x00\x00" count: 5]);
OTAssertEqualObjects([[OFNumber numberWithLongLong: -2147483649]
messagePackRepresentation],
[OFData dataWithItems: "\xD3\xFF\xFF\xFF\xFF\x7F\xFF\xFF\xFF"
count: 9]);
OTAssertEqualObjects([[OFNumber numberWithUnsignedLongLong: 4294967296]
messagePackRepresentation],
[OFData dataWithItems: "\xCF\x00\x00\x00\x01\x00\x00\x00\x00"
count: 9]);
OTAssertEqualObjects([[OFNumber numberWithFloat: 1.25f]
messagePackRepresentation],
[OFData dataWithItems: "\xCA\x3F\xA0\x00\x00" count: 5]);
OTAssertEqualObjects([[OFNumber numberWithDouble: 1.25]
messagePackRepresentation],
[OFData dataWithItems: "\xCB\x3F\xF4\x00\x00\x00\x00\x00\x00"
count: 9]);
OTAssertEqualObjects(
[[OFNumber numberWithBool: true] messagePackRepresentation],
[OFData dataWithItems: "\xC3" count: 1]);
OTAssertEqualObjects(
[[OFNumber numberWithBool: false] messagePackRepresentation],
[OFData dataWithItems: "\xC2" count: 1]);
}
- (void)testObjectByParsingMessagePackForNumber
{
OTAssertEqualObjects([[OFData dataWithItems: "\xE2" count: 1]
objectByParsingMessagePack],
[OFNumber numberWithChar: -30]);
OTAssertEqualObjects([[OFData dataWithItems: "\xD0\xDF" count: 2]
objectByParsingMessagePack],
[OFNumber numberWithChar: -33]);
OTAssertEqualObjects([[OFData dataWithItems: "\x7F" count: 1]
objectByParsingMessagePack],
[OFNumber numberWithUnsignedChar: 127]);
OTAssertEqualObjects([[OFData dataWithItems: "\xCC\x80" count: 2]
objectByParsingMessagePack],
[OFNumber numberWithUnsignedChar: 128]);
OTAssertEqualObjects([[OFData dataWithItems: "\xD1\xFF\x7F" count: 3]
objectByParsingMessagePack],
[OFNumber numberWithShort: -129]);
OTAssertEqualObjects([[OFData dataWithItems: "\xCD\x01\x00" count: 3]
objectByParsingMessagePack],
[OFNumber numberWithUnsignedShort: 256]);
OTAssertEqualObjects(
[[OFData dataWithItems: "\xD2\xFF\xFF\x7F\xFF"
count: 5] objectByParsingMessagePack],
[OFNumber numberWithLong: -32769]);
OTAssertEqualObjects(
[[OFData dataWithItems: "\xCE\x00\x01\x00\x00"
count: 5] objectByParsingMessagePack],
[OFNumber numberWithUnsignedLong: 65536]);
OTAssertEqualObjects(
[[OFData dataWithItems: "\xD3\xFF\xFF\xFF\xFF\x7F\xFF\xFF\xFF"
count: 9] objectByParsingMessagePack],
[OFNumber numberWithLongLong: -2147483649]);
OTAssertEqualObjects(
[[OFData dataWithItems: "\xCF\x00\x00\x00\x01\x00\x00\x00\x00"
count: 9] objectByParsingMessagePack],
[OFNumber numberWithUnsignedLongLong: 4294967296]);
OTAssertEqualObjects(
[[OFData dataWithItems: "\xCA\x3F\xA0\x00\x00"
count: 5] objectByParsingMessagePack],
[OFNumber numberWithFloat: 1.25f]);
OTAssertEqualObjects(
[[OFData dataWithItems: "\xCB\x3F\xF4\x00\x00\x00\x00\x00\x00"
count: 9] objectByParsingMessagePack],
[OFNumber numberWithDouble: 1.25]);
OTAssertEqualObjects([[OFData dataWithItems: "\xC3" count: 1]
objectByParsingMessagePack],
[OFNumber numberWithBool: true]);
OTAssertEqualObjects([[OFData dataWithItems: "\xC2" count: 1]
objectByParsingMessagePack],
[OFNumber numberWithBool: false]);
}
static void
generateStringAndData(OFString **string, OFMutableData **data, size_t length,
const char *dataPrefix, size_t dataPrefixLength)
{
*data = [OFMutableData dataWithCapacity: length + dataPrefixLength];
[*data addItems: dataPrefix count: dataPrefixLength];
[*data increaseCountBy: length];
memset([*data mutableItemAtIndex: dataPrefixLength], 'x', length);
*string = [OFString
stringWithUTF8String: [*data itemAtIndex: dataPrefixLength]
length: length];
}
- (void)testMessagePackRepresentationForString
{
OFString *string;
OFMutableData *data;
OTAssertEqualObjects(@"x".messagePackRepresentation,
[OFData dataWithItems: "\xA1x" count: 2]);
generateStringAndData(&string, &data, 32, "\xD9\x20", 2);
OTAssertEqualObjects(string.messagePackRepresentation, data);
generateStringAndData(&string, &data, 256, "\xDA\x01\x00", 3);
OTAssertEqualObjects(string.messagePackRepresentation, data);
generateStringAndData(&string, &data, 65536, "\xDB\x00\x01\x00\x00", 5);
OTAssertEqualObjects(string.messagePackRepresentation, data);
}
- (void)testObjectByParsingMessagePackForString
{
OFString *string;
OFMutableData *data;
OTAssertEqualObjects([[OFData dataWithItems: "\xA1x" count: 2]
objectByParsingMessagePack], @"x");
generateStringAndData(&string, &data, 32, "\xD9\x20", 2);
OTAssertEqualObjects(data.objectByParsingMessagePack, string);
generateStringAndData(&string, &data, 256, "\xDA\x01\x00", 3);
OTAssertEqualObjects(data.objectByParsingMessagePack, string);
generateStringAndData(&string, &data, 65536, "\xDB\x00\x01\x00\x00", 5);
OTAssertEqualObjects(data.objectByParsingMessagePack, string);
}
- (void)testMessagePackRepresentationForData
{
OFMutableData *data;
OTAssertEqualObjects(
[[OFData dataWithItems: "x" count: 1] messagePackRepresentation],
[OFData dataWithItems: "\xC4\x01x" count: 3]);
data = [OFMutableData data];
[data addItems: "\xC5\x01\x00" count: 3];
[data increaseCountBy: 256];
memset([data mutableItemAtIndex: 3], 'x', 256);
OTAssertEqualObjects([[data subdataWithRange: OFMakeRange(3, 256)]
messagePackRepresentation], data);
data = [OFMutableData data];
[data addItems: "\xC6\x00\x01\x00\x00" count: 5];
[data increaseCountBy: 65536];
memset([data mutableItemAtIndex: 5], 'x', 65536);
OTAssertEqualObjects([[data subdataWithRange: OFMakeRange(5, 65536)]
messagePackRepresentation], data);
}
- (void)testObjectByParsingMessagePackForData
{
OFMutableData *data;
OTAssertEqualObjects([[OFData dataWithItems: "\xC4\x01x" count: 3]
objectByParsingMessagePack],
[OFData dataWithItems: "x" count: 1]);
data = [OFMutableData data];
[data addItems: "\xC5\x01\x00" count: 3];
[data increaseCountBy: 256];
memset([data mutableItemAtIndex: 3], 'x', 256);
OTAssertEqualObjects(data.objectByParsingMessagePack,
[data subdataWithRange: OFMakeRange(3, 256)]);
data = [OFMutableData data];
[data addItems: "\xC6\x00\x01\x00\x00" count: 5];
[data increaseCountBy: 65536];
memset([data mutableItemAtIndex: 5], 'x', 65536);
OTAssertEqualObjects(data.objectByParsingMessagePack,
[data subdataWithRange: OFMakeRange(5, 65536)]);
}
- (void)testMessagePackRepresentationForArray
{
OFMutableArray *array = [OFMutableArray arrayWithCapacity: 65536];
OFNumber *number = [OFNumber numberWithUnsignedInt: 1];
OFMutableData *data;
OTAssertEqualObjects([[OFArray array] messagePackRepresentation],
[OFData dataWithItems: "\x90" count: 1]);
OTAssertEqualObjects(
[[OFArray arrayWithObject: number] messagePackRepresentation],
[OFData dataWithItems: "\x91\x01" count: 2]);
data = [OFMutableData dataWithCapacity: 19];
[data addItems: "\xDC\x00\x10" count: 3];
[data increaseCountBy: 16];
memset([data mutableItemAtIndex: 3], '\x01', 16);
for (size_t i = 0; i < 16; i++)
[array addObject: number];
OTAssertEqualObjects(array.messagePackRepresentation, data);
data = [OFMutableData dataWithCapacity: 65541];
[data addItems: "\xDD\x00\x01\x00\x00" count: 5];
[data increaseCountBy: 65536];
memset([data mutableItemAtIndex: 5], '\x01', 65536);
for (size_t i = 16; i < 65536; i++)
[array addObject: number];
OTAssertEqualObjects(array.messagePackRepresentation, data);
}
- (void)testObjectByParsingMessagePackForArray
{
OFMutableArray *array = [OFMutableArray arrayWithCapacity: 65536];
OFNumber *number = [OFNumber numberWithUnsignedInt: 1];
OFMutableData *data;
OTAssertEqualObjects([[OFData dataWithItems: "\x90" count: 1]
objectByParsingMessagePack], [OFArray array]);
OTAssertEqualObjects([[OFData dataWithItems: "\x91\x01" count: 2]
objectByParsingMessagePack],
[OFArray arrayWithObject: number]);
data = [OFMutableData dataWithCapacity: 19];
[data addItems: "\xDC\x00\x10" count: 3];
[data increaseCountBy: 16];
memset([data mutableItemAtIndex: 3], '\x01', 16);
for (size_t i = 0; i < 16; i++)
[array addObject: number];
OTAssertEqualObjects(data.objectByParsingMessagePack, array);
data = [OFMutableData dataWithCapacity: 65541];
[data addItems: "\xDD\x00\x01\x00\x00" count: 5];
[data increaseCountBy: 65536];
memset([data mutableItemAtIndex: 5], '\x01', 65536);
for (size_t i = 16; i < 65536; i++)
[array addObject: number];
OTAssertEqualObjects(data.objectByParsingMessagePack, array);
}
- (void)testMessagePackRepresentationForDictionary
{
OFMutableArray *keys = [OFMutableArray arrayWithCapacity: 65536];
OFMutableArray *objects = [OFMutableArray arrayWithCapacity: 65536];
OTAssertEqualObjects([[OFDictionary dictionary]
messagePackRepresentation],
[OFData dataWithItems: "\x80" count: 1]);
OTAssertEqualObjects([[OFDictionary
dictionaryWithObject: [OFNumber numberWithUnsignedInt: 2]
forKey: [OFNumber numberWithUnsignedInt: 1]]
messagePackRepresentation],
[OFData dataWithItems: "\x81\x01\x02" count: 3]);
for (unsigned int i = 0; i < 16; i++) {
[keys addObject: [OFNumber numberWithUnsignedInt: i]];
[objects addObject: [OFNumber numberWithUnsignedInt: i + 1]];
}
OTAssertEqualObjects([[OTOrderedDictionary
dictionaryWithObjects: objects.objects
forKeys: keys.objects
count: 16] messagePackRepresentation],
[OFData dataWithItems: smallDictionaryRepresentation count: 35]);
for (unsigned int i = 16; i < 65536; i++) {
[keys addObject: [OFNumber numberWithUnsignedInt: i]];
[objects addObject: [OFNumber numberWithUnsignedInt: i + 1]];
}
OTAssertEqualObjects([[OTOrderedDictionary
dictionaryWithObjects: objects.objects
forKeys: keys.objects
count: 65536] messagePackRepresentation],
[OFData dataWithContentsOfIRI:
[OFIRI IRIWithString: @"gzip:embedded:big_dictionary.msgpack.gz"]]);
}
- (void)testObjectByParsingMessagePackForDictionary
{
OFMutableDictionary *dictionary =
[OFMutableDictionary dictionaryWithCapacity: 65536];
OTAssertEqualObjects([[OFData dataWithItems: "\x80" count: 1]
objectByParsingMessagePack], [OFDictionary dictionary]);
OTAssertEqualObjects([[OFData dataWithItems: "\x81\x01\x02" count: 3]
objectByParsingMessagePack],
[OFDictionary
dictionaryWithObject: [OFNumber numberWithUnsignedInt: 2]
forKey: [OFNumber numberWithUnsignedInt: 1]]);
for (unsigned int i = 0; i < 16; i++)
[dictionary setObject: [OFNumber numberWithUnsignedInt: i + 1]
forKey: [OFNumber numberWithUnsignedInt: i]];
OTAssertEqualObjects(
[[OFData dataWithItems: smallDictionaryRepresentation
count: 35] objectByParsingMessagePack],
dictionary);
for (unsigned int i = 16; i < 65536; i++)
[dictionary setObject: [OFNumber numberWithUnsignedInt: i + 1]
forKey: [OFNumber numberWithUnsignedInt: i]];
OTAssertEqualObjects(dictionary,
[[OFData dataWithContentsOfIRI:
[OFIRI IRIWithString: @"gzip:embedded:big_dictionary.msgpack.gz"]]
objectByParsingMessagePack]);
}
- (void)testMessagePackRepresentationForExtension
{
OFMessagePackExtension *extension;
OFMutableData *data;
extension = [OFMessagePackExtension
extensionWithType: 1
data: [OFData dataWithItems: "x" count: 1]];
OTAssertEqualObjects(extension.messagePackRepresentation,
[OFData dataWithItems: "\xD4\x01x" count: 3]);
extension = [OFMessagePackExtension
extensionWithType: 1
data: [OFData dataWithItems: "xy" count: 2]];
OTAssertEqualObjects(extension.messagePackRepresentation,
[OFData dataWithItems: "\xD5\x01xy" count: 4]);
extension = [OFMessagePackExtension
extensionWithType: 1
data: [OFData dataWithItems: "abcd" count: 4]];
OTAssertEqualObjects(extension.messagePackRepresentation,
[OFData dataWithItems: "\xD6\x01" "abcd" count: 6]);
extension = [OFMessagePackExtension
extensionWithType: 1
data: [OFData dataWithItems: "12345678" count: 8]];
OTAssertEqualObjects(extension.messagePackRepresentation,
[OFData dataWithItems: "\xD7\x01" "12345678" count: 10]);
extension = [OFMessagePackExtension
extensionWithType: 1
data: [OFData dataWithItems: "12345678ABCDEFGH"
count: 16]];
OTAssertEqualObjects(extension.messagePackRepresentation,
[OFData dataWithItems: "\xD8\x01" "12345678ABCDEFGH" count: 18]);
extension = [OFMessagePackExtension extensionWithType: 1
data: [OFData data]];
OTAssertEqualObjects(extension.messagePackRepresentation,
[OFData dataWithItems: "\xC7\x00\x01" count: 3]);
extension = [OFMessagePackExtension
extensionWithType: 1
data: [OFData dataWithItems: "abc" count: 3]];
OTAssertEqualObjects(extension.messagePackRepresentation,
[OFData dataWithItems: "\xC7\x03\x01" "abc" count: 6]);
data = [OFMutableData dataWithCapacity: 260];
[data addItems: "\xC8\x01\x00\x01" count: 4];
[data increaseCountBy: 256];
memset([data mutableItemAtIndex: 4], 'x', 256);
extension = [OFMessagePackExtension
extensionWithType: 1
data: [data subdataWithRange: OFMakeRange(4, 256)]];
OTAssertEqualObjects(extension.messagePackRepresentation, data);
data = [OFMutableData dataWithCapacity: 65542];
[data addItems: "\xC9\x00\x01\x00\x00\x01" count: 6];
[data increaseCountBy: 65536];
memset([data mutableItemAtIndex: 6], 'x', 65536);
extension = [OFMessagePackExtension
extensionWithType: 1
data: [data subdataWithRange: OFMakeRange(6, 65536)]];
OTAssertEqualObjects(extension.messagePackRepresentation, data);
}
- (void)testObjectByParsingMessagePackForExtension
{
OFMessagePackExtension *extension;
OFMutableData *data;
extension = [OFMessagePackExtension
extensionWithType: 1
data: [OFData dataWithItems: "x" count: 1]];
OTAssertEqualObjects([[OFData dataWithItems: "\xD4\x01x" count: 3]
objectByParsingMessagePack], extension);
extension = [OFMessagePackExtension
extensionWithType: 1
data: [OFData dataWithItems: "xy" count: 2]];
OTAssertEqualObjects([[OFData dataWithItems: "\xD5\x01xy" count: 4]
objectByParsingMessagePack], extension);
extension = [OFMessagePackExtension
extensionWithType: 1
data: [OFData dataWithItems: "abcd" count: 4]];
OTAssertEqualObjects(
[[OFData dataWithItems: "\xD6\x01" "abcd"
count: 6] objectByParsingMessagePack],
extension);
extension = [OFMessagePackExtension
extensionWithType: 1
data: [OFData dataWithItems: "12345678" count: 8]];
OTAssertEqualObjects(
[[OFData dataWithItems: "\xD7\x01" "12345678"
count: 10] objectByParsingMessagePack],
extension);
extension = [OFMessagePackExtension
extensionWithType: 1
data: [OFData dataWithItems: "12345678ABCDEFGH"
count: 16]];
OTAssertEqualObjects(
[[OFData dataWithItems: "\xD8\x01" "12345678ABCDEFGH"
count: 18] objectByParsingMessagePack],
extension);
extension = [OFMessagePackExtension extensionWithType: 1
data: [OFData data]];
OTAssertEqualObjects([[OFData dataWithItems: "\xC7\x00\x01" count: 3]
objectByParsingMessagePack], extension);
extension = [OFMessagePackExtension
extensionWithType: 1
data: [OFData dataWithItems: "abc" count: 3]];
OTAssertEqualObjects(
[[OFData dataWithItems: "\xC7\x03\x01" "abc"
count: 6] objectByParsingMessagePack],
extension);
data = [OFMutableData dataWithCapacity: 260];
[data addItems: "\xC8\x01\x00\x01" count: 4];
[data increaseCountBy: 256];
memset([data mutableItemAtIndex: 4], 'x', 256);
extension = [OFMessagePackExtension
extensionWithType: 1
data: [data subdataWithRange: OFMakeRange(4, 256)]];
OTAssertEqualObjects(data.objectByParsingMessagePack, extension);
data = [OFMutableData dataWithCapacity: 65542];
[data addItems: "\xC9\x00\x01\x00\x00\x01" count: 6];
[data increaseCountBy: 65536];
memset([data mutableItemAtIndex: 6], 'x', 65536);
extension = [OFMessagePackExtension
extensionWithType: 1
data: [data subdataWithRange: OFMakeRange(6, 65536)]];
OTAssertEqualObjects(data.objectByParsingMessagePack, extension);
}
- (void)testMessagePackRepresentationForDate
{
OTAssertEqualObjects([[OFDate dateWithTimeIntervalSince1970: 1]
messagePackRepresentation],
[OFData dataWithItems: "\xD6\xFF\x00\x00\x00\x01" count: 6]);
OTAssertEqualObjects([[OFDate dateWithTimeIntervalSince1970: 1.25]
messagePackRepresentation],
[OFData dataWithItems: "\xD7\xFF\x3B\x9A\xCA\x00\x00\x00\x00\x01"
count: 10]);
OTAssertEqualObjects(
[[OFDate dateWithTimeIntervalSince1970: 0x400000000 + 0.25]
messagePackRepresentation],
[OFData dataWithItems: "\xC7\x0C\xFF\x0E\xE6\xB2\x80\x00\x00\x00"
"\x04\x00\x00\x00\x00"
count: 15]);
}
- (void)testObjectByParsingMessagePackForDate
{
OTAssertEqualObjects(
[[OFData dataWithItems: "\xD6\xFF\x00\x00\x00\x01"
count: 6] objectByParsingMessagePack],
[OFDate dateWithTimeIntervalSince1970: 1]);
OTAssertEqualObjects(
[[OFData dataWithItems: "\xD7\xFF\x3B\x9A\xCA\x00\x00\x00\x00\x01"
count: 10] objectByParsingMessagePack],
[OFDate dateWithTimeIntervalSince1970: 1.25]);
OTAssertEqualObjects(
[[OFData dataWithItems: "\xC7\x0C\xFF\x0E\xE6\xB2\x80\x00\x00\x00"
"\x04\x00\x00\x00\x00"
count: 15] objectByParsingMessagePack],
[OFDate dateWithTimeIntervalSince1970: 0x400000000 + 0.25]);
}
@end