/* Copyright (c) 2014 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 */ #ifndef __DeserializationContext_H__ #define __DeserializationContext_H__ #ifdef __cplusplus extern "C" { #endif typedef struct DeserializationContext DeserializationContext; #define DeserializationContext_superclass StemObject #include "gamemath/FixedPoint.h" #include "serialization/SerializationShared.h" #include "stemobject/StemObject.h" #include #include #include // Deprecated; implement _deserializeMinimal() instead of _loadSerializedData(), and use // deserialize_implementation_v2() instead of stemobject_deserialize_implementation() #define stemobject_deserialize_implementation(load_suffix, ...) \ DeserializationContext * context = deserializationContext; \ stemobject_implementation * self = malloc(sizeof(stemobject_implementation)); \ self->vtable = &stemobject_cat(stemobject_implementation, class); \ self->protected_ivar(allocated) = false; \ if (!stemobject_cat(stemobject_implementation, load_suffix)(self, context, ##__VA_ARGS__)) { \ free(self); \ return NULL; \ } \ self->protected_ivar(allocated) = true; \ \ return self; // Assumptions: // - In its full serialized form, object is written as a top-level structure with format_type and format_version fields. // - An untyped DeserializationContext is passed to the function using this macro, with the name deserializationContext. // - Constant *_FORMAT_TYPE is defined with the shared prefix given by format_type_version_prefix. // - object_type is both the type name of the object being deserialized, and the prefix for a _deserializeMinimal() function. // - _deserializeMinimal() takes the object as its first argument, the format version as a uint16_t as its second argument, // and any additional arguments in the same order as _deserialize() (or the function using this macro). // - The implementation of _deserializeMinimal() checks the value of formatVersion, and returns the appropriate error if // it's incompatible. // - A _dispose() function is defined for object_type. It's not necessary for object_type to be a StemObject as long as // the naming convention is compatible. #define deserialize_implementation_v2(object_type, format_type_version_prefix, ...) \ DeserializationContext * context = deserializationContext; \ call_virtual(beginStructure, context, stemobject_cat(format_type_version_prefix, FORMAT_TYPE)); \ const char * formatType = call_virtual(readString, context, "format_type"); \ if (context->status != SERIALIZATION_ERROR_OK) { \ return NULL; \ } \ if (strcmp(formatType, stemobject_cat(format_type_version_prefix, FORMAT_TYPE))) { \ context->status = SERIALIZATION_ERROR_FORMAT_TYPE_MISMATCH; \ return NULL; \ } \ unsigned int formatVersion = call_virtual(readUInt16, context, "format_version"); \ object_type * object = object_type##_deserializeMinimal(context, formatVersion, ##__VA_ARGS__); \ call_virtual(endStructure, context); \ if (object != NULL && context->status != SERIALIZATION_ERROR_OK) { \ object_type##_dispose(object); \ return NULL; \ } \ return object; // Same as deserialize_implementation_v2, but for deserializeMinimal functions that take a struct pointer and return a // success boolean instead of an allocated object. #define deserialize_implementation_v2_nonobject(struct_type, result, format_type_version_prefix, ...) \ DeserializationContext * context = deserializationContext; \ call_virtual(beginStructure, context, stemobject_cat(format_type_version_prefix, FORMAT_TYPE)); \ const char * formatType = call_virtual(readString, context, "format_type"); \ if (context->status != SERIALIZATION_ERROR_OK) { \ return NULL; \ } \ if (strcmp(formatType, stemobject_cat(format_type_version_prefix, FORMAT_TYPE))) { \ context->status = SERIALIZATION_ERROR_FORMAT_TYPE_MISMATCH; \ return NULL; \ } \ unsigned int formatVersion = call_virtual(readUInt16, context, "format_version"); \ bool success = struct_type##_deserializeMinimal(result, context, formatVersion, ##__VA_ARGS__); \ call_virtual(endStructure, context); \ if (success && context->status != SERIALIZATION_ERROR_OK) { \ struct_type##_dispose(result); \ return false; \ } \ return success; #define DeserializationContext_ivars \ StemObject_ivars \ \ jmp_buf * jmpBuf; \ int status; #define DeserializationContext_vtable(self_type) \ StemObject_vtable(self_type) \ \ const char * (* errorString)(int status); \ \ /* Clears all state and restarts from the beginning of deserialization, as though the context had just been newly created. */ \ void (* reset)(self_type * self); \ \ void (* beginStructure)(self_type * self, const char * key); \ /* Implementations must return the number of elements in the container */ \ size_t (* beginDictionary)(self_type * self, const char * key); \ size_t (* beginArray)(self_type * self, const char * key); \ \ void (* endStructure)(self_type * self); \ void (* endDictionary)(self_type * self); \ void (* endArray)(self_type * self); \ \ bool (* readBoolean)(self_type * self, const char * key); \ int8_t (* readInt8)(self_type * self, const char * key); \ uint8_t (* readUInt8)(self_type * self, const char * key); \ int16_t (* readInt16)(self_type * self, const char * key); \ uint16_t (* readUInt16)(self_type * self, const char * key); \ int32_t (* readInt32)(self_type * self, const char * key); \ uint32_t (* readUInt32)(self_type * self, const char * key); \ int64_t (* readInt64)(self_type * self, const char * key); \ uint64_t (* readUInt64)(self_type * self, const char * key); \ float (* readFloat)(self_type * self, const char * key); \ double (* readDouble)(self_type * self, const char * key); \ fixed16_16 (* readFixed16_16)(self_type * self, const char * key); \ \ /* All possible values of the value argument must be represented in values. Writing an unrepresented value */ \ /* results in status being set to SERIALIZATION_ERROR_ENUM_NOT_NAMED and serialization failing. Keys and */ \ /* values must also be unique (SERIALIZATION_ERROR_DUPLICATE_ENUM_NAME, SERIALIZATION_ERROR_DUPLICATE_ENUM_VALUE). */ \ int (* readEnumeration)(self_type * self, const char * key, unsigned int valueCount, Serialization_enumKeyValue * values); \ int8_t (* readEnumeration8)(self_type * self, const char * key, unsigned int valueCount, Serialization_enumKeyValue * values); \ int16_t (* readEnumeration16)(self_type * self, const char * key, unsigned int valueCount, Serialization_enumKeyValue * values); \ int32_t (* readEnumeration32)(self_type * self, const char * key, unsigned int valueCount, Serialization_enumKeyValue * values); \ \ /* All possible bits that can be set must be represented in bitNames. Reading a value where an unnamed bit is set */ \ /* results in status being set to SERIALIZATION_ERROR_UNNAMED_BIT. Bit names and indexes must also be unique */ \ /* (SERIALIZATION_ERROR_DUPLICATE_BIT). */ \ uint8_t (* readBitfield8)(self_type * self, const char * key, unsigned int bitNameCount, Serialization_bitName * bitNames); \ uint16_t (* readBitfield16)(self_type * self, const char * key, unsigned int bitNameCount, Serialization_bitName * bitNames); \ uint32_t (* readBitfield32)(self_type * self, const char * key, unsigned int bitNameCount, Serialization_bitName * bitNames); \ uint64_t (* readBitfield64)(self_type * self, const char * key, unsigned int bitNameCount, Serialization_bitName * bitNames); \ \ /* Returned pointers not owned by caller; do not free */ \ const char * (* readString)(self_type * self, const char * key); \ const char * (* readStringNullable)(self_type * self, const char * key); \ const void * (* readBlob)(self_type * self, const char * key, size_t * outLength); \ \ /* Returned string not owned by caller; do not free */ \ /* Valid only when reading an ordered dictionary */ \ const char * (* readNextDictionaryKey)(self_type * self); \ \ /* Valid only when reading an unordered dictionary */ \ bool (* hasDictionaryKey)(self_type * self, const char * key); stemobject_declare(DeserializationContext) bool DeserializationContext_init(DeserializationContext * self); void DeserializationContext_dispose(DeserializationContext * self); #ifdef __cplusplus } #endif #endif