Files
FlaxEngine/Source/Engine/Serialization/Stream.cpp
Wojtek Figat ed908a260e Add support for Double2/3/4 for Variant
Increase size to 40 bytes to store Double3 without allocations
Change Ray and BoundingBox to be stored inlined in BoundingBox
Use safer accessors to read Variant explicit data type
2022-05-20 18:10:35 +02:00

842 lines
21 KiB
C++

// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#include "ReadStream.h"
#include "WriteStream.h"
#include "JsonWriters.h"
#include "JsonSerializer.h"
#include "MemoryReadStream.h"
#include "Engine/Core/Types/CommonValue.h"
#include "Engine/Core/Types/Variant.h"
#include "Engine/Core/Math/Double2.h"
#include "Engine/Core/Math/Double3.h"
#include "Engine/Core/Math/Double4.h"
#include "Engine/Core/Collections/Dictionary.h"
#include "Engine/Content/Asset.h"
#include "Engine/Core/Cache.h"
#include "Engine/Debug/Exceptions/JsonParseException.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Scripting/ManagedSerialization.h"
#include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/ScriptingObject.h"
#include "Engine/Scripting/ScriptingObjectReference.h"
#include "Engine/Scripting/ManagedCLR/MCore.h"
#include "Engine/Scripting/ManagedCLR/MUtils.h"
void ReadStream::ReadStringAnsi(StringAnsi* data)
{
int32 length;
ReadInt32(&length);
if (length < 0 || length > STREAM_MAX_STRING_LENGTH)
{
_hasError = true;
*data = "";
return;
}
data->ReserveSpace(length);
if (length == 0)
return;
char* ptr = data->Get();
ASSERT(ptr != nullptr);
Read(ptr, length);
}
void ReadStream::ReadStringAnsi(StringAnsi* data, int8 lock)
{
int32 length;
ReadInt32(&length);
if (length < 0 || length > STREAM_MAX_STRING_LENGTH)
{
_hasError = true;
*data = "";
return;
}
data->ReserveSpace(length);
if (length == 0)
return;
char* ptr = data->Get();
ASSERT(ptr != nullptr);
Read(ptr, length);
for (int32 i = 0; i < length; i++)
{
*ptr = *ptr ^ lock;
ptr++;
}
}
void ReadStream::ReadString(String* data)
{
int32 length;
ReadInt32(&length);
if (length <= 0 || length > STREAM_MAX_STRING_LENGTH)
{
if (length != 0)
_hasError = true;
data->Clear();
return;
}
data->ReserveSpace(length);
Char* ptr = data->Get();
ASSERT(ptr != nullptr);
Read(ptr, length);
}
void ReadStream::ReadString(String* data, int16 lock)
{
int32 length;
ReadInt32(&length);
if (length <= 0 || length > STREAM_MAX_STRING_LENGTH)
{
if (length != 0)
_hasError = true;
data->Clear();
return;
}
data->ReserveSpace(length);
Char* ptr = data->Get();
ASSERT(ptr != nullptr);
Read(ptr, length);
for (int32 i = 0; i < length; i++)
{
*ptr = *ptr ^ lock;
ptr++;
}
}
void ReadStream::ReadCommonValue(CommonValue* data)
{
byte type;
ReadByte(&type);
switch (static_cast<CommonType>(type))
{
case CommonType::Bool:
data->Set(ReadBool());
break;
case CommonType::Integer:
{
int32 v;
ReadInt32(&v);
data->Set(v);
}
break;
case CommonType::Float:
{
float v;
ReadFloat(&v);
data->Set(v);
}
break;
case CommonType::Vector2:
{
Vector2 v;
Read(&v);
data->Set(v);
}
break;
case CommonType::Vector3:
{
Vector3 v;
Read(&v);
data->Set(v);
}
break;
case CommonType::Vector4:
{
Vector4 v;
Read(&v);
data->Set(v);
}
break;
case CommonType::Color:
{
Color v;
Read(&v);
data->Set(v);
}
break;
case CommonType::Guid:
{
Guid v;
Read(&v);
data->Set(v);
}
break;
case CommonType::String:
{
String v;
ReadString(&v, 953);
data->Set(v);
}
break;
case CommonType::Box:
{
BoundingBox v;
Read(&v);
data->Set(v);
}
break;
case CommonType::Rotation:
{
Quaternion v;
Read(&v);
data->Set(v);
}
break;
case CommonType::Transform:
{
Transform v;
Read(&v);
data->Set(v);
}
break;
case CommonType::Sphere:
{
BoundingSphere v;
Read(&v);
data->Set(v);
}
break;
case CommonType::Rectangle:
{
Rectangle v;
Read(&v);
data->Set(v);
}
case CommonType::Ray:
{
Ray v;
Read(&v);
data->Set(v);
}
break;
case CommonType::Matrix:
{
Matrix v;
Read(&v);
data->Set(v);
}
break;
case CommonType::Blob:
{
int32 length;
Read(&length);
data->SetBlob(length);
if (length > 0)
{
ReadBytes(data->AsBlob.Data, length);
}
}
break;
default: CRASH;
}
}
void ReadStream::ReadVariantType(VariantType* data)
{
*data = VariantType((VariantType::Types)ReadByte());
int32 typeNameLength;
ReadInt32(&typeNameLength);
if (typeNameLength == MAX_int32)
{
ReadInt32(&typeNameLength);
if (typeNameLength == 0)
return;
data->TypeName = static_cast<char*>(Allocator::Allocate(typeNameLength + 1));
char* ptr = data->TypeName;
Read(ptr, typeNameLength);
for (int32 i = 0; i < typeNameLength; i++)
{
*ptr = *ptr ^ 77;
ptr++;
}
*ptr = 0;
}
else if (typeNameLength > 0)
{
// [Deprecated on 27.08.2020, expires on 27.08.2021]
ASSERT(typeNameLength < STREAM_MAX_STRING_LENGTH);
Array<Char> chars;
chars.Resize(typeNameLength + 1);
Char* ptr = chars.Get();
Read(ptr, typeNameLength);
for (int32 i = 0; i < typeNameLength; i++)
{
*ptr = *ptr ^ 77;
ptr++;
}
*ptr = 0;
data->TypeName = static_cast<char*>(Allocator::Allocate(typeNameLength + 1));
StringUtils::ConvertUTF162ANSI(chars.Get(), data->TypeName, typeNameLength);
data->TypeName[typeNameLength] = 0;
}
}
void ReadStream::ReadVariant(Variant* data)
{
VariantType type;
ReadVariantType(&type);
data->SetType(MoveTemp(type));
switch (data->Type.Type)
{
case VariantType::Null:
case VariantType::Void:
break;
case VariantType::Bool:
data->AsBool = ReadBool();
break;
case VariantType::Int16:
ReadInt16(&data->AsInt16);
break;
case VariantType::Uint16:
ReadUint16(&data->AsUint16);
break;
case VariantType::Int:
ReadInt32(&data->AsInt);
break;
case VariantType::Uint:
ReadUint32(&data->AsUint);
break;
case VariantType::Int64:
ReadInt64(&data->AsInt64);
break;
case VariantType::Uint64:
case VariantType::Enum:
ReadUint64(&data->AsUint64);
break;
case VariantType::Float:
ReadFloat(&data->AsFloat);
break;
case VariantType::Double:
ReadDouble(&data->AsDouble);
break;
case VariantType::Pointer:
{
uint64 asUint64;
ReadUint64(&asUint64);
data->AsPointer = (void*)(uintptr)asUint64;
break;
}
case VariantType::String:
{
int32 length;
ReadInt32(&length);
ASSERT(length < STREAM_MAX_STRING_LENGTH);
const int32 dataLength = length * sizeof(Char) + 2;
if (data->AsBlob.Length != dataLength)
{
Allocator::Free(data->AsBlob.Data);
data->AsBlob.Data = dataLength > 0 ? Allocator::Allocate(dataLength) : nullptr;
data->AsBlob.Length = dataLength;
}
Char* ptr = (Char*)data->AsBlob.Data;
Read(ptr, length);
for (int32 i = 0; i < length; i++)
{
*ptr = *ptr ^ -14;
ptr++;
}
*ptr = 0;
break;
}
case VariantType::Object:
{
Guid id;
Read(&id);
data->SetObject(FindObject(id, ScriptingObject::GetStaticClass()));
break;
}
case VariantType::ManagedObject:
case VariantType::Structure:
{
const byte format = ReadByte();
if (format == 0)
{
// No data
}
else if (format == 1)
{
// Json
StringAnsi json;
ReadStringAnsi(&json, -71);
#if USE_MONO
MCore::AttachThread();
MonoClass* klass = MUtils::GetClass(data->Type);
if (!klass)
{
LOG(Error, "Invalid variant type {0}", data->Type);
return;
}
MonoObject* obj = mono_object_new(mono_domain_get(), klass);
if (!obj)
{
LOG(Error, "Failed to managed instance of the variant type {0}", data->Type);
return;
}
if (!mono_class_is_valuetype(klass))
mono_runtime_object_init(obj);
ManagedSerialization::Deserialize(json, obj);
if (data->Type.Type == VariantType::ManagedObject)
data->SetManagedObject(obj);
else
*data = MUtils::UnboxVariant(obj);
#endif
}
else
{
LOG(Error, "Invalid Variant {0) format {1}", data->Type.ToString(), format);
}
break;
}
case VariantType::Blob:
{
int32 length;
ReadInt32(&length);
data->SetBlob(length);
ReadBytes(data->AsBlob.Data, length);
break;
}
case VariantType::Asset:
{
Guid id;
Read(&id);
data->SetAsset(LoadAsset(id, Asset::TypeInitializer));
break;
}
case VariantType::Vector2:
ReadBytes(&data->AsData, sizeof(Vector2));
break;
case VariantType::Vector3:
ReadBytes(&data->AsData, sizeof(Vector3));
break;
case VariantType::Vector4:
ReadBytes(&data->AsData, sizeof(Vector4));
break;
case VariantType::Double2:
ReadBytes(&data->AsData, sizeof(Double2));
break;
case VariantType::Double3:
ReadBytes(&data->AsData, sizeof(Double3));
break;
case VariantType::Double4:
ReadBytes(data->AsBlob.Data, sizeof(Double4));
break;
case VariantType::Color:
ReadBytes(&data->AsData, sizeof(Color));
break;
case VariantType::Guid:
ReadBytes(&data->AsData, sizeof(Guid));
break;
case VariantType::BoundingBox:
ReadBytes(&data->AsData, sizeof(BoundingBox));
break;
case VariantType::BoundingSphere:
ReadBytes(&data->AsData, sizeof(BoundingSphere));
break;
case VariantType::Quaternion:
ReadBytes(&data->AsData, sizeof(Quaternion));
break;
case VariantType::Transform:
ReadBytes(data->AsBlob.Data, sizeof(Transform));
break;
case VariantType::Rectangle:
ReadBytes(&data->AsData, sizeof(Rectangle));
break;
case VariantType::Ray:
ReadBytes(&data->AsData, sizeof(Ray));
break;
case VariantType::Matrix:
ReadBytes(data->AsBlob.Data, sizeof(Matrix));
break;
case VariantType::Array:
{
int32 count;
ReadInt32(&count);
auto& array = *(Array<Variant>*)data->AsData;
array.Resize(count);
for (int32 i = 0; i < count; i++)
ReadVariant(&array[i]);
break;
}
case VariantType::Dictionary:
{
int32 count;
ReadInt32(&count);
auto& dictionary = *data->AsDictionary;
dictionary.Clear();
dictionary.EnsureCapacity(count);
for (int32 i = 0; i < count; i++)
{
Variant key;
ReadVariant(&key);
ReadVariant(&dictionary[MoveTemp(key)]);
}
break;
}
case VariantType::Typename:
{
int32 length;
ReadInt32(&length);
ASSERT(length < STREAM_MAX_STRING_LENGTH);
const int32 dataLength = length + 1;
if (data->AsBlob.Length != dataLength)
{
Allocator::Free(data->AsBlob.Data);
data->AsBlob.Data = dataLength > 0 ? Allocator::Allocate(dataLength) : nullptr;
data->AsBlob.Length = dataLength;
}
char* ptr = (char*)data->AsBlob.Data;
Read(ptr, length);
for (int32 i = 0; i < length; i++)
{
*ptr = *ptr ^ -14;
ptr++;
}
*ptr = 0;
break;
}
default:
CRASH;
}
}
void ReadStream::ReadJson(ISerializable* obj)
{
int32 engineBuild, size;
ReadInt32(&engineBuild);
ReadInt32(&size);
if (obj)
{
if (const auto memoryStream = dynamic_cast<MemoryReadStream*>(this))
{
JsonSerializer::LoadFromBytes(obj, Span<byte>((byte*)memoryStream->Read(size), size), engineBuild);
}
else
{
void* data = Allocator::Allocate(size);
ReadBytes(data, size);
JsonSerializer::LoadFromBytes(obj, Span<byte>((byte*)data, size), engineBuild);
Allocator::Free(data);
}
}
else
SetPosition(GetPosition() + size);
}
void WriteStream::WriteText(const StringView& text)
{
WriteBytes(text.Get(), sizeof(Char) * text.Length());
}
void WriteStream::WriteText(const StringAnsiView& text)
{
WriteBytes(text.Get(), sizeof(char) * text.Length());
}
void WriteStream::WriteString(const StringView& data)
{
const int32 length = data.Length();
ASSERT(length < STREAM_MAX_STRING_LENGTH);
WriteInt32(length);
Write(*data, length);
}
void WriteStream::WriteString(const StringView& data, int16 lock)
{
ASSERT(data.Length() < STREAM_MAX_STRING_LENGTH);
WriteInt32(data.Length());
for (int32 i = 0; i < data.Length(); i++)
WriteUint16((uint16)((uint16)data[i] ^ lock));
}
void WriteStream::WriteStringAnsi(const StringAnsiView& data)
{
const int32 length = data.Length();
ASSERT(length < STREAM_MAX_STRING_LENGTH);
WriteInt32(length);
Write(data.Get(), length);
}
void WriteStream::WriteStringAnsi(const StringAnsiView& data, int8 lock)
{
const int32 length = data.Length();
ASSERT(length < STREAM_MAX_STRING_LENGTH);
WriteInt32(length);
for (int32 i = 0; i < length; i++)
WriteUint8((uint8)((uint8)data[i] ^ lock));
}
void WriteStream::WriteCommonValue(const CommonValue& data)
{
WriteByte(static_cast<byte>(data.Type));
switch (data.Type)
{
case CommonType::Bool:
WriteBool(data.AsBool);
break;
case CommonType::Integer:
WriteInt32(data.AsInteger);
break;
case CommonType::Float:
WriteFloat(data.AsFloat);
break;
case CommonType::Vector2:
Write(&data.AsVector2);
break;
case CommonType::Vector3:
Write(&data.AsVector3);
break;
case CommonType::Vector4:
Write(&data.AsVector4);
break;
case CommonType::Color:
Write(&data.AsColor);
break;
case CommonType::Guid:
Write(&data.AsGuid);
break;
case CommonType::String:
WriteString(data.AsString, 953);
break;
case CommonType::Box:
Write(&data.AsBox);
break;
case CommonType::Rotation:
Write(&data.AsRotation);
break;
case CommonType::Transform:
Write(&data.AsTransform);
break;
case CommonType::Sphere:
Write(&data.AsSphere);
break;
case CommonType::Rectangle:
Write(&data.AsRectangle);
break;
case CommonType::Ray:
Write(&data.AsRay);
break;
case CommonType::Matrix:
Write(&data.AsMatrix);
break;
case CommonType::Blob:
WriteInt32(data.AsBlob.Length);
if (data.AsBlob.Length > 0)
WriteBytes(data.AsBlob.Data, data.AsBlob.Length);
break;
default: CRASH;
}
}
void WriteStream::WriteVariantType(const VariantType& data)
{
WriteByte((byte)data.Type);
WriteInt32(MAX_int32);
WriteStringAnsi(StringAnsiView(data.TypeName), 77);
}
void WriteStream::WriteVariant(const Variant& data)
{
WriteVariantType(data.Type);
Guid id;
switch (data.Type.Type)
{
case VariantType::Null:
case VariantType::Void:
break;
case VariantType::Bool:
WriteBool(data.AsBool);
break;
case VariantType::Int16:
WriteInt16(data.AsInt16);
break;
case VariantType::Uint16:
WriteUint16(data.AsUint16);
break;
case VariantType::Int:
WriteInt32(data.AsInt);
break;
case VariantType::Uint:
WriteUint32(data.AsUint);
break;
case VariantType::Int64:
WriteInt64(data.AsInt64);
break;
case VariantType::Uint64:
case VariantType::Enum:
WriteUint64(data.AsUint64);
break;
case VariantType::Float:
WriteFloat(data.AsFloat);
break;
case VariantType::Double:
WriteDouble(data.AsDouble);
break;
case VariantType::Pointer:
WriteUint64((uint64)(uintptr)data.AsPointer);
break;
case VariantType::String:
WriteString((StringView)data, -14);
break;
case VariantType::Object:
id = data.AsObject ? data.AsObject->GetID() : Guid::Empty;
Write(&id);
break;
case VariantType::Blob:
WriteInt32(data.AsBlob.Length);
WriteBytes(data.AsBlob.Data, data.AsBlob.Length);
break;
case VariantType::BoundingBox:
WriteBytes(data.AsData, sizeof(BoundingBox));
break;
case VariantType::Transform:
WriteBytes(data.AsBlob.Data, sizeof(Transform));
break;
case VariantType::Ray:
WriteBytes(data.AsData, sizeof(Ray));
break;
case VariantType::Matrix:
WriteBytes(data.AsBlob.Data, sizeof(Matrix));
break;
case VariantType::Asset:
id = data.AsAsset ? data.AsAsset->GetID() : Guid::Empty;
Write(&id);
break;
case VariantType::Vector2:
WriteBytes(data.AsData, sizeof(Vector2));
break;
case VariantType::Vector3:
WriteBytes(data.AsData, sizeof(Vector3));
break;
case VariantType::Vector4:
WriteBytes(data.AsData, sizeof(Vector4));
break;
case VariantType::Double2:
WriteBytes(data.AsData, sizeof(Double2));
break;
case VariantType::Double3:
WriteBytes(data.AsData, sizeof(Double3));
break;
case VariantType::Double4:
WriteBytes(data.AsBlob.Data, sizeof(Double4));
break;
case VariantType::Color:
WriteBytes(data.AsData, sizeof(Color));
break;
case VariantType::Guid:
WriteBytes(data.AsData, sizeof(Guid));
break;
case VariantType::Quaternion:
WriteBytes(data.AsData, sizeof(Quaternion));
break;
case VariantType::Rectangle:
WriteBytes(data.AsData, sizeof(Rectangle));
break;
case VariantType::BoundingSphere:
WriteBytes(data.AsData, sizeof(BoundingSphere));
break;
case VariantType::Array:
id.A = ((Array<Variant>*)data.AsData)->Count();
WriteInt32(id.A);
for (uint32 i = 0; i < id.A; i++)
WriteVariant(((Array<Variant>*)data.AsData)->At(i));
break;
case VariantType::Dictionary:
WriteInt32(data.AsDictionary->Count());
for (auto i = data.AsDictionary->Begin(); i.IsNotEnd(); ++i)
{
WriteVariant(i->Key);
WriteVariant(i->Value);
}
break;
case VariantType::Typename:
WriteStringAnsi((StringAnsiView)data, -14);
break;
case VariantType::ManagedObject:
case VariantType::Structure:
{
#if USE_MONO
MonoObject* obj;
if (data.Type.Type == VariantType::Structure)
obj = MUtils::BoxVariant(data);
else
obj = (MonoObject*)data;
if (obj)
{
WriteByte(1);
rapidjson_flax::StringBuffer json;
CompactJsonWriter writerObj(json);
MCore::AttachThread();
ManagedSerialization::Serialize(writerObj, obj);
WriteStringAnsi(StringAnsiView(json.GetString(), (int32)json.GetSize()), -71);
}
else
#endif
{
WriteByte(0);
}
break;
}
default:
CRASH;
}
}
void WriteStream::WriteJson(ISerializable* obj, const void* otherObj)
{
WriteInt32(FLAXENGINE_VERSION_BUILD);
if (obj)
{
rapidjson_flax::StringBuffer buffer;
CompactJsonWriter writer(buffer);
writer.StartObject();
obj->Serialize(writer, otherObj);
writer.EndObject();
WriteInt32((int32)buffer.GetSize());
WriteBytes((byte*)buffer.GetString(), (int32)buffer.GetSize());
}
else
WriteInt32(0);
}
Array<byte> JsonSerializer::SaveToBytes(ISerializable* obj)
{
Array<byte> result;
if (obj)
{
rapidjson_flax::StringBuffer buffer;
CompactJsonWriter writer(buffer);
writer.StartObject();
obj->Serialize(writer, nullptr);
writer.EndObject();
result.Set((byte*)buffer.GetString(), (int32)buffer.GetSize());
}
return result;
}
void JsonSerializer::LoadFromBytes(ISerializable* obj, const Span<byte>& data, int32 engineBuild)
{
if (!obj || data.Length() == 0)
return;
ISerializable::SerializeDocument document;
{
PROFILE_CPU_NAMED("Json.Parse");
document.Parse((const char*)data.Get(), data.Length());
}
if (document.HasParseError())
{
Log::JsonParseException(document.GetParseError(), document.GetErrorOffset());
return;
}
auto modifier = Cache::ISerializeModifier.Get();
modifier->EngineBuild = engineBuild;
obj->Deserialize(document, modifier.Value);
}