/** * @file GoSerializer.c * * @internal * Copyright (C) 2016-2021 by LMI Technologies Inc. * Licensed under the MIT License. * Redistributed files must retain the above copyright notice. */ #include #include #include #include kBeginValueEx(Go, GoSerializerTypeInfo) kAddField(GoSerializerTypeInfo, kType, type) kAddField(GoSerializerTypeInfo, kPointer, version) kAddField(GoSerializerTypeInfo, k16u, id) kEndValueEx() kBeginClassEx(Go, GoSerializer) kAddVMethod(GoSerializer, kObject, VRelease) kAddVMethod(GoSerializer, kSerializer, VInit) kAddVMethod(GoSerializer, kSerializer, VWriteObject) kAddVMethod(GoSerializer, kSerializer, VReadObject) kEndClassEx() GoFx(kStatus) GoSerializer_Construct(GoSerializer* serializer, kStream stream, kAlloc allocator) { kAlloc alloc = kAlloc_Fallback(allocator); kStatus status; kCheck(kAlloc_GetObject(alloc, kTypeOf(GoSerializer), serializer)); if (!kSuccess(status = GoSerializer_VInit(*serializer, kTypeOf(GoSerializer), stream, alloc))) { kAlloc_FreeRef(alloc, serializer); } return status; } GoFx(kStatus) GoSerializer_VInit(GoSerializer serializer, kType type, kStream stream, kAlloc allocator) { kObjR(GoSerializer, serializer); kStatus status; kCheck(kSerializer_VInit(serializer, type, stream, allocator)); obj->typeToInfo = kNULL; obj->idToType = kNULL; kTry { kTest(kStrCopy(obj->base.format, kCountOf(obj->base.format), GO_SERIALIZATION_FORMAT_NAME)); kTest(kSerializer_SetVersion(serializer, kNULL, GoSdk_ProtocolVersion())); kTest(kMap_Construct(&obj->typeToInfo, kTypeOf(kType), kTypeOf(GoSerializerTypeInfo), 0, allocator)); kTest(kMap_Construct(&obj->idToType, kTypeOf(k16u), kTypeOf(GoSerializerTypeInfo), 0, allocator)); kTest(GoSerializer_BuildIdToTypeMap(serializer)); } kCatch(&status) { GoSerializer_VRelease(serializer); kEndCatch(status); } return kOK; } GoFx(kStatus) GoSerializer_VRelease(GoSerializer serializer) { kObj(GoSerializer, serializer); kCheck(kObject_Destroy(obj->typeToInfo)); kCheck(kObject_Destroy(obj->idToType)); kCheck(kSerializer_VRelease(serializer)); return kOK; } GoFx(kStatus) GoSerializer_VWriteObject(GoSerializer serializer, kObject object) { kSize count; kSize i; kCheckArgs(!kIsNull(object) && (kObject_Type(object) == kTypeOf(GoDataSet))); count = GoDataSet_Count(object); for (i = 0; i < count; ++i) { kObject item = GoDataSet_At(object, i); kBool isLast = (i == (count-1)); kSerializerObjectSerializeFx serializeFx = kNULL; kCheck(kSerializer_BeginWrite(serializer, kTypeOf(k32u), kTRUE)); kCheck(GoSerializer_WriteTypeId(serializer, kObject_Type(item), isLast, &serializeFx)); kCheck(serializeFx(item, serializer)); kCheck(kSerializer_EndWrite(serializer)); } kCheck(kSerializer_Flush(serializer)); return kOK; } GoFx(kStatus) GoSerializer_VReadObject(GoSerializer serializer, kObject* object, kAlloc allocator) { kAlloc alloc = kAlloc_Fallback(allocator); GoDataSet output = kNULL; kObject item = kNULL; kBool isLast = kFALSE; kStatus status; kTry { kTest(GoDataSet_Construct(&output, alloc)); while (!isLast) { kTest(GoSerializer_ReadObjectItem(serializer, &isLast, &item, alloc)); kTest(GoDataSet_Add(output, item)); item = kNULL; } *object = output; } kCatch(&status) { kObject_Dispose(item); kObject_Dispose(output); kEndCatch(status); } return kOK; } GoFx(kStatus) GoSerializer_WriteTypeId(GoSerializer serializer, kType type, kBool isLast, kSerializerObjectSerializeFx* fx) { kObj(GoSerializer, serializer); GoSerializerTypeInfo info; k16u typeId; if (!kSuccess(kMap_FindT(obj->typeToInfo, &type, &info))) { info.type = type; kCheck(kSerializer_FindCompatibleVersion(serializer, type, &info.version)); info.id = (k16u) atoi(kType_VersionGuid(info.type, info.version)); kCheck(kMap_AddT(obj->typeToInfo, &type, &info)); } typeId = (k16u) ((isLast << 15) | info.id); kCheck(kSerializer_Write16u(serializer, typeId)); *fx = (kSerializerObjectSerializeFx) kType_VersionSerializeFx(info.type, info.version); return kOK; } GoFx(kStatus) GoSerializer_ReadTypeId(GoSerializer serializer, kType* type, kBool* isLast, kSerializerObjectDeserializeFx* fx) { kObj(GoSerializer, serializer); GoSerializerTypeInfo info; k16u typeId; k16u id; kCheck(kSerializer_Read16u(serializer, &typeId)); id = typeId & 0x7FFF; *isLast = (typeId >> 15); kCheck(kMap_FindT(obj->idToType, &id, &info)); *type = info.type; *fx = (kSerializerObjectDeserializeFx) kType_VersionDeserializeFx(info.type, info.version); return kOK; } GoFx(kStatus) GoSerializer_ConstructItem(GoSerializer serializer, kObject* item, kType type, kSerializerObjectDeserializeFx initializer, kAlloc alloc) { kStatus status; kCheck(kAlloc_GetObject(alloc, type, item)); if (!kSuccess(status = initializer(*item, serializer, alloc))) { kAlloc_FreeRef(alloc, item); } return status; } GoFx(kStatus) GoSerializer_BuildIdToTypeMap(GoSerializer serializer) { kObj(GoSerializer, serializer); kAssembly assembly = kAssemblyOf(GoSdk); kSize typeCount = kAssembly_TypeCount(assembly); GoSerializerTypeInfo info; kSize i, j; kCheck(kMap_Clear(obj->idToType)); for (i = 0; i < typeCount; ++i) { kType type = kAssembly_TypeAt(assembly, i); kSize versionCount = kType_VersionCount(type); for (j = 0; j < versionCount; ++j) { kTypeVersion version = kType_VersionAt(type, j); if (kStrEquals(kType_VersionFormat(type, version), GO_SERIALIZATION_FORMAT_NAME)) { const kChar* guid = kType_VersionGuid(type, version); info.type = type; info.version = version; info.id = (k16u) atoi(guid); kCheck(kMap_AddT(obj->idToType, &info.id, &info)); } } } return kOK; } GoFx(kStatus) GoSerializer_ReadObjectItem(GoSerializer serializer, kBool* isLast, kObject* item, kAlloc allocator) { kType itemType = kNULL; kSerializerObjectDeserializeFx deserializeFx = kNULL; // Read and store the message attributes data length ("attrSize") // to determine how many attributes to read. // The size field is included in the message size count stored in the size field. // // If this fails, the EndRead() could assert so skip the EndRead() if BeginRead() fails. kCheck(kSerializer_BeginRead(serializer, kTypeOf(k32u), kTRUE)); kTry { kTest(GoSerializer_ReadTypeId(serializer, &itemType, isLast, &deserializeFx)); kTest(GoSerializer_ConstructItem(serializer, item, itemType, deserializeFx, allocator)); } kFinally { // Automatically flush any unread data and free serializer read section. // Ignore errors. kSerializer_EndRead(serializer); kEndFinally(); } return kOK; }