/* Copyright (c) 2015 Alex Diener This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Alex Diener alex@ludobloom.com */ #include "binaryserialization/BinarySerializationContext.h" #include #include #define stemobject_implementation BinarySerializationContext stemobject_vtable_begin(); stemobject_vtable_entry(dispose); stemobject_vtable_custom_entry(errorString, BinarySerialization_errorString); stemobject_vtable_entry(beginStructure); stemobject_vtable_entry(beginDictionary); stemobject_vtable_entry(beginArray); stemobject_vtable_entry(endStructure); stemobject_vtable_entry(endDictionary); stemobject_vtable_entry(endArray); stemobject_vtable_entry(writeBoolean); stemobject_vtable_entry(writeInt8); stemobject_vtable_entry(writeUInt8); stemobject_vtable_entry(writeInt16); stemobject_vtable_entry(writeUInt16); stemobject_vtable_entry(writeInt32); stemobject_vtable_entry(writeUInt32); stemobject_vtable_entry(writeInt64); stemobject_vtable_entry(writeUInt64); stemobject_vtable_entry(writeFloat); stemobject_vtable_entry(writeDouble); stemobject_vtable_entry(writeFixed16_16); stemobject_vtable_entry(writeEnumeration); stemobject_vtable_entry(writeEnumeration8); stemobject_vtable_entry(writeEnumeration16); stemobject_vtable_entry(writeEnumeration32); stemobject_vtable_entry(writeBitfield8); stemobject_vtable_entry(writeBitfield16); stemobject_vtable_entry(writeBitfield32); stemobject_vtable_entry(writeBitfield64); stemobject_vtable_entry(writeString); stemobject_vtable_entry(writeStringNullable); stemobject_vtable_entry(writeBlob); stemobject_vtable_end(); BinarySerializationContext * BinarySerializationContext_create(bool bigEndian) { stemobject_create_implementation(init, bigEndian) } bool BinarySerializationContext_init(BinarySerializationContext * self, bool bigEndian) { call_super(init, self); self->jmpBuf = NULL; self->status = SERIALIZATION_ERROR_OK; self->bigEndian = bigEndian; self->memwriteContext = memwriteContextInit(NULL, 0, 0, true); memwrite(&self->memwriteContext, 4, self->bigEndian ? "Stem" : "metS"); self->containerCount = 0; self->containerListSize = 1; self->containerStack = malloc(sizeof(struct BinarySerializationContext_containerNode) * self->containerListSize); self->finished = false; return true; } void BinarySerializationContext_dispose(BinarySerializationContext * self) { size_t containerIndex; uint32_t keyIndex; free(self->memwriteContext.data); for (containerIndex = 0; containerIndex < self->containerCount; containerIndex++) { if (self->containerStack[containerIndex].type == BINARY_SERIALIZATION_CONTAINER_TYPE_DICTIONARY) { for (keyIndex = 0; keyIndex < self->containerStack[containerIndex].count; keyIndex++) { free(self->containerStack[containerIndex].keys[keyIndex]); } free(self->containerStack[containerIndex].keys); free(self->containerStack[containerIndex].offsets); } } free(self->containerStack); call_super(dispose, self); } #define failWithStatus(STATUS, RETURN_CODE) \ self->status = (STATUS); \ if (self->jmpBuf != NULL) { \ longjmp(*self->jmpBuf, self->status); \ } \ RETURN_CODE; #define failIfInvalid(RETURN_CODE) \ if (!self->finished) { \ failWithStatus(BINARY_SERIALIZATION_ERROR_NO_TOP_LEVEL_CONTAINER, RETURN_CODE) \ } \ if (self->status != SERIALIZATION_ERROR_OK) { \ RETURN_CODE; \ } void * BinarySerializationContext_writeToBytes(BinarySerializationContext * self, size_t * outLength) { void * bytes; failIfInvalid(return NULL) bytes = malloc(self->memwriteContext.length); memcpy(bytes, self->memwriteContext.data, self->memwriteContext.length); *outLength = self->memwriteContext.length; return bytes; } bool BinarySerializationContext_writeToFile(BinarySerializationContext * self, const char * filePath) { failIfInvalid(return false) return writeFileSimple(filePath, self->memwriteContext.data, self->memwriteContext.length); } #define writePreamble(self, key) { \ struct BinarySerializationContext_containerNode * container; \ \ if (self->containerCount == 0) { \ failWithStatus(SERIALIZATION_ERROR_NO_CONTAINER_STARTED, return) \ } \ container = &self->containerStack[self->containerCount - 1]; \ if (container->type != BINARY_SERIALIZATION_CONTAINER_TYPE_ARRAY && key == NULL) { \ failWithStatus(SERIALIZATION_ERROR_NULL_KEY, return) \ } \ if (container->type == BINARY_SERIALIZATION_CONTAINER_TYPE_DICTIONARY) { \ if (container->count >= container->keyListSize) { \ container->keyListSize *= 2; \ container->keys = realloc(container->keys, sizeof(char *) * container->keyListSize); \ container->offsets = realloc(container->offsets, sizeof(uint32_t) * container->keyListSize); \ } \ container->keys[container->count] = malloc(strlen(key) + 1); \ strcpy(container->keys[container->count], key); \ container->offsets[container->count] = self->memwriteContext.position - container->startOffset; \ } \ container->count++; \ } static void writeUInt16Internal(BinarySerializationContext * self, uint16_t value) { unsigned char bytes[2]; if (self->bigEndian) { bytes[0] = (value >> 8) & 0xFF; bytes[1] = value & 0xFF; } else { bytes[1] = (value >> 8) & 0xFF; bytes[0] = value & 0xFF; } memwrite(&self->memwriteContext, 2, bytes); } static void writeUInt32Internal(BinarySerializationContext * self, uint32_t value) { unsigned char bytes[4]; if (self->bigEndian) { bytes[0] = value >> 24; bytes[1] = (value >> 16) & 0xFF; bytes[2] = (value >> 8) & 0xFF; bytes[3] = value & 0xFF; } else { bytes[3] = value >> 24; bytes[2] = (value >> 16) & 0xFF; bytes[1] = (value >> 8) & 0xFF; bytes[0] = value & 0xFF; } memwrite(&self->memwriteContext, 4, bytes); } static void writeUInt64Internal(BinarySerializationContext * self, uint64_t value) { unsigned char bytes[8]; if (self->bigEndian) { bytes[0] = value >> 56; bytes[1] = (value >> 48) & 0xFF; bytes[2] = (value >> 40) & 0xFF; bytes[3] = (value >> 32) & 0xFF; bytes[4] = (value >> 24) & 0xFF; bytes[5] = (value >> 16) & 0xFF; bytes[6] = (value >> 8) & 0xFF; bytes[7] = value & 0xFF; } else { bytes[7] = value >> 56; bytes[6] = (value >> 48) & 0xFF; bytes[5] = (value >> 40) & 0xFF; bytes[4] = (value >> 32) & 0xFF; bytes[3] = (value >> 24) & 0xFF; bytes[2] = (value >> 16) & 0xFF; bytes[1] = (value >> 8) & 0xFF; bytes[0] = value & 0xFF; } memwrite(&self->memwriteContext, 8, bytes); } void BinarySerializationContext_beginStructure(BinarySerializationContext * self, const char * key) { struct BinarySerializationContext_containerNode containerNode; if (self->finished) { failWithStatus(SERIALIZATION_ERROR_MULTIPLE_TOP_LEVEL_CONTAINERS, return) } if (self->containerCount > 0) { writePreamble(self, key); } containerNode.type = BINARY_SERIALIZATION_CONTAINER_TYPE_STRUCT; // hush clang containerNode.count = 0; containerNode.startOffset = self->memwriteContext.position; containerNode.keyListSize = 0; containerNode.keys = NULL; containerNode.offsets = NULL; if (self->containerListSize <= self->containerCount) { self->containerListSize *= 2; self->containerStack = realloc(self->containerStack, sizeof(struct BinarySerializationContext_containerNode) * self->containerListSize); } self->containerStack[self->containerCount++] = containerNode; } void BinarySerializationContext_beginDictionary(BinarySerializationContext * self, const char * key) { struct BinarySerializationContext_containerNode containerNode; if (self->finished) { failWithStatus(SERIALIZATION_ERROR_MULTIPLE_TOP_LEVEL_CONTAINERS, return) } if (self->containerCount > 0) { writePreamble(self, key); } containerNode.type = BINARY_SERIALIZATION_CONTAINER_TYPE_DICTIONARY; containerNode.count = 0; containerNode.startOffset = self->memwriteContext.position; containerNode.keyListSize = 1; containerNode.keys = malloc(sizeof(char *) * containerNode.keyListSize); containerNode.offsets = malloc(sizeof(uint32_t) * containerNode.keyListSize); if (self->containerListSize <= self->containerCount) { self->containerListSize *= 2; self->containerStack = realloc(self->containerStack, sizeof(struct BinarySerializationContext_containerNode) * self->containerListSize); } self->containerStack[self->containerCount++] = containerNode; } void BinarySerializationContext_beginArray(BinarySerializationContext * self, const char * key) { struct BinarySerializationContext_containerNode containerNode; if (self->finished) { failWithStatus(SERIALIZATION_ERROR_MULTIPLE_TOP_LEVEL_CONTAINERS, return) } if (self->containerCount > 0) { writePreamble(self, key); } containerNode.type = BINARY_SERIALIZATION_CONTAINER_TYPE_ARRAY; containerNode.count = 0; containerNode.startOffset = self->memwriteContext.position; // hush clang containerNode.keyListSize = 0; containerNode.keys = NULL; containerNode.offsets = NULL; if (self->containerListSize <= self->containerCount) { self->containerListSize *= 2; self->containerStack = realloc(self->containerStack, sizeof(struct BinarySerializationContext_containerNode) * self->containerListSize); } self->containerStack[self->containerCount++] = containerNode; memwrite(&self->memwriteContext, 4, "\x00\x00\x00\x00"); } void BinarySerializationContext_endStructure(BinarySerializationContext * self) { if (self->containerCount == 0) { failWithStatus(SERIALIZATION_ERROR_CONTAINER_UNDERFLOW, return) } if (self->containerStack[self->containerCount - 1].type != BINARY_SERIALIZATION_CONTAINER_TYPE_STRUCT) { failWithStatus(SERIALIZATION_ERROR_CONTAINER_TYPE_MISMATCH, return) } self->containerCount--; if (self->containerCount == 0) { self->finished = true; } } void BinarySerializationContext_endDictionary(BinarySerializationContext * self) { size_t keysSize, valuesSize; uint32_t keyIndex; void * values; if (self->containerCount == 0) { failWithStatus(SERIALIZATION_ERROR_CONTAINER_UNDERFLOW, return) } if (self->containerStack[self->containerCount - 1].type != BINARY_SERIALIZATION_CONTAINER_TYPE_DICTIONARY) { failWithStatus(SERIALIZATION_ERROR_CONTAINER_TYPE_MISMATCH, return) } valuesSize = self->memwriteContext.position - self->containerStack[self->containerCount - 1].startOffset; values = malloc(valuesSize); memcpy(values, self->memwriteContext.data + self->containerStack[self->containerCount - 1].startOffset, valuesSize); keysSize = 0; for (keyIndex = 0; keyIndex < self->containerStack[self->containerCount - 1].count; keyIndex++) { keysSize += strlen(self->containerStack[self->containerCount - 1].keys[keyIndex]) + 5; } self->memwriteContext.position = self->containerStack[self->containerCount - 1].startOffset; writeUInt32Internal(self, self->containerStack[self->containerCount - 1].count); writeUInt32Internal(self, keysSize + valuesSize + 8); for (keyIndex = 0; keyIndex < self->containerStack[self->containerCount - 1].count; keyIndex++) { memwrite(&self->memwriteContext, strlen(self->containerStack[self->containerCount - 1].keys[keyIndex]) + 1, self->containerStack[self->containerCount - 1].keys[keyIndex]); writeUInt32Internal(self, self->containerStack[self->containerCount - 1].offsets[keyIndex] + keysSize + 8); free(self->containerStack[self->containerCount - 1].keys[keyIndex]); } free(self->containerStack[self->containerCount - 1].keys); free(self->containerStack[self->containerCount - 1].offsets); memwrite(&self->memwriteContext, valuesSize, values); free(values); self->containerCount--; if (self->containerCount == 0) { self->finished = true; } } void BinarySerializationContext_endArray(BinarySerializationContext * self) { size_t position; if (self->containerCount == 0) { failWithStatus(SERIALIZATION_ERROR_CONTAINER_UNDERFLOW, return) } if (self->containerStack[self->containerCount - 1].type != BINARY_SERIALIZATION_CONTAINER_TYPE_ARRAY) { failWithStatus(SERIALIZATION_ERROR_CONTAINER_TYPE_MISMATCH, return) } position = self->memwriteContext.position; self->memwriteContext.position = self->containerStack[self->containerCount - 1].startOffset; writeUInt32Internal(self, self->containerStack[self->containerCount - 1].count); self->memwriteContext.position = position; self->containerCount--; if (self->containerCount == 0) { self->finished = true; } } void BinarySerializationContext_writeBoolean(BinarySerializationContext * self, const char * key, bool value) { uint8_t value8; writePreamble(self, key); value8 = !!value; memwrite(&self->memwriteContext, 1, &value8); } void BinarySerializationContext_writeInt8(BinarySerializationContext * self, const char * key, int8_t value) { writePreamble(self, key); memwrite(&self->memwriteContext, 1, &value); } void BinarySerializationContext_writeUInt8(BinarySerializationContext * self, const char * key, uint8_t value) { writePreamble(self, key); memwrite(&self->memwriteContext, 1, &value); } void BinarySerializationContext_writeInt16(BinarySerializationContext * self, const char * key, int16_t value) { writePreamble(self, key); writeUInt16Internal(self, value); } void BinarySerializationContext_writeUInt16(BinarySerializationContext * self, const char * key, uint16_t value) { writePreamble(self, key); writeUInt16Internal(self, value); } void BinarySerializationContext_writeInt32(BinarySerializationContext * self, const char * key, int32_t value) { writePreamble(self, key); writeUInt32Internal(self, value); } void BinarySerializationContext_writeUInt32(BinarySerializationContext * self, const char * key, uint32_t value) { writePreamble(self, key); writeUInt32Internal(self, value); } void BinarySerializationContext_writeInt64(BinarySerializationContext * self, const char * key, int64_t value) { writePreamble(self, key); writeUInt64Internal(self, value); } void BinarySerializationContext_writeUInt64(BinarySerializationContext * self, const char * key, uint64_t value) { writePreamble(self, key); writeUInt64Internal(self, value); } void BinarySerializationContext_writeFloat(BinarySerializationContext * self, const char * key, float value) { union {float f; uint32_t u;} valueUnion = {value}; writePreamble(self, key); writeUInt32Internal(self, valueUnion.u); } void BinarySerializationContext_writeDouble(BinarySerializationContext * self, const char * key, double value) { union {double f; uint64_t u;} valueUnion = {value}; writePreamble(self, key); writeUInt64Internal(self, valueUnion.u); } void BinarySerializationContext_writeFixed16_16(BinarySerializationContext * self, const char * key, fixed16_16 value) { writePreamble(self, key); writeUInt32Internal(self, value); } void BinarySerializationContext_writeEnumeration(BinarySerializationContext * self, const char * key, int value, unsigned int valueCount, Serialization_enumKeyValue * values) { int status = Serialization_checkEnumerationErrors(value, valueCount, values); if (status != SERIALIZATION_ERROR_OK) { failWithStatus(status, return); } writePreamble(self, key); writeUInt32Internal(self, value); } void BinarySerializationContext_writeEnumeration8(BinarySerializationContext * self, const char * key, int8_t value, unsigned int valueCount, Serialization_enumKeyValue * values) { int status = Serialization_checkEnumerationErrors(value, valueCount, values); if (status != SERIALIZATION_ERROR_OK) { failWithStatus(status, return); } writePreamble(self, key); memwrite(&self->memwriteContext, 1, &value); } void BinarySerializationContext_writeEnumeration16(BinarySerializationContext * self, const char * key, int16_t value, unsigned int valueCount, Serialization_enumKeyValue * values) { int status = Serialization_checkEnumerationErrors(value, valueCount, values); if (status != SERIALIZATION_ERROR_OK) { failWithStatus(status, return); } writePreamble(self, key); writeUInt16Internal(self, value); } void BinarySerializationContext_writeEnumeration32(BinarySerializationContext * self, const char * key, int32_t value, unsigned int valueCount, Serialization_enumKeyValue * values) { int status = Serialization_checkEnumerationErrors(value, valueCount, values); if (status != SERIALIZATION_ERROR_OK) { failWithStatus(status, return); } writePreamble(self, key); writeUInt32Internal(self, value); } void BinarySerializationContext_writeBitfield8(BinarySerializationContext * self, const char * key, uint8_t value, unsigned int bitNameCount, Serialization_bitName * bitNames) { int status = Serialization_checkBitfield8Errors(value, bitNameCount, bitNames); if (status != SERIALIZATION_ERROR_OK) { failWithStatus(status, return); } writePreamble(self, key); memwrite(&self->memwriteContext, 1, &value); } void BinarySerializationContext_writeBitfield16(BinarySerializationContext * self, const char * key, uint16_t value, unsigned int bitNameCount, Serialization_bitName * bitNames) { int status = Serialization_checkBitfield16Errors(value, bitNameCount, bitNames); if (status != SERIALIZATION_ERROR_OK) { failWithStatus(status, return); } writePreamble(self, key); writeUInt16Internal(self, value); } void BinarySerializationContext_writeBitfield32(BinarySerializationContext * self, const char * key, uint32_t value, unsigned int bitNameCount, Serialization_bitName * bitNames) { int status = Serialization_checkBitfield32Errors(value, bitNameCount, bitNames); if (status != SERIALIZATION_ERROR_OK) { failWithStatus(status, return); } writePreamble(self, key); writeUInt32Internal(self, value); } void BinarySerializationContext_writeBitfield64(BinarySerializationContext * self, const char * key, uint64_t value, unsigned int bitNameCount, Serialization_bitName * bitNames) { int status = Serialization_checkBitfield64Errors(value, bitNameCount, bitNames); if (status != SERIALIZATION_ERROR_OK) { failWithStatus(status, return); } writePreamble(self, key); writeUInt64Internal(self, value); } void BinarySerializationContext_writeString(BinarySerializationContext * self, const char * key, const char * value) { writePreamble(self, key); memwrite(&self->memwriteContext, strlen(value) + 1, value); } void BinarySerializationContext_writeStringNullable(BinarySerializationContext * self, const char * key, const char * value) { uint8_t controlByte; writePreamble(self, key); if (value == NULL) { controlByte = 0x00; memwrite(&self->memwriteContext, 1, &controlByte); } else { controlByte = 0x01; memwrite(&self->memwriteContext, 1, &controlByte); memwrite(&self->memwriteContext, strlen(value) + 1, value); } } void BinarySerializationContext_writeBlob(BinarySerializationContext * self, const char * key, const void * value, size_t length) { writePreamble(self, key); if (value == NULL) { writeUInt32Internal(self, 0xFFFFFFFFu); } else { writeUInt32Internal(self, length); memwrite(&self->memwriteContext, length, value); } }