#include "unittest/TestSuite.h"
#include "utilities/printfFormats.h"
#include "binaryserialization/BinaryDeserializationContext.h"
#include "utilities/IOUtilities.h"
#include <float.h>
#include <string.h>
#include <unistd.h>

static void verifyInit(BinaryDeserializationContext * context) {
	TestCase_assertPointerNULL(context->jmpBuf);
	TestCase_assertIntEqual(context->status, SERIALIZATION_ERROR_OK);
	TestCase_assertPointerEqual(context->vtable, &BinaryDeserializationContext_class);
	TestCase_assertPointerEqual(context->vtable->dispose, BinaryDeserializationContext_dispose);
	TestCase_assertPointerEqual(context->vtable->errorString, BinaryDeserializationContext_errorString);
	TestCase_assertPointerEqual(context->vtable->beginStructure, BinaryDeserializationContext_beginStructure);
	TestCase_assertPointerEqual(context->vtable->beginDictionary, BinaryDeserializationContext_beginDictionary);
	TestCase_assertPointerEqual(context->vtable->beginArray, BinaryDeserializationContext_beginArray);
	TestCase_assertPointerEqual(context->vtable->endStructure, BinaryDeserializationContext_endStructure);
	TestCase_assertPointerEqual(context->vtable->endDictionary, BinaryDeserializationContext_endDictionary);
	TestCase_assertPointerEqual(context->vtable->endArray, BinaryDeserializationContext_endArray);
	TestCase_assertPointerEqual(context->vtable->readBoolean, BinaryDeserializationContext_readBoolean);
	TestCase_assertPointerEqual(context->vtable->readInt8, BinaryDeserializationContext_readInt8);
	TestCase_assertPointerEqual(context->vtable->readUInt8, BinaryDeserializationContext_readUInt8);
	TestCase_assertPointerEqual(context->vtable->readInt16, BinaryDeserializationContext_readInt16);
	TestCase_assertPointerEqual(context->vtable->readUInt16, BinaryDeserializationContext_readUInt16);
	TestCase_assertPointerEqual(context->vtable->readInt32, BinaryDeserializationContext_readInt32);
	TestCase_assertPointerEqual(context->vtable->readUInt32, BinaryDeserializationContext_readUInt32);
	TestCase_assertPointerEqual(context->vtable->readInt64, BinaryDeserializationContext_readInt64);
	TestCase_assertPointerEqual(context->vtable->readUInt64, BinaryDeserializationContext_readUInt64);
	TestCase_assertPointerEqual(context->vtable->readFloat, BinaryDeserializationContext_readFloat);
	TestCase_assertPointerEqual(context->vtable->readDouble, BinaryDeserializationContext_readDouble);
	TestCase_assertPointerEqual(context->vtable->readFixed16_16, BinaryDeserializationContext_readFixed16_16);
	TestCase_assertPointerEqual(context->vtable->readEnumeration, BinaryDeserializationContext_readEnumeration);
	TestCase_assertPointerEqual(context->vtable->readEnumeration8, BinaryDeserializationContext_readEnumeration8);
	TestCase_assertPointerEqual(context->vtable->readEnumeration16, BinaryDeserializationContext_readEnumeration16);
	TestCase_assertPointerEqual(context->vtable->readEnumeration32, BinaryDeserializationContext_readEnumeration32);
	TestCase_assertPointerEqual(context->vtable->readBitfield8, BinaryDeserializationContext_readBitfield8);
	TestCase_assertPointerEqual(context->vtable->readBitfield16, BinaryDeserializationContext_readBitfield16);
	TestCase_assertPointerEqual(context->vtable->readBitfield32, BinaryDeserializationContext_readBitfield32);
	TestCase_assertPointerEqual(context->vtable->readBitfield64, BinaryDeserializationContext_readBitfield64);
	TestCase_assertPointerEqual(context->vtable->readString, BinaryDeserializationContext_readString);
	TestCase_assertPointerEqual(context->vtable->readStringNullable, BinaryDeserializationContext_readStringNullable);
	TestCase_assertPointerEqual(context->vtable->readBlob, BinaryDeserializationContext_readBlob);
	TestCase_assertPointerEqual(context->vtable->readNextDictionaryKey, BinaryDeserializationContext_readNextDictionaryKey);
	TestCase_assertPointerEqual(context->vtable->hasDictionaryKey, BinaryDeserializationContext_hasDictionaryKey);
}

static void testInit() {
	BinaryDeserializationContext context, * contextPtr;
	const char * tempFilePath;
	int fd;
	
	memset(&context, 0, sizeof(context));
	context.status = -1;
	context.jmpBuf = (void *) -1;
	stemobject_assign_vtable(context, BinaryDeserializationContext);
	BinaryDeserializationContext_initWithBytes(&context, "Stem", 4);
	verifyInit(&context);
	TestCase_assert(context.bigEndian, "Expected true but got false");
	BinaryDeserializationContext_dispose(&context);
	
	memset(&context, 0, sizeof(context));
	context.status = -1;
	context.jmpBuf = (void *) -1;
	tempFilePath = temporaryFilePath("tmpXXXXXX", &fd);
	writeFileSimple(tempFilePath, "metS", 4);
	stemobject_assign_vtable(context, BinaryDeserializationContext);
	BinaryDeserializationContext_initWithFile(&context, tempFilePath);
	close(fd);
	unlink(tempFilePath);
	verifyInit(&context);
	TestCase_assert(!context.bigEndian, "Expected false but got true");
	BinaryDeserializationContext_dispose(&context);
	
	contextPtr = BinaryDeserializationContext_createWithBytes("metS", 4);
	TestCase_assert(contextPtr != NULL, "Expected non-NULL but got NULL");
	if (contextPtr == NULL) {return;} // Suppress clang warning
	verifyInit(contextPtr);
	TestCase_assert(!contextPtr->bigEndian, "Expected false but got true");
	BinaryDeserializationContext_dispose(contextPtr);
	
	tempFilePath = temporaryFilePath("tmpXXXXXX", &fd);
	writeFileSimple(tempFilePath, "Stem", 4);
	contextPtr = BinaryDeserializationContext_createWithFile(tempFilePath);
	close(fd);
	unlink(tempFilePath);
	TestCase_assert(contextPtr != NULL, "Expected non-NULL but got NULL");
	if (contextPtr == NULL) {return;} // Suppress clang warning
	verifyInit(contextPtr);
	TestCase_assert(contextPtr->bigEndian, "Expected true but got false");
	BinaryDeserializationContext_dispose(contextPtr);
}

static void testInitErrors() {
	BinaryDeserializationContext * context;
	
	context = BinaryDeserializationContext_createWithBytes("", 0);
	TestCase_assert(context != NULL, "Expected non-NULL but got NULL");
	if (context == NULL) {return;} // Suppress clang warning
	TestCase_assert(context->status == BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF, "Expected %d but got %d", BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF, context->status);
	context->vtable->dispose(context);
	
	context = BinaryDeserializationContext_createWithBytes("Ste", 3);
	TestCase_assert(context != NULL, "Expected non-NULL but got NULL");
	if (context == NULL) {return;} // Suppress clang warning
	TestCase_assert(context->status == BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF, "Expected %d but got %d", BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF, context->status);
	context->vtable->dispose(context);
	
	context = BinaryDeserializationContext_createWithBytes("met", 3);
	TestCase_assert(context != NULL, "Expected non-NULL but got NULL");
	if (context == NULL) {return;} // Suppress clang warning
	TestCase_assert(context->status == BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF, "Expected %d but got %d", BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF, context->status);
	context->vtable->dispose(context);
	
	context = BinaryDeserializationContext_createWithBytes("nope", 4);
	TestCase_assert(context != NULL, "Expected non-NULL but got NULL");
	if (context == NULL) {return;} // Suppress clang warning
	TestCase_assert(context->status == BINARY_SERIALIZATION_ERROR_INVALID_SIGNATURE, "Expected %d but got %d", BINARY_SERIALIZATION_ERROR_INVALID_SIGNATURE, context->status);
	context->vtable->dispose(context);
}

static unsigned int vaArgsToEnumKeyValues(Serialization_enumKeyValue * outValues, ...) {
	va_list args;
	va_start(args, outValues);
	unsigned int valueCount = 0;
	for (;;) {
		const char * name = va_arg(args, const char *);
		if (name == NULL) {
			break;
		}
		outValues[valueCount].key = name;
		outValues[valueCount].value = va_arg(args, int);
		valueCount++;
	}
	va_end(args);
	return valueCount;
}

static unsigned int vaArgsToBitNames(Serialization_bitName * outBitNames, ...) {
	va_list args;
	va_start(args, outBitNames);
	unsigned int bitCount = 0;
	for (;;) {
		const char * name = va_arg(args, const char *);
		if (name == NULL) {
			break;
		}
		outBitNames[bitCount].name = name;
		outBitNames[bitCount].index = bitCount;
		bitCount++;
	}
	va_end(args);
	return bitCount;
}

#define enumKV(value) #value, value

#define beginAndVerifyContainer(CONTAINER_TYPE, KEY, EXPECTED_SIZE) { \
	size_t size; \
	size = context->vtable->begin##CONTAINER_TYPE(context, KEY); \
	TestCase_assert(size == (EXPECTED_SIZE), "Expected " SIZE_T_FORMAT " but got " SIZE_T_FORMAT " (status %d)", (size_t) (EXPECTED_SIZE), size, context->status); \
}
#define beginAndVerifyArray(KEY, EXPECTED_SIZE) beginAndVerifyContainer(Array, KEY, EXPECTED_SIZE)
#define beginAndVerifyDictionary(KEY, EXPECTED_SIZE) beginAndVerifyContainer(Dictionary, KEY, EXPECTED_SIZE)

#define readFunction_int8_t readInt8
#define readFunction_uint8_t readUInt8
#define readFunction_int16_t readInt16
#define readFunction_uint16_t readUInt16
#define readFunction_int32_t readInt32
#define readFunction_uint32_t readUInt32
#define readFunction_int64_t readInt64
#define readFunction_uint64_t readUInt64
#define readFunction_float readFloat
#define readFunction_double readDouble
#define readFunction_fixed16_16 readFixed16_16
#define readFunction_bool readBoolean
#define printfSpecifier_int8_t "%d"
#define printfSpecifier_uint8_t "%u"
#define printfSpecifier_int16_t "%d"
#define printfSpecifier_uint16_t "%u"
#define printfSpecifier_int32_t "%d"
#define printfSpecifier_uint32_t "%u"
#define printfSpecifier_int64_t INT64_FORMAT
#define printfSpecifier_uint64_t UINT64_FORMAT
#define printfSpecifier_float "%f"
#define printfSpecifier_double "%f"
#define printfSpecifier_fixed16_16 "0x%05X"
#define printfSpecifier_bool "%d"
#define readAndVerifyNumber(TYPE, KEY, EXPECTED_VALUE) { \
	TYPE value; \
	value = context->vtable->readFunction_##TYPE(context, KEY); \
	TestCase_assert(value == (EXPECTED_VALUE), "Expected " printfSpecifier_##TYPE " but got " printfSpecifier_##TYPE " (status %d)", (TYPE) (EXPECTED_VALUE), value, context->status); \
	TestCase_assert(context->status == SERIALIZATION_ERROR_OK, "Got error from operation that should have succeeded: %d", context->status); \
}

#define readAndVerifyString(KEY, EXPECTED_VALUE) { \
	const char * value; \
	value = context->vtable->readString(context, KEY); \
	TestCase_assert(value != NULL, "Expected non-NULL but got NULL"); \
	TestCase_assert(!strcmp(value, (EXPECTED_VALUE)), "Expected \"%s\" but got \"%s\"", (EXPECTED_VALUE), value); \
	TestCase_assert(context->status == SERIALIZATION_ERROR_OK, "Got error from operation that should have succeeded: %d", context->status); \
}

#define readAndVerifyStringNullable(KEY, EXPECTED_VALUE) { \
	const char * value; \
	value = context->vtable->readStringNullable(context, KEY); \
	if ((EXPECTED_VALUE) == NULL) { \
		TestCase_assert(value == NULL, "Expected NULL but got \"%s\"", value); \
	} else { \
		const char * expectedValue = (EXPECTED_VALUE); \
		TestCase_assert(value != NULL, "Expected non-NULL but got NULL"); \
		TestCase_assert(!strcmp(value, expectedValue), "Expected \"%s\" but got \"%s\"", expectedValue, value); \
	} \
	TestCase_assert(context->status == SERIALIZATION_ERROR_OK, "Got error from operation that should have succeeded: %d", context->status); \
}

#define readAndVerifyBlob(KEY, EXPECTED_VALUE, EXPECTED_LENGTH) { \
	const void * value; \
	size_t length; \
	value = context->vtable->readBlob(context, KEY, &length); \
	if ((EXPECTED_VALUE) == NULL) { \
		TestCase_assert(value == NULL, "Expected NULL but got %p", value); \
	} else { \
		const void * expectedValue = (EXPECTED_VALUE); \
		TestCase_assert(value != NULL, "Expected non-NULL but got NULL"); \
		TestCase_assert(length == EXPECTED_LENGTH, "Expected " SIZE_T_FORMAT " but got " SIZE_T_FORMAT, (size_t) EXPECTED_LENGTH, length); \
		TestCase_assert(!memcmp(value, expectedValue, EXPECTED_LENGTH), "Expected \"%s\" but got \"%s\"", (char *) expectedValue, (char *) value); \
	} \
	TestCase_assert(context->status == SERIALIZATION_ERROR_OK, "Got error from operation that should have succeeded: %d", context->status); \
}

#define enumReadFunction_int readEnumeration
#define enumReadFunction_int8_t readEnumeration8
#define enumReadFunction_int16_t readEnumeration16
#define enumReadFunction_int32_t readEnumeration32
#define printfSpecifier_int "%d"
#define readAndVerifyEnumeration(TYPE, KEY, EXPECTED_VALUE, ...) { \
	TYPE value; \
	Serialization_enumKeyValue values[3]; \
	unsigned int valueCount = vaArgsToEnumKeyValues(values, __VA_ARGS__); \
	value = call_virtual(enumReadFunction_##TYPE, context, KEY, valueCount, values); \
	TestCase_assert(value == (EXPECTED_VALUE), "Expected " printfSpecifier_##TYPE " but got " printfSpecifier_##TYPE " (status %d)", (TYPE) (EXPECTED_VALUE), value, context->status); \
	TestCase_assert(context->status == SERIALIZATION_ERROR_OK, "Got error from operation that should have succeeded: %d", context->status); \
}

#define printfSpecifier_bitfield8 "0x%02X"
#define printfSpecifier_bitfield16 "0x%04X"
#define printfSpecifier_bitfield32 "0x%08X"
#if defined(WIN32)
#define printfSpecifier_bitfield64 "0x%016I64X"
#elif defined(linux) && defined(_LP64)
#define printfSpecifier_bitfield64 "0x%016lX"
#else
#define printfSpecifier_bitfield64 "0x%016llX"
#endif
#define readAndVerifyBitfield(NBITS, KEY, EXPECTED_VALUE, ...) { \
	uint##NBITS##_t value; \
	Serialization_bitName bitNames[64]; \
	unsigned int bitCount = vaArgsToBitNames(bitNames, __VA_ARGS__); \
	value = call_virtual(readBitfield##NBITS, context, KEY, bitCount, bitNames); \
	TestCase_assert(value == (EXPECTED_VALUE), "Expected " printfSpecifier_bitfield##NBITS " but got " printfSpecifier_bitfield##NBITS " (status %d)", (EXPECTED_VALUE), value, context->status); \
	TestCase_assert(context->status == SERIALIZATION_ERROR_OK, "Got error from operation that should have succeeded: %d", context->status); \
}

#define verifyReadNextDictionaryKey(KEY) \
	key = context->vtable->readNextDictionaryKey(context); \
	TestCase_assert(key != NULL, "Expected non-NULL but got NULL (status %d)", context->status); \
	TestCase_assert(!strcmp(key, (KEY)), "Expected \"%s\" but got \"%s\"", (KEY), key); \
	TestCase_assert(context->status == SERIALIZATION_ERROR_OK, "Got error from operation that should have succeeded: %d", context->status);

#define verifyHasDictionaryKey(KEY, HAS_KEY) \
	TestCase_assert(context->vtable->hasDictionaryKey(context, (KEY)) == (HAS_KEY), "Expected %s but got %s (status %d)", (HAS_KEY) ? "true" : "false", (HAS_KEY) ? "false" : "true", context->status); \
	TestCase_assert(context->status == SERIALIZATION_ERROR_OK, "Got error from operation that should have succeeded: %d", context->status);

static void testNumberValues() {
	BinaryDeserializationContext * context;
	
	context = BinaryDeserializationContext_createWithBytes(
		"Stem"
		"\x00\x00\x00\x0B"
		"\x00"
		"\x01"
		"\x00\x02"
		"\x00\x03"
		"\x00\x00\x00\x04"
		"\x00\x00\x00\x05"
		"\x00\x00\x00\x00\x00\x00\x00\x06"
		"\x00\x00\x00\x00\x00\x00\x00\x07"
		"\x41\x00\x00\x00"
		"\x40\x22\x00\x00\x00\x00\x00\x00"
		"\x00\x0A\x00\x00",
		54
	);
	TestCase_assert(context != NULL, "Expected non-NULL but got NULL");
	if (context == NULL) {return;} // Suppress clang warning
	beginAndVerifyArray("", 11)
	readAndVerifyNumber(int8_t, "", 0)
	readAndVerifyNumber(uint8_t, "", 1)
	readAndVerifyNumber(int16_t, "", 2)
	readAndVerifyNumber(uint16_t, "", 3)
	readAndVerifyNumber(int32_t, "", 4)
	readAndVerifyNumber(uint32_t, "", 5)
	readAndVerifyNumber(int64_t, "", 6)
	readAndVerifyNumber(uint64_t, "", 7)
	readAndVerifyNumber(float, "", 8)
	readAndVerifyNumber(double, "", 9)
	readAndVerifyNumber(fixed16_16, "", 0xA0000)
	context->vtable->endArray(context);
	context->vtable->dispose(context);
	
	context = BinaryDeserializationContext_createWithBytes(
		"metS"
		"\x0B\x00\x00\x00"
		"\x00"
		"\x01"
		"\x02\x00"
		"\x03\x00"
		"\x04\x00\x00\x00"
		"\x05\x00\x00\x00"
		"\x06\x00\x00\x00\x00\x00\x00\x00"
		"\x07\x00\x00\x00\x00\x00\x00\x00"
		"\x00\x00\x00\x41"
		"\x00\x00\x00\x00\x00\x00\x22\x40"
		"\x00\x00\x0A\x00",
		54
	);
	TestCase_assert(context != NULL, "Expected non-NULL but got NULL");
	if (context == NULL) {return;} // Suppress clang warning
	beginAndVerifyArray("", 11)
	readAndVerifyNumber(int8_t, "", 0)
	readAndVerifyNumber(uint8_t, "", 1)
	readAndVerifyNumber(int16_t, "", 2)
	readAndVerifyNumber(uint16_t, "", 3)
	readAndVerifyNumber(int32_t, "", 4)
	readAndVerifyNumber(uint32_t, "", 5)
	readAndVerifyNumber(int64_t, "", 6)
	readAndVerifyNumber(uint64_t, "", 7)
	readAndVerifyNumber(float, "", 8)
	readAndVerifyNumber(double, "", 9)
	readAndVerifyNumber(fixed16_16, "", 0xA0000)
	context->vtable->endArray(context);
	context->vtable->dispose(context);
	
	context = BinaryDeserializationContext_createWithBytes(
		"Stem"
		"\x00\x00\x00\x0B"
		"\x80"
		"\xFF"
		"\x80\x00"
		"\xFF\xFF"
		"\x80\x00\x00\x00"
		"\xFF\xFF\xFF\xFF"
		"\x80\x00\x00\x00\x00\x00\x00\x00"
		"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
		"\x7F\x7F\xFF\xFF"
		"\x7F\xEF\xFF\xFF\xFF\xFF\xFF\xFF"
		"\x80\x00\x00\x02",
		54
	);
	TestCase_assert(context != NULL, "Expected non-NULL but got NULL");
	if (context == NULL) {return;} // Suppress clang warning
	beginAndVerifyArray("", 11)
	readAndVerifyNumber(int8_t, "", INT8_MIN)
	readAndVerifyNumber(uint8_t, "", UINT8_MAX)
	readAndVerifyNumber(int16_t, "", INT16_MIN)
	readAndVerifyNumber(uint16_t, "", UINT16_MAX)
	readAndVerifyNumber(int32_t, "", INT32_MIN)
	readAndVerifyNumber(uint32_t, "", UINT32_MAX)
	readAndVerifyNumber(int64_t, "", INT64_MIN)
	readAndVerifyNumber(uint64_t, "", UINT64_MAX)
	readAndVerifyNumber(float, "", FLT_MAX)
	readAndVerifyNumber(double, "", DBL_MAX)
	readAndVerifyNumber(fixed16_16, "", FIXED_16_16_MIN)
	context->vtable->endArray(context);
	context->vtable->dispose(context);
	
	context = BinaryDeserializationContext_createWithBytes(
		"metS"
		"\x0B\x00\x00\x00"
		"\x80"
		"\xFF"
		"\x00\x80"
		"\xFF\xFF"
		"\x00\x00\x00\x80"
		"\xFF\xFF\xFF\xFF"
		"\x00\x00\x00\x00\x00\x00\x00\x80"
		"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
		"\xFF\xFF\x7F\x7F"
		"\xFF\xFF\xFF\xFF\xFF\xFF\xEF\x7F"
		"\x02\x00\x00\x80",
		54
	);
	TestCase_assert(context != NULL, "Expected non-NULL but got NULL");
	if (context == NULL) {return;} // Suppress clang warning
	beginAndVerifyArray("", 11)
	readAndVerifyNumber(int8_t, "", INT8_MIN)
	readAndVerifyNumber(uint8_t, "", UINT8_MAX)
	readAndVerifyNumber(int16_t, "", INT16_MIN)
	readAndVerifyNumber(uint16_t, "", UINT16_MAX)
	readAndVerifyNumber(int32_t, "", INT32_MIN)
	readAndVerifyNumber(uint32_t, "", UINT32_MAX)
	readAndVerifyNumber(int64_t, "", INT64_MIN)
	readAndVerifyNumber(uint64_t, "", UINT64_MAX)
	readAndVerifyNumber(float, "", FLT_MAX)
	readAndVerifyNumber(double, "", DBL_MAX)
	readAndVerifyNumber(fixed16_16, "", FIXED_16_16_MIN)
	context->vtable->endArray(context);
	context->vtable->dispose(context);
}

static void testStringValues() {
	BinaryDeserializationContext * context;
	
	context = BinaryDeserializationContext_createWithBytes(
		"Stem"
		"\x00\x00\x00\x05"
		"foo\x00"
		"Hello, world!\x00"
		"\x01null\x00"
		"\x00"
		"\x01\x00",
		35
	);
	TestCase_assert(context != NULL, "Expected non-NULL but got NULL");
	if (context == NULL) {return;} // Suppress clang warning
	beginAndVerifyArray("", 5)
	readAndVerifyString("", "foo")
	readAndVerifyString("", "Hello, world!")
	readAndVerifyStringNullable("", "null")
	readAndVerifyStringNullable("", NULL)
	readAndVerifyStringNullable("", "")
	context->vtable->endArray(context);
	context->vtable->dispose(context);
}

static void testBlobValues() {
	BinaryDeserializationContext * context;
	
	context = BinaryDeserializationContext_createWithBytes(
		"Stem"
		"\x00\x00\x00\x03"
		"\x00\x00\x00\x03" "foo"
		"\x00\x00\x00\x0D" "Hello, world!"
		"\xFF\xFF\xFF\xFF",
		36
	);
	TestCase_assert(context != NULL, "Expected non-NULL but got NULL");
	if (context == NULL) {return;} // Suppress clang warning
	beginAndVerifyArray("", 3)
	readAndVerifyBlob("", "foo", 3)
	readAndVerifyBlob("", "Hello, world!", 13)
	readAndVerifyBlob("", NULL, 0)
	context->vtable->endArray(context);
	context->vtable->dispose(context);
	
	context = BinaryDeserializationContext_createWithBytes(
		"metS"
		"\x03\x00\x00\x00"
		"\x03\x00\x00\x00" "foo"
		"\x0D\x00\x00\x00" "Hello, world!"
		"\xFF\xFF\xFF\xFF",
		36
	);
	TestCase_assert(context != NULL, "Expected non-NULL but got NULL");
	if (context == NULL) {return;} // Suppress clang warning
	beginAndVerifyArray("", 3)
	readAndVerifyBlob("", "foo", 3)
	readAndVerifyBlob("", "Hello, world!", 13)
	readAndVerifyBlob("", NULL, 0)
	context->vtable->endArray(context);
	context->vtable->dispose(context);
}

static void testBooleanValues() {
	BinaryDeserializationContext * context;
	
	context = BinaryDeserializationContext_createWithBytes(
		"Stem"
		"\x00\x00\x00\x02"
		"\x00"
		"\x01",
		10
	);
	TestCase_assert(context != NULL, "Expected non-NULL but got NULL");
	if (context == NULL) {return;} // Suppress clang warning
	beginAndVerifyArray("", 2)
	readAndVerifyNumber(bool, "", false)
	readAndVerifyNumber(bool, "", true)
	context->vtable->endArray(context);
	context->vtable->dispose(context);
}

#define ENUM_TEST_0 0
#define ENUM_TEST_1 1
#define ENUM_TEST_zero 0
#define ENUM_TEST_one 1
#define ENUM_TEST_two 2

static void testEnumerations() {
	BinaryDeserializationContext * context;
	
	context = BinaryDeserializationContext_createWithBytes(
		"Stem"
		"\x00\x00\x00\x05"
		"\x00\x00\x00\x00"
		"\x00\x00\x00\x01"
		"\x00\x00\x00\x00"
		"\x00\x00\x00\x01"
		"\x00\x00\x00\x02",
		28
	);
	TestCase_assert(context != NULL, "Expected non-NULL but got NULL");
	if (context == NULL) {return;} // Suppress clang warning
	beginAndVerifyArray("", 5)
	readAndVerifyEnumeration(int, "", ENUM_TEST_0, enumKV(ENUM_TEST_0), enumKV(ENUM_TEST_1), NULL)
	readAndVerifyEnumeration(int, "", ENUM_TEST_1, enumKV(ENUM_TEST_0), enumKV(ENUM_TEST_1), NULL)
	readAndVerifyEnumeration(int, "", ENUM_TEST_zero, enumKV(ENUM_TEST_zero), enumKV(ENUM_TEST_one), enumKV(ENUM_TEST_two), NULL)
	readAndVerifyEnumeration(int, "", ENUM_TEST_one, enumKV(ENUM_TEST_zero), enumKV(ENUM_TEST_one), enumKV(ENUM_TEST_two), NULL)
	readAndVerifyEnumeration(int, "", ENUM_TEST_two, enumKV(ENUM_TEST_zero), enumKV(ENUM_TEST_one), enumKV(ENUM_TEST_two), NULL)
	context->vtable->endArray(context);
	context->vtable->dispose(context);
	
	context = BinaryDeserializationContext_createWithBytes(
		"metS"
		"\x05\x00\x00\x00"
		"\x00\x00\x00\x00"
		"\x01\x00\x00\x00"
		"\x00\x00\x00\x00"
		"\x01\x00\x00\x00"
		"\x02\x00\x00\x00",
		28
	);
	TestCase_assert(context != NULL, "Expected non-NULL but got NULL");
	if (context == NULL) {return;} // Suppress clang warning
	beginAndVerifyArray("", 5)
	readAndVerifyEnumeration(int, "", ENUM_TEST_0, enumKV(ENUM_TEST_0), enumKV(ENUM_TEST_1), NULL)
	readAndVerifyEnumeration(int, "", ENUM_TEST_1, enumKV(ENUM_TEST_0), enumKV(ENUM_TEST_1), NULL)
	readAndVerifyEnumeration(int, "", ENUM_TEST_zero, enumKV(ENUM_TEST_zero), enumKV(ENUM_TEST_one), enumKV(ENUM_TEST_two), NULL)
	readAndVerifyEnumeration(int, "", ENUM_TEST_one, enumKV(ENUM_TEST_zero), enumKV(ENUM_TEST_one), enumKV(ENUM_TEST_two), NULL)
	readAndVerifyEnumeration(int, "", ENUM_TEST_two, enumKV(ENUM_TEST_zero), enumKV(ENUM_TEST_one), enumKV(ENUM_TEST_two), NULL)
	context->vtable->endArray(context);
	context->vtable->dispose(context);
	
	context = BinaryDeserializationContext_createWithBytes(
		"Stem"
		"\x00\x00\x00\x05"
		"\x00"
		"\x00\x01"
		"\x00\x00\x00\x00"
		"\x01"
		"\x00\x02",
		18
	);
	TestCase_assert(context != NULL, "Expected non-NULL but got NULL");
	if (context == NULL) {return;} // Suppress clang warning
	beginAndVerifyArray("", 5)
	readAndVerifyEnumeration(int8_t, "", ENUM_TEST_0, enumKV(ENUM_TEST_0), enumKV(ENUM_TEST_1), NULL)
	readAndVerifyEnumeration(int16_t, "", ENUM_TEST_1, enumKV(ENUM_TEST_0), enumKV(ENUM_TEST_1), NULL)
	readAndVerifyEnumeration(int32_t, "", ENUM_TEST_zero, enumKV(ENUM_TEST_zero), enumKV(ENUM_TEST_one), enumKV(ENUM_TEST_two), NULL)
	readAndVerifyEnumeration(int8_t, "", ENUM_TEST_one, enumKV(ENUM_TEST_zero), enumKV(ENUM_TEST_one), enumKV(ENUM_TEST_two), NULL)
	readAndVerifyEnumeration(int16_t, "", ENUM_TEST_two, enumKV(ENUM_TEST_zero), enumKV(ENUM_TEST_one), enumKV(ENUM_TEST_two), NULL)
	context->vtable->endArray(context);
	context->vtable->dispose(context);
	
	context = BinaryDeserializationContext_createWithBytes(
		"metS"
		"\x05\x00\x00\x00"
		"\x00"
		"\x01\x00"
		"\x00\x00\x00\x00"
		"\x01"
		"\x02\x00",
		18
	);
	TestCase_assert(context != NULL, "Expected non-NULL but got NULL");
	if (context == NULL) {return;} // Suppress clang warning
	beginAndVerifyArray("", 5)
	readAndVerifyEnumeration(int8_t, "", ENUM_TEST_0, enumKV(ENUM_TEST_0), enumKV(ENUM_TEST_1), NULL)
	readAndVerifyEnumeration(int16_t, "", ENUM_TEST_1, enumKV(ENUM_TEST_0), enumKV(ENUM_TEST_1), NULL)
	readAndVerifyEnumeration(int32_t, "", ENUM_TEST_zero, enumKV(ENUM_TEST_zero), enumKV(ENUM_TEST_one), enumKV(ENUM_TEST_two), NULL)
	readAndVerifyEnumeration(int8_t, "", ENUM_TEST_one, enumKV(ENUM_TEST_zero), enumKV(ENUM_TEST_one), enumKV(ENUM_TEST_two), NULL)
	readAndVerifyEnumeration(int16_t, "", ENUM_TEST_two, enumKV(ENUM_TEST_zero), enumKV(ENUM_TEST_one), enumKV(ENUM_TEST_two), NULL)
	context->vtable->endArray(context);
	context->vtable->dispose(context);
}

static void testBitfields() {
	BinaryDeserializationContext * context;
	
	context = BinaryDeserializationContext_createWithBytes(
		"Stem"
		"\x00\x00\x00\x0C"
		"\xAA"
		"\x57"
		"\xF0\x01"
		"\x00\x0F"
		"\xF0\x00\x00\x01"
		"\x00\x00\x00\x1E"
		"\xF0\x00\x00\x00\x00\x00\x00\x01"
		"\x00\x00\x00\x00\x00\x00\x00\x3C"
		"\x00"
		"\x00\x00"
		"\x00\x00\x00\x00"
		"\x00\x00\x00\x00\x00\x00\x00\x00",
		53
	);
	TestCase_assert(context != NULL, "Expected non-NULL but got NULL");
	if (context == NULL) {return;} // Suppress clang warning
	beginAndVerifyArray("", 12)
	readAndVerifyBitfield(8, "", 0xAA, "bit_0", "bit_1", "bit_2", "bit_3", "bit_4", "bit_5", "bit_6", "bit_7", NULL);
	readAndVerifyBitfield(8, "", 0x57, "bit0", "bit1", "bit2", "bit3", "bit4", "bit5", "bit6", NULL);
	readAndVerifyBitfield(16, "", 0xF001, "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", NULL);
	readAndVerifyBitfield(16, "", 0x000F, "b0", "b1", "b2", "b3", "b4", "b5", NULL);
	readAndVerifyBitfield(32, "", 0xF0000001, "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1A", "1B", "1C", "1D", "1E", "1F", NULL);
	readAndVerifyBitfield(32, "", 0x0000001E, "b0", "b1", "b2", "b3", "b4", "b5", NULL);
	readAndVerifyBitfield(64, "", (uint64_t) 0xF000000000000001ull, "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1A", "1B", "1C", "1D", "1E", "1F", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B", "3C", "3D", "3E", "3F", NULL);
	readAndVerifyBitfield(64, "", (uint64_t) 0x000000000000003Cull, "b0", "b1", "b2", "b3", "b4", "b5", NULL);
	readAndVerifyBitfield(8, "", 0x00, NULL);
	readAndVerifyBitfield(16, "", 0x0000, NULL);
	readAndVerifyBitfield(32, "", 0x00000000, NULL);
	readAndVerifyBitfield(64, "", (uint64_t) 0x0000000000000000ull, NULL);
	context->vtable->endArray(context);
	context->vtable->dispose(context);
	
	context = BinaryDeserializationContext_createWithBytes(
		"metS"
		"\x0C\x00\x00\x00"
		"\xAA"
		"\x57"
		"\x01\xF0"
		"\x0F\x00"
		"\x01\x00\x00\xF0"
		"\x1E\x00\x00\x00"
		"\x01\x00\x00\x00\x00\x00\x00\xF0"
		"\x3C\x00\x00\x00\x00\x00\x00\x00"
		"\x00"
		"\x00\x00"
		"\x00\x00\x00\x00"
		"\x00\x00\x00\x00\x00\x00\x00\x00",
		53
	);
	TestCase_assert(context != NULL, "Expected non-NULL but got NULL");
	if (context == NULL) {return;} // Suppress clang warning
	beginAndVerifyArray("", 12)
	readAndVerifyBitfield(8, "", 0xAA, "bit_0", "bit_1", "bit_2", "bit_3", "bit_4", "bit_5", "bit_6", "bit_7", NULL);
	readAndVerifyBitfield(8, "", 0x57, "bit0", "bit1", "bit2", "bit3", "bit4", "bit5", "bit6", NULL);
	readAndVerifyBitfield(16, "", 0xF001, "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", NULL);
	readAndVerifyBitfield(16, "", 0x000F, "b0", "b1", "b2", "b3", "b4", "b5", NULL);
	readAndVerifyBitfield(32, "", 0xF0000001, "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1A", "1B", "1C", "1D", "1E", "1F", NULL);
	readAndVerifyBitfield(32, "", 0x0000001E, "b0", "b1", "b2", "b3", "b4", "b5", NULL);
	readAndVerifyBitfield(64, "", (uint64_t) 0xF000000000000001ull, "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1A", "1B", "1C", "1D", "1E", "1F", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B", "3C", "3D", "3E", "3F", NULL);
	readAndVerifyBitfield(64, "", (uint64_t) 0x000000000000003Cull, "b0", "b1", "b2", "b3", "b4", "b5", NULL);
	readAndVerifyBitfield(8, "", 0x00, NULL);
	readAndVerifyBitfield(16, "", 0x0000, NULL);
	readAndVerifyBitfield(32, "", 0x00000000, NULL);
	readAndVerifyBitfield(64, "", (uint64_t) 0x0000000000000000ull, NULL);
	context->vtable->endArray(context);
	context->vtable->dispose(context);
}

static void testArrays() {
	BinaryDeserializationContext * context;
	
	context = BinaryDeserializationContext_createWithBytes(
		"Stem"
		"\x00\x00\x00\x02"
		"\x00\x00\x00\x01"
		"\xFF"
		"\x00\x00\x00\x0F"
		"\x00"
		"\x01"
		"\x00\x00\x00\x06"
		"\x00\x02"
		"\x00\x03"
		"\x00\x00\x00\x04"
		"\x00\x00\x00\x05"
		"\x00\x00\x00\x00\x00\x00\x00\x06"
		"\x00\x00\x00\x00\x00\x00\x00\x07"
		"\x41\x00\x00\x00"
		"\x40\x22\x00\x00\x00\x00\x00\x00"
		"\x00\x0A\x00\x00"
		"10\x00"
		"\x00"
		"\x00\x00\x00\x04" "blob"
		"\x01"
		"\x00\x00\x00\x0C"
		"\x01"
		"\x00\x01"
		"\x00\x00\x00\x01"
		"\x00\x00\x00\x00\x00\x00\x00\x01",
		99
	);
	TestCase_assert(context != NULL, "Expected non-NULL but got NULL");
	if (context == NULL) {return;} // Suppress clang warning
	beginAndVerifyArray("", 2)
		beginAndVerifyArray("", 1)
			readAndVerifyNumber(int8_t, "", -1)
		context->vtable->endArray(context);
		beginAndVerifyArray("", 15)
			readAndVerifyNumber(int8_t, "", 0)
			readAndVerifyNumber(uint8_t, "", 1)
			beginAndVerifyArray("", 6)
				readAndVerifyNumber(int16_t, "", 2)
				readAndVerifyNumber(uint16_t, "", 3)
				readAndVerifyNumber(int32_t, "", 4)
				readAndVerifyNumber(uint32_t, "", 5)
				readAndVerifyNumber(int64_t, "", 6)
				readAndVerifyNumber(uint64_t, "", 7)
			context->vtable->endArray(context);
			readAndVerifyNumber(float, "", 8)
			readAndVerifyNumber(double, "", 9)
			readAndVerifyNumber(fixed16_16, "", 0xA0000)
			readAndVerifyString("", "10")
			readAndVerifyStringNullable("", NULL)
			readAndVerifyBlob("", "blob", 4)
			readAndVerifyNumber(bool, "", true)
			readAndVerifyEnumeration(int, "", 12, "enum", 12, NULL)
			readAndVerifyBitfield(8, "", 0x01, "13", NULL)
			readAndVerifyBitfield(16, "", 0x0001, "14", NULL)
			readAndVerifyBitfield(32, "", 0x00000001, "15", NULL)
			readAndVerifyBitfield(64, "", (uint64_t) 0x0000000000000001ull, "16", NULL)
		context->vtable->endArray(context);
	context->vtable->endArray(context);
	context->vtable->dispose(context);
}

static void testStructures() {
	BinaryDeserializationContext * context;
	
	context = BinaryDeserializationContext_createWithBytes(
		"Stem"
		"\xFF"
		"\x00"
		"\x01"
		"\x00\x02"
		"\x00\x03"
		"\x00\x00\x00\x04"
		"\x00\x00\x00\x05"
		"\x00\x00\x00\x00\x00\x00\x00\x06"
		"\x00\x00\x00\x00\x00\x00\x00\x07"
		"\x41\x00\x00\x00"
		"\x40\x22\x00\x00\x00\x00\x00\x00"
		"\x00\x0A\x00\x00"
		"10\x00"
		"\x00"
		"\x01"
		"\x00\x00\x00\x0C"
		"\x01"
		"\x00\x01"
		"\x00\x00\x00\x01"
		"\x00\x00\x00\x00\x00\x00\x00\x01",
		77
	);
	TestCase_assert(context != NULL, "Expected non-NULL but got NULL");
	if (context == NULL) {return;} // Suppress clang warning
	context->vtable->beginStructure(context, "");
		context->vtable->beginStructure(context, "a");
			readAndVerifyNumber(int8_t, "b", -1)
		context->vtable->endStructure(context);
		context->vtable->beginStructure(context, "c");
			readAndVerifyNumber(int8_t, "d", 0)
			readAndVerifyNumber(uint8_t, "e", 1)
			context->vtable->beginStructure(context, "f");
				readAndVerifyNumber(int16_t, "g", 2)
				readAndVerifyNumber(uint16_t, "h", 3)
				readAndVerifyNumber(int32_t, "i", 4)
				readAndVerifyNumber(uint32_t, "j", 5)
				readAndVerifyNumber(int64_t, "k", 6)
				readAndVerifyNumber(uint64_t, "l", 7)
			context->vtable->endStructure(context);
			readAndVerifyNumber(float, "m", 8)
			readAndVerifyNumber(double, "n", 9)
			readAndVerifyNumber(fixed16_16, "v", 0xA0000)
			readAndVerifyString("o", "10")
			readAndVerifyStringNullable("w", NULL)
			readAndVerifyNumber(bool, "p", true)
			readAndVerifyEnumeration(int, "q", 12, "enum", 12, NULL)
			readAndVerifyBitfield(8, "r", 0x01, "13", NULL)
			readAndVerifyBitfield(16, "s", 0x0001, "14", NULL)
			readAndVerifyBitfield(32, "t", 0x00000001, "15", NULL)
			readAndVerifyBitfield(64, "u", (uint64_t) 0x0000000000000001ull, "16", NULL)
		context->vtable->endStructure(context);
	context->vtable->endStructure(context);
	context->vtable->dispose(context);
}

static void testDictionaries() {
	BinaryDeserializationContext * context;
	const char * key;
	
	context = BinaryDeserializationContext_createWithBytes(
		"Stem"
		"\x00\x00\x00\x02" // top count
		"\x00\x00\x00\xF1" // top end offset
		"a\x00"
		"\x00\x00\x00\x14" // a offset from top start
		"c\x00"
		"\x00\x00\x00\x23" // c offset from top start
		"\x00\x00\x00\x01" // a count
		"\x00\x00\x00\x0F" // a end offset
		"b\x00"
		"\x00\x00\x00\x0E" // b offset from a start
		"\xFF" // b
		// a end
		"\x00\x00\x00\x0E" // c count
		"\x00\x00\x00\xCC" // c end offset
		"d\x00"
		"\x00\x00\x00\x5C" // d offset from c start
		"e\x00"
		"\x00\x00\x00\x5D" // e offset from c start
		"f\x00"
		"\x00\x00\x00\x5E" // f offset from c start
		"m\x00"
		"\x00\x00\x00\xA6" // m offset from c start
		"n\x00"
		"\x00\x00\x00\xAA" // n offset from c start
		"o\x00"
		"\x00\x00\x00\xB2" // o offset from c start
		"p\x00"
		"\x00\x00\x00\xB5" // p offset from c start
		"q\x00"
		"\x00\x00\x00\xB6" // q offset from c start
		"r\x00"
		"\x00\x00\x00\xBA" // r offset from c start
		"s\x00"
		"\x00\x00\x00\xBB" // s offset from c start
		"t\x00"
		"\x00\x00\x00\xBD" // t offset from c start
		"u\x00"
		"\x00\x00\x00\xC1" // u offset from c start
		"v\x00"
		"\x00\x00\x00\xC9" // v offset from c start
		"w\x00"
		"\x00\x00\x00\xCC" // w offset from c start
		"\x00" // d
		"\x01" // e
		"\x00\x00\x00\x06" // f count
		"\x00\x00\x00\x48" // f end offset
		"g\x00"
		"\x00\x00\x00\x2C" // g offset from f start
		"h\x00"
		"\x00\x00\x00\x2E" // h offset from f start
		"i\x00"
		"\x00\x00\x00\x30" // i offset from f start
		"j\x00"
		"\x00\x00\x00\x34" // j offset from f start
		"k\x00"
		"\x00\x00\x00\x38" // k offset from f start
		"l\x00"
		"\x00\x00\x00\x40" // l offset from f start
		"\x00\x02" // g
		"\x00\x03" // h
		"\x00\x00\x00\x04" // i
		"\x00\x00\x00\x05" // j
		"\x00\x00\x00\x00\x00\x00\x00\x06" // k
		"\x00\x00\x00\x00\x00\x00\x00\x07" // l
		// f end
		"\x41\x00\x00\x00" // m
		"\x40\x22\x00\x00\x00\x00\x00\x00" // n
		"10\x00" // o
		"\x01" // p
		"\x00\x00\x00\x0C" // q
		"\x01" // r
		"\x00\x01" // s
		"\x00\x00\x00\x01" // t
		"\x00\x00\x00\x00\x00\x00\x00\x01" // u
		"\x00\x0A\x00\x00" // v
		"\x00", // w
		// c end
		// top end
		245
	);
	TestCase_assert(context != NULL, "Expected non-NULL but got NULL");
	if (context == NULL) {return;} // Suppress clang warning
	beginAndVerifyDictionary("", 2)
		beginAndVerifyDictionary("a", 1)
			readAndVerifyNumber(int8_t, "b", -1)
		context->vtable->endDictionary(context);
		beginAndVerifyDictionary("c", 14)
			readAndVerifyNumber(int8_t, "d", 0)
			readAndVerifyNumber(uint8_t, "e", 1)
			beginAndVerifyDictionary("f", 6)
				readAndVerifyNumber(int16_t, "g", 2)
				readAndVerifyNumber(uint16_t, "h", 3)
				readAndVerifyNumber(int32_t, "i", 4)
				readAndVerifyNumber(uint32_t, "j", 5)
				readAndVerifyNumber(int64_t, "k", 6)
				readAndVerifyNumber(uint64_t, "l", 7)
			context->vtable->endDictionary(context);
			readAndVerifyNumber(float, "m", 8)
			readAndVerifyNumber(double, "n", 9)
			readAndVerifyString("o", "10")
			readAndVerifyNumber(bool, "p", true)
			readAndVerifyEnumeration(int, "q", 12, "enum", 12, NULL)
			readAndVerifyBitfield(8, "r", 0x01, "13", NULL)
			readAndVerifyBitfield(16, "s", 0x0001, "14", NULL)
			readAndVerifyBitfield(32, "t", 0x00000001, "15", NULL)
			readAndVerifyBitfield(64, "u", (uint64_t) 0x0000000000000001ull, "16", NULL)
			readAndVerifyNumber(fixed16_16, "v", 0xA0000)
			readAndVerifyStringNullable("w", NULL)
		context->vtable->endDictionary(context);
	context->vtable->endDictionary(context);
	context->vtable->dispose(context);
	
	context = BinaryDeserializationContext_createWithBytes(
		"Stem"
		"\x00\x00\x00\x02" // top count
		"\x00\x00\x00\xF9" // top end offset
		"c\x00"
		"\x00\x00\x00\x14" // c offset from top start
		"a\x00"
		"\x00\x00\x00\xE2" // a offset from top start
		"\x00\x00\x00\x0E" // c count
		"\x00\x00\x00\xCC" // c end offset
		"w\x00"
		"\x00\x00\x00\x5C" // w offset from c start
		"v\x00"
		"\x00\x00\x00\x5D" // v offset from c start
		"u\x00"
		"\x00\x00\x00\x61" // u offset from c start
		"t\x00"
		"\x00\x00\x00\x69" // t offset from c start
		"s\x00"
		"\x00\x00\x00\x6D" // s offset from c start
		"r\x00"
		"\x00\x00\x00\x6F" // r offset from c start
		"q\x00"
		"\x00\x00\x00\x70" // q offset from c start
		"p\x00"
		"\x00\x00\x00\x74" // p offset from c start
		"o\x00"
		"\x00\x00\x00\x75" // o offset from c start
		"n\x00"
		"\x00\x00\x00\x78" // n offset from c start
		"m\x00"
		"\x00\x00\x00\x80" // m offset from c start
		"f\x00"
		"\x00\x00\x00\x84" // f offset from c start
		"e\x00"
		"\x00\x00\x00\xCC" // e offset from c start
		"d\x00"
		"\x00\x00\x00\xCD" // d offset from c start
		"\x00" // w
		"\x00\x0A\x00\x00" // v
		"\x00\x00\x00\x00\x00\x00\x00\x01" // u
		"\x00\x00\x00\x01" // t
		"\x00\x01" // s
		"\x01" // r
		"\x00\x00\x00\x0C" // q
		"\x01" // p
		"10\x00" // o
		"\x40\x22\x00\x00\x00\x00\x00\x00" // n
		"\x41\x00\x00\x00" // m
		"\x00\x00\x00\x06" // f count
		"\x00\x00\x00\x48" // f end offset
		"l\x00"
		"\x00\x00\x00\x2C" //! l offset from f start
		"k\x00"
		"\x00\x00\x00\x34" //! k offset from f start
		"j\x00"
		"\x00\x00\x00\x3C" //! j offset from f start
		"i\x00"
		"\x00\x00\x00\x40" //! i offset from f start
		"h\x00"
		"\x00\x00\x00\x44" //! h offset from f start
		"g\x00"
		"\x00\x00\x00\x46" //! g offset from f start
		"\x00\x00\x00\x00\x00\x00\x00\x07" // l
		"\x00\x00\x00\x00\x00\x00\x00\x06" // k
		"\x00\x00\x00\x05" // j
		"\x00\x00\x00\x04" // i
		"\x00\x03" // h
		"\x00\x02" // g
		// f end
		"\x01" // e
		"\x00" // d
		// c end
		"\x00\x00\x00\x02" // a count
		"\x00\x00\x00\x17" // a end offset
		"b2\x00"
		"\x00\x00\x00\x15" //! b2 offset from a start
		"b\x00"
		"\x00\x00\x00\x16" //! b offset from a start
		"\xF6" // b2
		"\xFF", // b
		// a end
		// top end
		253
	);
	TestCase_assert(context != NULL, "Expected non-NULL but got NULL");
	if (context == NULL) {return;} // Suppress clang warning
	beginAndVerifyDictionary("", 2)
		beginAndVerifyDictionary("a", 2)
			readAndVerifyNumber(int8_t, "b", -1)
			readAndVerifyNumber(int8_t, "b2", -10)
		context->vtable->endDictionary(context);
		beginAndVerifyDictionary("c", 14)
			readAndVerifyNumber(int8_t, "d", 0)
			readAndVerifyNumber(uint8_t, "e", 1)
			beginAndVerifyDictionary("f", 6)
				readAndVerifyNumber(int16_t, "g", 2)
				readAndVerifyNumber(uint16_t, "h", 3)
				readAndVerifyNumber(int32_t, "i", 4)
				readAndVerifyNumber(uint32_t, "j", 5)
				readAndVerifyNumber(int64_t, "k", 6)
				readAndVerifyNumber(uint64_t, "l", 7)
			context->vtable->endDictionary(context);
			readAndVerifyNumber(float, "m", 8)
			readAndVerifyNumber(double, "n", 9)
			readAndVerifyString("o", "10")
			readAndVerifyNumber(bool, "p", true)
			readAndVerifyEnumeration(int, "q", 12, "enum", 12, NULL)
			readAndVerifyBitfield(8, "r", 0x01, "13", NULL)
			readAndVerifyBitfield(16, "s", 0x0001, "14", NULL)
			readAndVerifyBitfield(32, "t", 0x00000001, "15", NULL)
			readAndVerifyBitfield(64, "u", (uint64_t) 0x0000000000000001ull, "16", NULL)
			readAndVerifyNumber(fixed16_16, "v", 0xA0000)
			readAndVerifyStringNullable("w", NULL)
		context->vtable->endDictionary(context);
	context->vtable->endDictionary(context);
	context->vtable->dispose(context);
	
	context = BinaryDeserializationContext_createWithBytes(
		"Stem"
		"\x00\x00\x00\x03" // top start
		"\x00\x00\x00\x67" // top end offset
		"foo\x00"
		"\x00\x00\x00\x20" // foo 1 offset from top start
		"foo\x00"
		"\x00\x00\x00\x43" // foo 2 offset from top start
		"bar\x00"
		"\x00\x00\x00\x44" // bar 1 offset from top start
		"\x00\x00\x00\x03" // foo 1 start
		"\x00\x00\x00\x23" // foo 1 end offset
		"bar\x00"
		"\x00\x00\x00\x20" // bar 2 offset from foo 1 start
		"baz\x00"
		"\x00\x00\x00\x21" // baz 1 offset from foo 1 start
		"bar\x00"
		"\x00\x00\x00\x22" // bar 3 offset from foo 1 start
		"\x00" // bar 2
		"\x01" // baz 1
		"\x02" // bar 3
		// foo 1 end
		"\x03" // foo 2
		"\x00\x00\x00\x03" // bar 1 start
		"\x00\x00\x00\x23" // bar 1 end offset
		"foo\x00"
		"\x00\x00\x00\x20" // foo 3 offset from bar 1 start
		"baz\x00"
		"\x00\x00\x00\x21" // baz 2 offset from bar 1 start
		"baz\x00"
		"\x00\x00\x00\x22" // baz 3 offset from bar 1 start
		"\x04"
		"\x05"
		"\x06",
		// bar 1 end
		// top end
		111
	);
	TestCase_assert(context != NULL, "Expected non-NULL but got NULL");
	if (context == NULL) {return;} // Suppress clang warning
	beginAndVerifyDictionary("", 3)
		verifyHasDictionaryKey("foo", true)
		verifyHasDictionaryKey("bar", true)
		verifyHasDictionaryKey("baz", false)
		verifyReadNextDictionaryKey("foo")
		beginAndVerifyDictionary(key, 3)
			verifyHasDictionaryKey("foo", false)
			verifyHasDictionaryKey("bar", true)
			verifyHasDictionaryKey("baz", true)
			verifyReadNextDictionaryKey("bar")
			readAndVerifyNumber(int8_t, key, 0)
			verifyReadNextDictionaryKey("baz")
			readAndVerifyNumber(int8_t, key, 1)
			verifyReadNextDictionaryKey("bar")
			readAndVerifyNumber(int8_t, key, 2)
		context->vtable->endDictionary(context);
		verifyReadNextDictionaryKey("foo")
		readAndVerifyNumber(int8_t, key, 3)
		verifyReadNextDictionaryKey("bar")
		beginAndVerifyDictionary(key, 3)
			verifyHasDictionaryKey("foo", true)
			verifyHasDictionaryKey("bar", false)
			verifyHasDictionaryKey("baz", true)
			verifyReadNextDictionaryKey("foo")
			readAndVerifyNumber(int8_t, key, 4)
			verifyReadNextDictionaryKey("baz")
			readAndVerifyNumber(int8_t, key, 5)
			verifyReadNextDictionaryKey("baz")
			readAndVerifyNumber(int8_t, key, 6)
		context->vtable->endDictionary(context);
	context->vtable->endDictionary(context);
	context->vtable->dispose(context);
	
	context = BinaryDeserializationContext_createWithBytes(
		"Stem"
		"\x00\x00\x00\x17" // top count
		"\x00\x00\x00\xDA" // top end offset
		"\x00"
		"\x00\x00\x00\x7B" // key 0 offset from top start
		"\x00"
		"\x00\x00\x00\x7C" // key 1 offset from top start
		"\x00"
		"\x00\x00\x00\x7D" // key 2 offset from top start
		"\x00"
		"\x00\x00\x00\x7E" // key 3 offset from top start
		"\x00"
		"\x00\x00\x00\x80" // key 4 offset from top start
		"\x00"
		"\x00\x00\x00\x82" // key 5 offset from top start
		"\x00"
		"\x00\x00\x00\x86" // key 6 offset from top start
		"\x00"
		"\x00\x00\x00\x8A" // key 7 offset from top start
		"\x00"
		"\x00\x00\x00\x92" // key 8 offset from top start
		"\x00"
		"\x00\x00\x00\x9A" // key 9 offset from top start
		"\x00"
		"\x00\x00\x00\x9E" // key 10 offset from top start
		"\x00"
		"\x00\x00\x00\xA6" // key 11 offset from top start
		"\x00"
		"\x00\x00\x00\xA9" // key 12 offset from top start
		"\x00"
		"\x00\x00\x00\xAA" // key 13 offset from top start
		"\x00"
		"\x00\x00\x00\xAE" // key 14 offset from top start
		"\x00"
		"\x00\x00\x00\xAF" // key 15 offset from top start
		"\x00"
		"\x00\x00\x00\xB1" // key 16 offset from top start
		"\x00"
		"\x00\x00\x00\xB5" // key 17 offset from top start
		"\x00"
		"\x00\x00\x00\xBD" // key 18 offset from top start
		"\x00"
		"\x00\x00\x00\xC1" // key 19 offset from top start
		"\x00"
		"\x00\x00\x00\xC1" // key 20 offset from top start
		"\x00"
		"\x00\x00\x00\xD5" // key 21 offset from top start
		"\x00"
		"\x00\x00\x00\xD9" // key 22 offset from top start
		"\xFF" // value 0
		"\x00" // value 1
		"\x01" // value 2
		"\x00\x02" // value 3
		"\x00\x03" // value 4
		"\x00\x00\x00\x04" // value 5
		"\x00\x00\x00\x05" // value 6
		"\x00\x00\x00\x00\x00\x00\x00\x06" // value 7
		"\x00\x00\x00\x00\x00\x00\x00\x07" // value 8
		"\x41\x00\x00\x00" // value 9
		"\x40\x22\x00\x00\x00\x00\x00\x00" // value 10
		"10\x00" // value 11
		"\x01" // value 12
		"\x00\x00\x00\x0C" // value 13
		"\x01" // value 14
		"\x00\x01" // value 15
		"\x00\x00\x00\x01" // value 16
		"\x00\x00\x00\x00\x00\x00\x00\x01" // value 17
		"\x00\x00\x00\x00" // value 18
		// value 19
		"\x00\x00\x00\x02" // value 20 start
		"\x00\x00\x00\x14" // value 20 end offset
		"\x00"
		"\x00\x00\x00\x12" // value 20 key 0 offset from value 20 start
		"\x00"
		"\x00\x00\x00\x13" // value 20 key 1 offset from value 20 start
		"\x00" // value 20 value 0
		"\x00" // value 20 value 1
		// value 20 end
		"\x00\x0A\x00\x00" // value 21 (fixed16_16)
		"\x00", // value 22 (string nullable)
		// top end
		222
	);
	TestCase_assert(context != NULL, "Expected non-NULL but got NULL");
	if (context == NULL) {return;} // Suppress clang warning
	beginAndVerifyDictionary("", 23)
		verifyHasDictionaryKey("", true)
		verifyHasDictionaryKey(" ", false)
		verifyReadNextDictionaryKey("")
		readAndVerifyNumber(int8_t, key, -1)
		verifyReadNextDictionaryKey("")
		readAndVerifyNumber(int8_t, key, 0)
		verifyReadNextDictionaryKey("")
		readAndVerifyNumber(uint8_t, key, 1)
		verifyReadNextDictionaryKey("")
		readAndVerifyNumber(int16_t, key, 2)
		verifyReadNextDictionaryKey("")
		readAndVerifyNumber(uint16_t, key, 3)
		verifyReadNextDictionaryKey("")
		readAndVerifyNumber(int32_t, key, 4)
		verifyReadNextDictionaryKey("")
		readAndVerifyNumber(uint32_t, key, 5)
		verifyReadNextDictionaryKey("")
		readAndVerifyNumber(int64_t, key, 6)
		verifyReadNextDictionaryKey("")
		readAndVerifyNumber(uint64_t, key, 7)
		verifyReadNextDictionaryKey("")
		readAndVerifyNumber(float, key, 8)
		verifyReadNextDictionaryKey("")
		readAndVerifyNumber(double, key, 9)
		verifyReadNextDictionaryKey("")
		readAndVerifyString(key, "10")
		verifyReadNextDictionaryKey("")
		readAndVerifyNumber(bool, key, true)
		verifyReadNextDictionaryKey("")
		readAndVerifyEnumeration(int, key, 12, "enum", 12, NULL)
		verifyReadNextDictionaryKey("")
		readAndVerifyBitfield(8, key, 0x01, "13", NULL)
		verifyReadNextDictionaryKey("")
		readAndVerifyBitfield(16, key, 0x0001, "14", NULL)
		verifyReadNextDictionaryKey("")
		readAndVerifyBitfield(32, key, 0x00000001, "15", NULL)
		verifyReadNextDictionaryKey("")
		readAndVerifyBitfield(64, key, (uint64_t) 0x0000000000000001ull, "16", NULL)
		verifyReadNextDictionaryKey("")
		beginAndVerifyArray(key, 0)
		context->vtable->endArray(context);
		verifyReadNextDictionaryKey("")
		context->vtable->beginStructure(context, key);
		context->vtable->endStructure(context);
		verifyReadNextDictionaryKey("")
		beginAndVerifyDictionary(key, 2)
		context->vtable->endDictionary(context);
		verifyReadNextDictionaryKey("")
		readAndVerifyNumber(fixed16_16, key, 0xA0000)
		verifyReadNextDictionaryKey("")
		readAndVerifyStringNullable(key, NULL)
	context->vtable->endDictionary(context);
	context->vtable->dispose(context);
}

static void testMixedContainers() {
	BinaryDeserializationContext * context;
	
	context = BinaryDeserializationContext_createWithBytes(
		"Stem"
		"\x00\x00\x00\x02"
		"\x00\x00\x00\x00"
		"\x00\x00\x00\x00"
		"\x00\x00\x00\x08"
		"\x00\x00\x00\x02"
		"\x00\x00\x00\x2C"
		"innerArray2\x00"
		"\x00\x00\x00\x28"
		"innerStruct\x00"
		"\x00\x00\x00\x2C"
		"\x00\x00\x00\x00",
		64
	);
	TestCase_assert(context != NULL, "Expected non-NULL but got NULL");
	if (context == NULL) {return;} // Suppress clang warning
	beginAndVerifyArray("outerArray", 2)
		context->vtable->beginStructure(context, "outerStruct");
			beginAndVerifyArray("innerArray", 0)
			context->vtable->endArray(context);
			beginAndVerifyDictionary("innerDict", 0)
			context->vtable->endDictionary(context);
		context->vtable->endStructure(context);
		beginAndVerifyDictionary("outerDict", 2)
			beginAndVerifyArray("innerArray2", 0)
			context->vtable->endArray(context);
			context->vtable->beginStructure(context, "innerStruct");
			context->vtable->endStructure(context);
		context->vtable->endDictionary(context);
	context->vtable->endArray(context);
	context->vtable->dispose(context);
}

#define performActionWithTemporaryFile(STRING, LENGTH, ACTION) { \
	const char * tempFilePath; \
	int fd; \
	\
	tempFilePath = temporaryFilePath("tmpXXXXXX", &fd); \
	writeFileSimple(tempFilePath, STRING, LENGTH); \
	ACTION(tempFilePath) \
	close(fd); \
	unlink(tempFilePath); \
}

#define initContextWithFile(FILE_PATH) \
	stemobject_assign_vtable(context2, BinaryDeserializationContext); \
	BinaryDeserializationContext_initWithFile(&context2, FILE_PATH); \
	context = &context2;
#define initContextFromTemporaryFile(STRING, LENGTH) performActionWithTemporaryFile(STRING, LENGTH, initContextWithFile)
#define createContextWithFile(FILE_PATH) context = BinaryDeserializationContext_createWithFile(tempFilePath);
#define createContextFromTemporaryFile(STRING, LENGTH) performActionWithTemporaryFile(STRING, LENGTH, initContextWithFile)

static void testInitWithFile() {
	BinaryDeserializationContext * context, context2;
	
	initContextFromTemporaryFile("Stem\x00\x00\x00\x01" "foo\x00", 12)
	TestCase_assert(context->status == SERIALIZATION_ERROR_OK, "Expected %d but got %d", SERIALIZATION_ERROR_OK, context->status);
	beginAndVerifyArray("", 1)
		readAndVerifyString("", "foo")
	context->vtable->endArray(context);
	context->vtable->dispose(context);
	
	initContextFromTemporaryFile("Stem\x00\x00\x00\x01" "bar\x00", 12)
	TestCase_assert(context->status == SERIALIZATION_ERROR_OK, "Expected %d but got %d", SERIALIZATION_ERROR_OK, context->status);
	beginAndVerifyArray("", 1)
		readAndVerifyString("", "bar")
	context->vtable->endArray(context);
	context->vtable->dispose(context);
	
	createContextFromTemporaryFile("Stem\x00\x00\x00\x01" "foo\x00", 12)
	TestCase_assert(context != NULL, "Expected non-NULL but got NULL");
	if (context == NULL) {return;} // Suppress clang warning
	TestCase_assert(context->status == SERIALIZATION_ERROR_OK, "Expected %d but got %d", SERIALIZATION_ERROR_OK, context->status);
	beginAndVerifyArray("", 1)
		readAndVerifyString("", "foo")
	context->vtable->endArray(context);
	context->vtable->dispose(context);
	
	createContextFromTemporaryFile("Stem\x00\x00\x00\x01" "bar\x00", 12)
	TestCase_assert(context != NULL, "Expected non-NULL but got NULL");
	if (context == NULL) {return;} // Suppress clang warning
	TestCase_assert(context->status == SERIALIZATION_ERROR_OK, "Expected %d but got %d", SERIALIZATION_ERROR_OK, context->status);
	beginAndVerifyArray("", 1)
		readAndVerifyString("", "bar")
	context->vtable->endArray(context);
	context->vtable->dispose(context);
}

static void testErrorReporting() {
	BinaryDeserializationContext * context, context2;
	jmp_buf jmpBuf;
	int status;
	size_t length;
	
	context = BinaryDeserializationContext_createWithFile("If this file exists, unit tests will fail");
	TestCase_assert(context != NULL, "Expected non-NULL but got NULL");
	if (context == NULL) {return;} // Suppress clang warning
	TestCase_assert(context->status == BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF, "Expected %d but got %d", BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF, context->status);
	context->vtable->dispose(context);
	
	stemobject_assign_vtable(context2, BinaryDeserializationContext);
	BinaryDeserializationContext_initWithFile(&context2, "If this file exists, unit tests will fail");
	TestCase_assert(context2.status == BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF, "Expected %d but got %d", BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF, context2.status);
	BinaryDeserializationContext_dispose(&context2);
	
	context = BinaryDeserializationContext_createWithBytes(NULL, 0);
	TestCase_assert(context != NULL, "Expected non-NULL but got NULL");
	if (context == NULL) {return;} // Suppress clang warning
	TestCase_assert(context->status == BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF, "Expected %d but got %d", BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF, context->status);
	context->vtable->dispose(context);
	
	stemobject_assign_vtable(context2, BinaryDeserializationContext);
	BinaryDeserializationContext_initWithBytes(&context2, NULL, 0);
	TestCase_assert(context2.status == BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF, "Expected %d but got %d", BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF, context2.status);
	BinaryDeserializationContext_dispose(&context2);
	
#define _testFailure(BYTES, LENGTH, ERROR_STATUS, PREAMBLE_CODE, FAIL_CODE) \
	context = BinaryDeserializationContext_createWithBytes(BYTES, LENGTH); \
	PREAMBLE_CODE \
	TestCase_assert(context->status == SERIALIZATION_ERROR_OK, "Expected %d but got %d (OK)", SERIALIZATION_ERROR_OK, context->status); \
	FAIL_CODE \
	TestCase_assert(context->status == ERROR_STATUS, "Expected %d but got %d (context->status)", ERROR_STATUS, context->status); \
	context->vtable->dispose(context); \
	\
	context = BinaryDeserializationContext_createWithBytes(BYTES, LENGTH); \
	context->jmpBuf = &jmpBuf; \
	status = setjmp(jmpBuf); \
	if (!status) { \
		PREAMBLE_CODE \
		TestCase_assert(context->status == SERIALIZATION_ERROR_OK, "Expected %d but got %d (OK setjmp)", SERIALIZATION_ERROR_OK, context->status); \
		FAIL_CODE \
	} \
	TestCase_assert(status == ERROR_STATUS, "Expected %d but got %d (status setjmp)", ERROR_STATUS, status); \
	TestCase_assert(context->status == ERROR_STATUS, "Expected %d but got %d (context->status setjmp)", ERROR_STATUS, context->status); \
	context->vtable->dispose(context);
	
	_testFailure("Stem",
	             4,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             ,
	             context->vtable->beginArray(context, "");)
	_testFailure("Stem\x00",
	             5,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             ,
	             context->vtable->beginArray(context, "");)
	_testFailure("Stem\x00\x00",
	             6,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             ,
	             context->vtable->beginArray(context, "");)
	_testFailure("Stem\x00\x00\x00",
	             7,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             ,
	             context->vtable->beginArray(context, "");)
	
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00",
	             11,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             ,
	             context->vtable->beginDictionary(context, "");)
	
	_testFailure("Stem",
	             4,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->readInt8(context, "");)
	_testFailure("Stem",
	             4,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->readUInt8(context, "");)
	_testFailure("Stem\x00",
	             5,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->readInt16(context, "");)
	_testFailure("Stem\x00",
	             5,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->readUInt16(context, "");)
	_testFailure("Stem\x00\x00\x00",
	             7,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->readInt32(context, "");)
	_testFailure("Stem\x00\x00\x00",
	             7,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->readUInt32(context, "");)
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00",
	             11,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->readInt64(context, "");)
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00",
	             11,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->readUInt64(context, "");)
	_testFailure("Stem\x00\x00\x00",
	             7,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->readFloat(context, "");)
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00",
	             11,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->readDouble(context, "");)
	_testFailure("Stem\x00\x00\x00",
	             7,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->readFixed16_16(context, "");)
	_testFailure("Stem",
	             4,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->readString(context, "");)
	_testFailure("Stem\xFF",
	             5,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->readString(context, "");)
	_testFailure("Stem",
	             4,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->readStringNullable(context, "");)
	_testFailure("Stem\x01",
	             5,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->readStringNullable(context, "");)
	_testFailure("Stem",
	             4,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->readBlob(context, "", &length);)
	_testFailure("Stem\xFF",
	             5,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->readBlob(context, "", &length);)
	_testFailure("Stem\x00\x00\x00\x01",
	             5,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->readBlob(context, "", &length);)
	_testFailure("Stem",
	             4,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->readBoolean(context, "");)
	Serialization_enumKeyValue enumValue = {"enum", 0};
	_testFailure("Stem\x00\x00\x00",
	             7,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->readEnumeration(context, "", 1, &enumValue);)
	_testFailure("Stem",
	             4,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->readBitfield8(context, "", 0, NULL);)
	_testFailure("Stem\x00",
	             5,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->readBitfield16(context, "", 0, NULL);)
	_testFailure("Stem\x00\x00\x00",
	             7,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->readBitfield32(context, "", 0, NULL);)
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00",
	             11,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->readBitfield64(context, "", 0, NULL);)
	
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00\x09",
	             12,
	             BINARY_SERIALIZATION_ERROR_INVALID_OFFSET,
	             ,
	             context->vtable->beginDictionary(context, "");)
	
	_testFailure("Stem\x00\x00\x00\x01\x00\x00\x00\x0D\x00\x00\x00\x00\x0E",
	             17,
	             BINARY_SERIALIZATION_ERROR_INVALID_OFFSET,
	             ,
	             context->vtable->beginDictionary(context, "");)
	
	_testFailure("Stem\x00\x00\x00\x01\x00\x00\x00\x08",
	             12,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             ,
	             context->vtable->beginDictionary(context, "");)
	_testFailure("Stem\x00\x00\x00\x01\x00\x00\x00\x0C\x00\x00\x00\x00",
	             11,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             ,
	             context->vtable->beginDictionary(context, "");)
	_testFailure("Stem\x00\x00\x00\x02\x00\x00\x00\x0D\x00\x00\x00\x00\x0D",
	             11,
	             BINARY_SERIALIZATION_ERROR_UNEXPECTED_EOF,
	             ,
	             context->vtable->beginDictionary(context, "");)
	
	_testFailure("Stem\x00",
	             5,
	             BINARY_SERIALIZATION_ERROR_EXTRA_DATA_AT_EOF,
	             context->vtable->beginStructure(context, ""); context->vtable->beginStructure(context, ""); context->vtable->endStructure(context);,
	             context->vtable->endStructure(context);)
	_testFailure("Stem\x00\x00\x00\x01\x00\x00\x00\x00\x00",
	             13,
	             BINARY_SERIALIZATION_ERROR_EXTRA_DATA_AT_EOF,
	             context->vtable->beginArray(context, ""); context->vtable->beginArray(context, ""); context->vtable->endArray(context);,
	             context->vtable->endArray(context);)
	_testFailure("Stem\x00\x00\x00\x01\x00\x00\x00\x18\x00\x00\x00\x00\x0D\x00\x00\x00\x00\x00\x00\x00\x08\x00",
	             29,
	             BINARY_SERIALIZATION_ERROR_EXTRA_DATA_AT_EOF,
	             context->vtable->beginDictionary(context, ""); context->vtable->beginDictionary(context, ""); context->vtable->endDictionary(context);,
	             context->vtable->endDictionary(context);)
	
	_testFailure("Stem\x00\x00\x00\x01\xFF",
	             9,
	             BINARY_SERIALIZATION_ERROR_ARRAY_NOT_FULLY_READ,
	             context->vtable->beginArray(context, "");,
	             context->vtable->endArray(context);)
	
	_testFailure("Stem\x00\x00\x00\x01\x00",
	             9,
	             SERIALIZATION_ERROR_END_OF_CONTAINER,
	             context->vtable->beginArray(context, ""); context->vtable->readInt8(context, "");,
	             context->vtable->readInt8(context, "");)
	_testFailure("Stem\x00\x00\x00\x01\x00",
	             9,
	             SERIALIZATION_ERROR_END_OF_CONTAINER,
	             context->vtable->beginArray(context, ""); context->vtable->readInt8(context, "");,
	             context->vtable->readUInt8(context, "");)
	_testFailure("Stem\x00\x00\x00\x01\x00",
	             9,
	             SERIALIZATION_ERROR_END_OF_CONTAINER,
	             context->vtable->beginArray(context, ""); context->vtable->readInt8(context, "");,
	             context->vtable->readInt16(context, "");)
	_testFailure("Stem\x00\x00\x00\x01\x00",
	             9,
	             SERIALIZATION_ERROR_END_OF_CONTAINER,
	             context->vtable->beginArray(context, ""); context->vtable->readInt8(context, "");,
	             context->vtable->readUInt16(context, "");)
	_testFailure("Stem\x00\x00\x00\x01\x00",
	             9,
	             SERIALIZATION_ERROR_END_OF_CONTAINER,
	             context->vtable->beginArray(context, ""); context->vtable->readInt8(context, "");,
	             context->vtable->readInt32(context, "");)
	_testFailure("Stem\x00\x00\x00\x01\x00",
	             9,
	             SERIALIZATION_ERROR_END_OF_CONTAINER,
	             context->vtable->beginArray(context, ""); context->vtable->readInt8(context, "");,
	             context->vtable->readUInt32(context, "");)
	_testFailure("Stem\x00\x00\x00\x01\x00",
	             9,
	             SERIALIZATION_ERROR_END_OF_CONTAINER,
	             context->vtable->beginArray(context, ""); context->vtable->readInt8(context, "");,
	             context->vtable->readInt64(context, "");)
	_testFailure("Stem\x00\x00\x00\x01\x00",
	             9,
	             SERIALIZATION_ERROR_END_OF_CONTAINER,
	             context->vtable->beginArray(context, ""); context->vtable->readInt8(context, "");,
	             context->vtable->readUInt64(context, "");)
	_testFailure("Stem\x00\x00\x00\x01\x00",
	             9,
	             SERIALIZATION_ERROR_END_OF_CONTAINER,
	             context->vtable->beginArray(context, ""); context->vtable->readInt8(context, "");,
	             context->vtable->readFloat(context, "");)
	_testFailure("Stem\x00\x00\x00\x01\x00",
	             9,
	             SERIALIZATION_ERROR_END_OF_CONTAINER,
	             context->vtable->beginArray(context, ""); context->vtable->readInt8(context, "");,
	             context->vtable->readDouble(context, "");)
	_testFailure("Stem\x00\x00\x00\x01\x00",
	             9,
	             SERIALIZATION_ERROR_END_OF_CONTAINER,
	             context->vtable->beginArray(context, ""); context->vtable->readInt8(context, "");,
	             context->vtable->readFixed16_16(context, "");)
	_testFailure("Stem\x00\x00\x00\x01\x00",
	             9,
	             SERIALIZATION_ERROR_END_OF_CONTAINER,
	             context->vtable->beginArray(context, ""); context->vtable->readInt8(context, "");,
	             context->vtable->readString(context, "");)
	_testFailure("Stem\x00\x00\x00\x01\x00",
	             9,
	             SERIALIZATION_ERROR_END_OF_CONTAINER,
	             context->vtable->beginArray(context, ""); context->vtable->readInt8(context, "");,
	             context->vtable->readStringNullable(context, "");)
	_testFailure("Stem\x00\x00\x00\x01\x00",
	             9,
	             SERIALIZATION_ERROR_END_OF_CONTAINER,
	             context->vtable->beginArray(context, ""); context->vtable->readInt8(context, "");,
	             context->vtable->readBoolean(context, "");)
	_testFailure("Stem\x00\x00\x00\x01\x00",
	             9,
	             SERIALIZATION_ERROR_END_OF_CONTAINER,
	             context->vtable->beginArray(context, ""); context->vtable->readInt8(context, "");,
	             context->vtable->readEnumeration(context, "", 1, &enumValue);)
	Serialization_bitName bitName = {"bit", 0};
	_testFailure("Stem\x00\x00\x00\x01\x00",
	             9,
	             SERIALIZATION_ERROR_END_OF_CONTAINER,
	             context->vtable->beginArray(context, ""); context->vtable->readInt8(context, "");,
	             context->vtable->readBitfield8(context, "", 1, &bitName);)
	_testFailure("Stem\x00\x00\x00\x01\x00",
	             9,
	             SERIALIZATION_ERROR_END_OF_CONTAINER,
	             context->vtable->beginArray(context, ""); context->vtable->readInt8(context, "");,
	             context->vtable->readBitfield16(context, "", 1, &bitName);)
	_testFailure("Stem\x00\x00\x00\x01\x00",
	             9,
	             SERIALIZATION_ERROR_END_OF_CONTAINER,
	             context->vtable->beginArray(context, ""); context->vtable->readInt8(context, "");,
	             context->vtable->readBitfield32(context, "", 1, &bitName);)
	_testFailure("Stem\x00\x00\x00\x01\x00",
	             9,
	             SERIALIZATION_ERROR_END_OF_CONTAINER,
	             context->vtable->beginArray(context, ""); context->vtable->readInt8(context, "");,
	             context->vtable->readBitfield64(context, "", 1, &bitName);)
	
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00\x08",
	             12,
	             SERIALIZATION_ERROR_END_OF_CONTAINER,
	             context->vtable->beginDictionary(context, "");,
	             context->vtable->readNextDictionaryKey(context);)
	_testFailure("Stem\x00\x00\x00\x01\x00\x00\x00\x0E\x00\x00\x00\x00\x0D\x00",
	             18,
	             SERIALIZATION_ERROR_END_OF_CONTAINER,
	             context->vtable->beginDictionary(context, "");
	             context->vtable->readNextDictionaryKey(context);,
	             context->vtable->readNextDictionaryKey(context);)
	_testFailure("Stem\x00\x00\x00\x02\x00\x00\x00\x1C\x00\x00\x00\x00\x13" "a\x00\x00\x00\x00\x1B\x00\x00\x00\x00\x00\x00\x00\x08\x00",
	             32,
	             SERIALIZATION_ERROR_END_OF_CONTAINER,
	             context->vtable->beginDictionary(context, "");
	             context->vtable->beginDictionary(context, "");,
	             context->vtable->readNextDictionaryKey(context);)
	
#define _testNoSuchKey(READ_CODE) \
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00\x08", 12, SERIALIZATION_ERROR_KEY_NOT_FOUND, context->vtable->beginDictionary(context, "");, READ_CODE) \
	_testFailure("Stem\x00\x00\x00\x01\x00\x00\x00\x0F" "a\x00\x00\x00\x00\x0E\x00", 19, SERIALIZATION_ERROR_KEY_NOT_FOUND, context->vtable->beginDictionary(context, "");, READ_CODE) \
	_testFailure("metS\x00\x00\x00\x00\x08\x00\x00\x00", 12, SERIALIZATION_ERROR_KEY_NOT_FOUND, context->vtable->beginDictionary(context, "");, READ_CODE) \
	_testFailure("metS\x01\x00\x00\x00\x0F\x00\x00\x00" "a\x00\x0E\x00\x00\x00\x00", 19, SERIALIZATION_ERROR_KEY_NOT_FOUND, context->vtable->beginDictionary(context, "");, READ_CODE)
	
	_testNoSuchKey(context->vtable->beginArray(context, "");)
	_testNoSuchKey(context->vtable->beginStructure(context, "");)
	_testNoSuchKey(context->vtable->beginDictionary(context, "");)
	_testNoSuchKey(context->vtable->readInt8(context, "");)
	_testNoSuchKey(context->vtable->readUInt8(context, "");)
	_testNoSuchKey(context->vtable->readInt16(context, "");)
	_testNoSuchKey(context->vtable->readUInt16(context, "");)
	_testNoSuchKey(context->vtable->readInt32(context, "");)
	_testNoSuchKey(context->vtable->readUInt32(context, "");)
	_testNoSuchKey(context->vtable->readInt64(context, "");)
	_testNoSuchKey(context->vtable->readUInt64(context, "");)
	_testNoSuchKey(context->vtable->readFloat(context, "");)
	_testNoSuchKey(context->vtable->readDouble(context, "");)
	_testNoSuchKey(context->vtable->readFixed16_16(context, "");)
	_testNoSuchKey(context->vtable->readString(context, "");)
	_testNoSuchKey(context->vtable->readStringNullable(context, "");)
	_testNoSuchKey(context->vtable->readBoolean(context, "");)
	_testNoSuchKey(context->vtable->readEnumeration(context, "", 1, &enumValue);)
	_testNoSuchKey(context->vtable->readBitfield8(context, "", 0, NULL);)
	_testNoSuchKey(context->vtable->readBitfield16(context, "", 0, NULL);)
	_testNoSuchKey(context->vtable->readBitfield32(context, "", 0, NULL);)
	_testNoSuchKey(context->vtable->readBitfield64(context, "", 0, NULL);)
	
#undef _testNoSuchKey
	
	
	
	
#define _testReadBitfieldFailure(BYTES, LENGTH, ERROR, ...) \
	_testFailure(BYTES, LENGTH, ERROR, context->vtable->beginStructure(context, "");, context->vtable->readBitfield8(context, "", __VA_ARGS__);) \
	_testFailure(BYTES, LENGTH, ERROR, context->vtable->beginStructure(context, "");, context->vtable->readBitfield16(context, "", __VA_ARGS__);) \
	_testFailure(BYTES, LENGTH, ERROR, context->vtable->beginStructure(context, "");, context->vtable->readBitfield32(context, "", __VA_ARGS__);) \
	_testFailure(BYTES, LENGTH, ERROR, context->vtable->beginStructure(context, "");, context->vtable->readBitfield64(context, "", __VA_ARGS__);)
	
	Serialization_bitName failBitName1 = {"foo", 0};
	Serialization_bitName failBitNames2[] = {{"foo", 0}, {"foo", 1}};
	_testReadBitfieldFailure("Stem\x80\x00\x00\x00\x00\x00\x00\x00", 12, SERIALIZATION_ERROR_UNNAMED_BIT, 1, &failBitName1)
	_testReadBitfieldFailure("Stem\x02\x00\x00\x00\x00\x00\x00\x00", 12, SERIALIZATION_ERROR_UNNAMED_BIT, 1, &failBitName1)
	
	_testReadBitfieldFailure("Stem\x00\x00\x00\x00\x00\x00\x00\x00", 12, SERIALIZATION_ERROR_DUPLICATE_BIT, 2, failBitNames2);
	
#undef _testReadBitfieldFailure
	
	_testFailure("Stem\x00\x00\x00\x00",
	             8,
	             SERIALIZATION_ERROR_ENUM_NOT_NAMED,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->readEnumeration(context, "", 0, NULL);)
	Serialization_enumKeyValue unnamedEnumValue = {"a", 0};
	_testFailure("Stem\x00\x00\x00\x01",
	             8,
	             SERIALIZATION_ERROR_ENUM_NOT_NAMED,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->readEnumeration(context, "", 1, &unnamedEnumValue);)
	
	Serialization_enumKeyValue duplicateEnumValues[] = {{"enum0", 0}, {"enum1", 0}};
	_testFailure("Stem\x00\x00\x00\x00",
	             8,
	             SERIALIZATION_ERROR_DUPLICATE_ENUM_VALUE,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->readEnumeration(context, "", 2, duplicateEnumValues);)
	
	Serialization_enumKeyValue duplicateEnumNames[] = {{"enum0", 0}, {"enum0", 1}};
	_testFailure("Stem\x00\x00\x00\x00",
	             8,
	             SERIALIZATION_ERROR_DUPLICATE_ENUM_NAME,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->readEnumeration(context, "", 2, duplicateEnumNames);)
	
	_testFailure("Stem\x00\x00\x00\x00",
	             8,
	             SERIALIZATION_ERROR_INVALID_OPERATION,
	             context->vtable->beginArray(context, "");,
	             context->vtable->hasDictionaryKey(context, "");)
	_testFailure("Stem\x00\x00\x00\x00",
	             8,
	             SERIALIZATION_ERROR_INVALID_OPERATION,
	             context->vtable->beginArray(context, "");,
	             context->vtable->readNextDictionaryKey(context);)
	_testFailure("Stem\x00\x00\x00\x01\x00\x00\x00\x00",
	             12,
	             SERIALIZATION_ERROR_INVALID_OPERATION,
	             context->vtable->beginArray(context, "");
	             context->vtable->beginArray(context, "");,
	             context->vtable->hasDictionaryKey(context, "");)
	_testFailure("Stem\x00\x00\x00\x01\x00\x00\x00\x00",
	             12,
	             SERIALIZATION_ERROR_INVALID_OPERATION,
	             context->vtable->beginArray(context, "");
	             context->vtable->beginArray(context, "");,
	             context->vtable->readNextDictionaryKey(context);)
	_testFailure("Stem",
	             4,
	             SERIALIZATION_ERROR_INVALID_OPERATION,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->hasDictionaryKey(context, "");)
	_testFailure("Stem",
	             4,
	             SERIALIZATION_ERROR_INVALID_OPERATION,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->readNextDictionaryKey(context);)
	_testFailure("Stem",
	             4,
	             SERIALIZATION_ERROR_INVALID_OPERATION,
	             context->vtable->beginStructure(context, "");
	             context->vtable->beginStructure(context, "");,
	             context->vtable->hasDictionaryKey(context, "");)
	_testFailure("Stem",
	             4,
	             SERIALIZATION_ERROR_INVALID_OPERATION,
	             context->vtable->beginStructure(context, "");
	             context->vtable->beginStructure(context, "");,
	             context->vtable->readNextDictionaryKey(context);)
	
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginStructure(context, "");, context->vtable->readInt8(context, NULL);)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginStructure(context, "");, context->vtable->readUInt8(context, NULL);)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginStructure(context, "");, context->vtable->readInt16(context, NULL);)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginStructure(context, "");, context->vtable->readUInt16(context, NULL);)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginStructure(context, "");, context->vtable->readInt32(context, NULL);)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginStructure(context, "");, context->vtable->readUInt32(context, NULL);)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginStructure(context, "");, context->vtable->readInt64(context, NULL);)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginStructure(context, "");, context->vtable->readUInt64(context, NULL);)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginStructure(context, "");, context->vtable->readFloat(context, NULL);)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginStructure(context, "");, context->vtable->readDouble(context, NULL);)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginStructure(context, "");, context->vtable->readFixed16_16(context, NULL);)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginStructure(context, "");, context->vtable->readString(context, NULL);)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginStructure(context, "");, context->vtable->readStringNullable(context, NULL);)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginStructure(context, "");, context->vtable->readBoolean(context, NULL);)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginStructure(context, "");, context->vtable->readEnumeration(context, NULL, 0, NULL);)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginStructure(context, "");, context->vtable->readBitfield8(context, NULL, 0, NULL);)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginStructure(context, "");, context->vtable->readBitfield16(context, NULL, 0, NULL);)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginStructure(context, "");, context->vtable->readBitfield32(context, NULL, 0, NULL);)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginStructure(context, "");, context->vtable->readBitfield64(context, NULL, 0, NULL);)
	
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00\x08", 12, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginDictionary(context, "");, context->vtable->readInt8(context, NULL);)
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00\x08", 12, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginDictionary(context, "");, context->vtable->readUInt8(context, NULL);)
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00\x08", 12, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginDictionary(context, "");, context->vtable->readInt16(context, NULL);)
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00\x08", 12, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginDictionary(context, "");, context->vtable->readUInt16(context, NULL);)
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00\x08", 12, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginDictionary(context, "");, context->vtable->readInt32(context, NULL);)
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00\x08", 12, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginDictionary(context, "");, context->vtable->readUInt32(context, NULL);)
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00\x08", 12, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginDictionary(context, "");, context->vtable->readInt64(context, NULL);)
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00\x08", 12, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginDictionary(context, "");, context->vtable->readUInt64(context, NULL);)
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00\x08", 12, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginDictionary(context, "");, context->vtable->readFloat(context, NULL);)
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00\x08", 12, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginDictionary(context, "");, context->vtable->readDouble(context, NULL);)
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00\x08", 12, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginDictionary(context, "");, context->vtable->readFixed16_16(context, NULL);)
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00\x08", 12, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginDictionary(context, "");, context->vtable->readString(context, NULL);)
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00\x08", 12, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginDictionary(context, "");, context->vtable->readStringNullable(context, NULL);)
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00\x08", 12, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginDictionary(context, "");, context->vtable->readBoolean(context, NULL);)
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00\x08", 12, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginDictionary(context, "");, context->vtable->readEnumeration(context, NULL, 0, NULL);)
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00\x08", 12, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginDictionary(context, "");, context->vtable->readBitfield8(context, NULL, 0, NULL);)
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00\x08", 12, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginDictionary(context, "");, context->vtable->readBitfield16(context, NULL, 0, NULL);)
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00\x08", 12, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginDictionary(context, "");, context->vtable->readBitfield32(context, NULL, 0, NULL);)
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00\x08", 12, SERIALIZATION_ERROR_NULL_KEY, context->vtable->beginDictionary(context, "");, context->vtable->readBitfield64(context, NULL, 0, NULL);)
	
	_testFailure("Stem\x00\x00\x00\x00",
	             8,
	             SERIALIZATION_ERROR_MULTIPLE_TOP_LEVEL_CONTAINERS,
	             context->vtable->beginArray(context, "");
	             context->vtable->endArray(context);,
	             context->vtable->beginArray(context, "");)
	_testFailure("Stem\x00\x00\x00\x00",
	             8,
	             SERIALIZATION_ERROR_MULTIPLE_TOP_LEVEL_CONTAINERS,
	             context->vtable->beginArray(context, "");
	             context->vtable->endArray(context);,
	             context->vtable->beginDictionary(context, "");)
	_testFailure("Stem\x00\x00\x00\x00",
	             8,
	             SERIALIZATION_ERROR_MULTIPLE_TOP_LEVEL_CONTAINERS,
	             context->vtable->beginArray(context, "");
	             context->vtable->endArray(context);,
	             context->vtable->beginStructure(context, "");)
	_testFailure("Stem",
	             4,
	             SERIALIZATION_ERROR_MULTIPLE_TOP_LEVEL_CONTAINERS,
	             context->vtable->beginStructure(context, "");
	             context->vtable->endStructure(context);,
	             context->vtable->beginArray(context, "");)
	_testFailure("Stem",
	             4,
	             SERIALIZATION_ERROR_MULTIPLE_TOP_LEVEL_CONTAINERS,
	             context->vtable->beginStructure(context, "");
	             context->vtable->endStructure(context);,
	             context->vtable->beginDictionary(context, "");)
	_testFailure("Stem",
	             4,
	             SERIALIZATION_ERROR_MULTIPLE_TOP_LEVEL_CONTAINERS,
	             context->vtable->beginStructure(context, "");
	             context->vtable->endStructure(context);,
	             context->vtable->beginStructure(context, "");)
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00\x08",
	             12,
	             SERIALIZATION_ERROR_MULTIPLE_TOP_LEVEL_CONTAINERS,
	             context->vtable->beginDictionary(context, "");
	             context->vtable->endDictionary(context);,
	             context->vtable->beginArray(context, "");)
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00\x08",
	             12,
	             SERIALIZATION_ERROR_MULTIPLE_TOP_LEVEL_CONTAINERS,
	             context->vtable->beginDictionary(context, "");
	             context->vtable->endDictionary(context);,
	             context->vtable->beginDictionary(context, "");)
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00\x08",
	             12,
	             SERIALIZATION_ERROR_MULTIPLE_TOP_LEVEL_CONTAINERS,
	             context->vtable->beginDictionary(context, "");
	             context->vtable->endDictionary(context);,
	             context->vtable->beginStructure(context, "");)
	
	_testFailure("Stem\x00\x00\x00\x00",
	             8,
	             SERIALIZATION_ERROR_CONTAINER_UNDERFLOW,
	             ;,
	             context->vtable->endArray(context);)
	_testFailure("Stem\x00\x00\x00\x00",
	             8,
	             SERIALIZATION_ERROR_CONTAINER_UNDERFLOW,
	             context->vtable->beginArray(context, "");
	             context->vtable->endArray(context);,
	             context->vtable->endArray(context);)
	_testFailure("Stem",
	             4,
	             SERIALIZATION_ERROR_CONTAINER_UNDERFLOW,
	             ;,
	             context->vtable->endStructure(context);)
	_testFailure("Stem",
	             4,
	             SERIALIZATION_ERROR_CONTAINER_UNDERFLOW,
	             context->vtable->beginStructure(context, "");
	             context->vtable->endStructure(context);,
	             context->vtable->endStructure(context);)
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00\x08",
	             12,
	             SERIALIZATION_ERROR_CONTAINER_UNDERFLOW,
	             ;,
	             context->vtable->endDictionary(context);)
	_testFailure("Stem\x00\x00\x00\x01\x00\x00\x00\x15\x00\x00\x00\x00\x0D\x00\x00\x00\x00\x00\x00\x00\x08",
	             25,
	             SERIALIZATION_ERROR_CONTAINER_UNDERFLOW,
	             context->vtable->beginDictionary(context, "");
	             context->vtable->endDictionary(context);,
	             context->vtable->endDictionary(context);)
	
	_testFailure("Stem\x00\x00\x00\x00",
	             8,
	             SERIALIZATION_ERROR_CONTAINER_TYPE_MISMATCH,
	             context->vtable->beginArray(context, "");,
	             context->vtable->endStructure(context);)
	_testFailure("Stem\x00\x00\x00\x01\x00\x00\x00\x00",
	             12,
	             SERIALIZATION_ERROR_CONTAINER_TYPE_MISMATCH,
	             context->vtable->beginArray(context, "");
	             context->vtable->beginArray(context, "");,
	             context->vtable->endStructure(context);)
	_testFailure("Stem\x00\x00\x00\x00",
	             8,
	             SERIALIZATION_ERROR_CONTAINER_TYPE_MISMATCH,
	             context->vtable->beginArray(context, "");,
	             context->vtable->endDictionary(context);)
	_testFailure("Stem\x00\x00\x00\x01\x00\x00\x00\x00",
	             12,
	             SERIALIZATION_ERROR_CONTAINER_TYPE_MISMATCH,
	             context->vtable->beginArray(context, "");
	             context->vtable->beginArray(context, "");,
	             context->vtable->endDictionary(context);)
	_testFailure("Stem",
	             4,
	             SERIALIZATION_ERROR_CONTAINER_TYPE_MISMATCH,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->endArray(context);)
	_testFailure("Stem",
	             4,
	             SERIALIZATION_ERROR_CONTAINER_TYPE_MISMATCH,
	             context->vtable->beginStructure(context, "");
	             context->vtable->beginStructure(context, "");,
	             context->vtable->endArray(context);)
	_testFailure("Stem",
	             4,
	             SERIALIZATION_ERROR_CONTAINER_TYPE_MISMATCH,
	             context->vtable->beginStructure(context, "");,
	             context->vtable->endDictionary(context);)
	_testFailure("Stem",
	             4,
	             SERIALIZATION_ERROR_CONTAINER_TYPE_MISMATCH,
	             context->vtable->beginStructure(context, "");
	             context->vtable->beginStructure(context, "");,
	             context->vtable->endDictionary(context);)
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00\x08",
	             12,
	             SERIALIZATION_ERROR_CONTAINER_TYPE_MISMATCH,
	             context->vtable->beginDictionary(context, "");,
	             context->vtable->endArray(context);)
	_testFailure("Stem\x00\x00\x00\x01\x00\x00\x00\x15\x00\x00\x00\x00\x0D\x00\x00\x00\x00\x00\x00\x00\x08",
	             25,
	             SERIALIZATION_ERROR_CONTAINER_TYPE_MISMATCH,
	             context->vtable->beginDictionary(context, "");
	             context->vtable->beginDictionary(context, "");,
	             context->vtable->endArray(context);)
	_testFailure("Stem\x00\x00\x00\x00\x00\x00\x00\x08",
	             12,
	             SERIALIZATION_ERROR_CONTAINER_TYPE_MISMATCH,
	             context->vtable->beginDictionary(context, "");,
	             context->vtable->endStructure(context);)
	_testFailure("Stem\x00\x00\x00\x01\x00\x00\x00\x15\x00\x00\x00\x00\x0D\x00\x00\x00\x00\x00\x00\x00\x08",
	             25,
	             SERIALIZATION_ERROR_CONTAINER_TYPE_MISMATCH,
	             context->vtable->beginDictionary(context, "");
	             context->vtable->beginDictionary(context, "");,
	             context->vtable->endStructure(context);)
	
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, , context->vtable->readInt8(context, "key");)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, , context->vtable->readUInt8(context, "key");)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, , context->vtable->readInt16(context, "key");)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, , context->vtable->readUInt16(context, "key");)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, , context->vtable->readInt32(context, "key");)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, , context->vtable->readUInt32(context, "key");)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, , context->vtable->readInt64(context, "key");)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, , context->vtable->readUInt64(context, "key");)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, , context->vtable->readFloat(context, "key");)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, , context->vtable->readDouble(context, "key");)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, , context->vtable->readFixed16_16(context, "key");)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, , context->vtable->readString(context, "key");)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, , context->vtable->readStringNullable(context, "key");)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, , context->vtable->readBoolean(context, "key");)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, , context->vtable->readEnumeration(context, "key", 1, &enumValue);)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, , context->vtable->readBitfield8(context, "key", 0, NULL);)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, , context->vtable->readBitfield16(context, "key", 0, NULL);)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, , context->vtable->readBitfield32(context, "key", 0, NULL);)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, , context->vtable->readBitfield64(context, "key", 0, NULL);)
	
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, context->vtable->beginStructure(context, ""); context->vtable->endStructure(context);, context->vtable->readInt8(context, "key");)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, context->vtable->beginStructure(context, ""); context->vtable->endStructure(context);, context->vtable->readUInt8(context, "key");)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, context->vtable->beginStructure(context, ""); context->vtable->endStructure(context);, context->vtable->readInt16(context, "key");)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, context->vtable->beginStructure(context, ""); context->vtable->endStructure(context);, context->vtable->readUInt16(context, "key");)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, context->vtable->beginStructure(context, ""); context->vtable->endStructure(context);, context->vtable->readInt32(context, "key");)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, context->vtable->beginStructure(context, ""); context->vtable->endStructure(context);, context->vtable->readUInt32(context, "key");)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, context->vtable->beginStructure(context, ""); context->vtable->endStructure(context);, context->vtable->readInt64(context, "key");)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, context->vtable->beginStructure(context, ""); context->vtable->endStructure(context);, context->vtable->readUInt64(context, "key");)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, context->vtable->beginStructure(context, ""); context->vtable->endStructure(context);, context->vtable->readFloat(context, "key");)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, context->vtable->beginStructure(context, ""); context->vtable->endStructure(context);, context->vtable->readDouble(context, "key");)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, context->vtable->beginStructure(context, ""); context->vtable->endStructure(context);, context->vtable->readFixed16_16(context, "key");)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, context->vtable->beginStructure(context, ""); context->vtable->endStructure(context);, context->vtable->readString(context, "key");)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, context->vtable->beginStructure(context, ""); context->vtable->endStructure(context);, context->vtable->readStringNullable(context, "key");)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, context->vtable->beginStructure(context, ""); context->vtable->endStructure(context);, context->vtable->readBoolean(context, "key");)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, context->vtable->beginStructure(context, ""); context->vtable->endStructure(context);, context->vtable->readEnumeration(context, "key", 1, &enumValue);)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, context->vtable->beginStructure(context, ""); context->vtable->endStructure(context);, context->vtable->readBitfield8(context, "key", 0, NULL);)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, context->vtable->beginStructure(context, ""); context->vtable->endStructure(context);, context->vtable->readBitfield16(context, "key", 0, NULL);)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, context->vtable->beginStructure(context, ""); context->vtable->endStructure(context);, context->vtable->readBitfield32(context, "key", 0, NULL);)
	_testFailure("Stem", 4, SERIALIZATION_ERROR_NO_CONTAINER_STARTED, context->vtable->beginStructure(context, ""); context->vtable->endStructure(context);, context->vtable->readBitfield64(context, "key", 0, NULL);)
	
	_testFailure("Stem\x02", 5, BINARY_SERIALIZATION_ERROR_INVALID_NULLABLE_STRING_CONTROL_BYTE, context->vtable->beginStructure(context, "");, context->vtable->readStringNullable(context, "key");)
	
#undef _testFailure
}

TEST_SUITE(BinaryDeserializationContextTest,
           testInit,
           testInitErrors,
           testNumberValues,
           testStringValues,
           testBlobValues,
           testBooleanValues,
           testEnumerations,
           testBitfields,
           testArrays,
           testStructures,
           testDictionaries,
           testMixedContainers,
           testInitWithFile,
           testErrorReporting)
