// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Asio/Asio.h" #include "CborReader.h" #include "CborWriter.h" #include "Containers/Array.h" #include "Containers/ContainerAllocationPolicies.h" #include "Containers/StringView.h" #include "Serialization/MemoryArchive.h" #include "Serialization/MemoryReader.h" namespace UE { namespace Trace { //////////////////////////////////////////////////////////////////////////////// template struct TInlineMemoryWriter : public FMemoryArchive { typedef TArray> BufferType; TInlineMemoryWriter(); virtual void Serialize(void* Data, int64 Num) override; BufferType Buffer; }; //////////////////////////////////////////////////////////////////////////////// template inline TInlineMemoryWriter::TInlineMemoryWriter() { SetIsSaving(true); } //////////////////////////////////////////////////////////////////////////////// template inline void TInlineMemoryWriter::Serialize(void* Data, int64 Num) { Buffer.Append((const uint8*)Data, IntCastChecked(Num)); } //////////////////////////////////////////////////////////////////////////////// struct FPayload { const uint8* Data; uint32 Size; }; //////////////////////////////////////////////////////////////////////////////// enum class EStatusCode { Unknown = 0, Success = 200, BadRequest = 400, NotFound = 404, InternalError = 500, }; //////////////////////////////////////////////////////////////////////////////// template class TPayloadBuilder { public: TPayloadBuilder(EStatusCode StatusCode); template TPayloadBuilder(const char (&Path)[N]); template void AddInteger(const char (&Name)[N], int64 Value); template void AddString(const char (&Name)[N], const char* Value, int32 Length=-1); template void AddString(const char (&Name)[N], const FStringView& String); template void AddStringArray(const char(&Name)[N], const TArray& Values); template void AddArray(const char(&Name)[N], const TArrayView& Values); FPayload Done(); private: TInlineMemoryWriter MemoryWriter; FCborWriter CborWriter = { &MemoryWriter, ECborEndianness::StandardCompliant }; }; //////////////////////////////////////////////////////////////////////////////// template inline TPayloadBuilder::TPayloadBuilder(EStatusCode StatusCode) { CborWriter.WriteContainerStart(ECborCode::Map, -1); AddInteger("$status", int32(StatusCode)); } //////////////////////////////////////////////////////////////////////////////// template template inline TPayloadBuilder::TPayloadBuilder(const char (&Path)[N]) { CborWriter.WriteContainerStart(ECborCode::Map, -1); AddString("$request", "GET"); AddString("$path", Path, N - 1); } //////////////////////////////////////////////////////////////////////////////// template template inline void TPayloadBuilder::AddInteger(const char (&Name)[N], int64 Value) { CborWriter.WriteValue(Name, N - 1); CborWriter.WriteValue(Value); } //////////////////////////////////////////////////////////////////////////////// template template inline void TPayloadBuilder::AddString( const char (&Name)[N], const char* Value, int Length) { Length = (Length < 0) ? int32(strlen(Value)) : Length; CborWriter.WriteValue(Name, N - 1); CborWriter.WriteValue(Value, Length); } //////////////////////////////////////////////////////////////////////////////// template template void TPayloadBuilder::AddString(const char(& Name)[N], const FStringView& String) { CborWriter.WriteValue(Name, N - 1); CborWriter.WriteValue(String); } //////////////////////////////////////////////////////////////////////////////// template template inline void TPayloadBuilder::AddStringArray(const char(&Name)[N], const TArray& Values) { CborWriter.WriteValue(Name, N - 1); CborWriter.WriteContainerStart(ECborCode::Array, Values.Num()); for (const FString& Elem : Values) { CborWriter.WriteValue(Elem); } } //////////////////////////////////////////////////////////////////////////////// template template void TPayloadBuilder::AddArray(const char(& Name)[N], const TArrayView& Values) { CborWriter.WriteValue(Name, N - 1); CborWriter.WriteContainerStart(ECborCode::Array, Values.Num()); for (const T& Elem : Values) { CborWriter.WriteValue(Elem); } } //////////////////////////////////////////////////////////////////////////////// template inline FPayload TPayloadBuilder::Done() { CborWriter.WriteContainerEnd(); return { MemoryWriter.Buffer.GetData(), uint32(MemoryWriter.Buffer.Num()) }; } //////////////////////////////////////////////////////////////////////////////// class FResponse { public: EStatusCode GetStatusCode() const; int64 GetInt64Checked(const char* Key, int64 Default) const; uint64 GetUint64Checked(const char* Key, uint64 Default) const; int32 GetInt32Checked(const char* Key, int32 Default) const; uint32 GetUint32Checked(const char* Key, uint32 Default) const; int64 GetInteger(const char* Key, int64 Default) const { return GetInt64Checked(Key, Default); } template FUtf8StringView GetString(const char* Key, const char (&Default)[N]) const; bool GetStringArray(const char* Key, TArray& OutArray) const; const uint8* GetData() const; uint32 GetSize() const; uint8* Reserve(uint32 Size); private: template Type GetValue(const char* Key, Type Default, LambdaType&& Lambda) const; TArray Buffer; }; //////////////////////////////////////////////////////////////////////////////// inline EStatusCode FResponse::GetStatusCode() const { int32 Code = GetInt32Checked("$status", 0); return Code ? EStatusCode(Code) : EStatusCode::Unknown; } //////////////////////////////////////////////////////////////////////////////// inline bool FResponse::GetStringArray(const char* Key, TArray& OutArray) const { FMemoryReader MemoryReader(Buffer); FCborReader CborReader(&MemoryReader, ECborEndianness::StandardCompliant); FCborContext Context; while (CborReader.ReadNext(Context)) { if (Context.IsString()) { uint32 Length = uint32(Context.AsLength()); uint32 Offset = uint32(MemoryReader.Tell()); auto* String = (const char*)(Buffer.GetData() + Offset - Length); if (FCStringAnsi::Strncmp(Key, String, Length) == 0) { if (CborReader.ReadNext(Context) && Context.IsContainer() && Context.MajorType() == ECborCode::Array) { while (CborReader.ReadNext(Context) && Context.IsString()) { OutArray.Emplace(Context.AsString()); } return true; } } } } return false; } //////////////////////////////////////////////////////////////////////////////// inline const uint8* FResponse::GetData() const { return Buffer.GetData(); }; //////////////////////////////////////////////////////////////////////////////// inline uint32 FResponse::GetSize() const { return Buffer.Num(); } //////////////////////////////////////////////////////////////////////////////// inline uint8* FResponse::Reserve(uint32 Size) { Buffer.SetNumUninitialized(Size, EAllowShrinking::No); return Buffer.GetData(); } //////////////////////////////////////////////////////////////////////////////// template inline Type FResponse::GetValue(const char* Key, Type Default, LambdaType&& Lambda) const { FMemoryReader MemoryReader(Buffer); FCborReader CborReader(&MemoryReader, ECborEndianness::StandardCompliant); FCborContext Context; if (!CborReader.ReadNext(Context) || Context.MajorType() != ECborCode::Map) { return Default; } while (true) { // Read key if (!CborReader.ReadNext(Context) || !Context.IsString()) { return Default; } uint32 Length = static_cast(Context.AsLength()); uint32 Offset = static_cast(MemoryReader.Tell()); auto* String = (const char*)(Buffer.GetData() + Offset - Length); bool bIsTarget = (FCStringAnsi::Strncmp(Key, String, Length) == 0); // Read value if (!CborReader.ReadNext(Context)) { return Default; } if (bIsTarget) { return Lambda(Context, static_cast(MemoryReader.Tell())); } } } //////////////////////////////////////////////////////////////////////////////// inline int64 FResponse::GetInt64Checked(const char* Key, int64 Default) const { return GetValue( Key, Default, [this] (const FCborContext& Context, int64) { return Context.AsInt(); } ); } //////////////////////////////////////////////////////////////////////////////// inline uint64 FResponse::GetUint64Checked(const char* Key, uint64 Default) const { return GetValue( Key, Default, [this](const FCborContext& Context, uint64) { return Context.AsUInt(); } ); } //////////////////////////////////////////////////////////////////////////////// inline int32 FResponse::GetInt32Checked(const char* Key, int32 Default) const { return GetValue( Key, Default, [this](const FCborContext& Context, int32) { int64 Value = Context.AsInt(); check(Value >= INT_MIN && Value <= INT_MAX); return static_cast(Value); } ); } //////////////////////////////////////////////////////////////////////////////// inline uint32 FResponse::GetUint32Checked(const char* Key, uint32 Default) const { return GetValue( Key, Default, [this](const FCborContext& Context, uint32) { uint64 Value = Context.AsUInt(); check(Value <= UINT_MAX); return static_cast(Value); } ); } //////////////////////////////////////////////////////////////////////////////// template inline FUtf8StringView FResponse::GetString(const char* Key, const char (&Default)[N]) const { FUtf8StringView DefaultView(Default, N - 1); return GetValue( Key, DefaultView, [this, DefaultView] (const FCborContext& Context, uint32 Offset) { if (Context.IsString()) { uint32 Length = static_cast(Context.AsLength()); const char* Data = (const char*)(Buffer.GetData() + Offset - Length); return FUtf8StringView(Data, Length); } return DefaultView; } ); } } // namespace Trace } // namespace UE