Files
2025-05-18 13:04:45 +08:00

370 lines
10 KiB
C++

// 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 <int BufferSize>
struct TInlineMemoryWriter
: public FMemoryArchive
{
typedef TArray<uint8, TInlineAllocator<BufferSize>> BufferType;
TInlineMemoryWriter();
virtual void Serialize(void* Data, int64 Num) override;
BufferType Buffer;
};
////////////////////////////////////////////////////////////////////////////////
template <int BufferSize>
inline TInlineMemoryWriter<BufferSize>::TInlineMemoryWriter()
{
SetIsSaving(true);
}
////////////////////////////////////////////////////////////////////////////////
template <int BufferSize>
inline void TInlineMemoryWriter<BufferSize>::Serialize(void* Data, int64 Num)
{
Buffer.Append((const uint8*)Data, IntCastChecked<int32>(Num));
}
////////////////////////////////////////////////////////////////////////////////
struct FPayload
{
const uint8* Data;
uint32 Size;
};
////////////////////////////////////////////////////////////////////////////////
enum class EStatusCode
{
Unknown = 0,
Success = 200,
BadRequest = 400,
NotFound = 404,
InternalError = 500,
};
////////////////////////////////////////////////////////////////////////////////
template <int Size=128>
class TPayloadBuilder
{
public:
TPayloadBuilder(EStatusCode StatusCode);
template <int N> TPayloadBuilder(const char (&Path)[N]);
template <int N> void AddInteger(const char (&Name)[N], int64 Value);
template <int N> void AddString(const char (&Name)[N], const char* Value, int32 Length=-1);
template <int N> void AddString(const char (&Name)[N], const FStringView& String);
template <int N> void AddStringArray(const char(&Name)[N], const TArray<FString>& Values);
template <int N, typename T>
void AddArray(const char(&Name)[N], const TArrayView<T>& Values);
FPayload Done();
private:
TInlineMemoryWriter<Size> MemoryWriter;
FCborWriter CborWriter = { &MemoryWriter, ECborEndianness::StandardCompliant };
};
////////////////////////////////////////////////////////////////////////////////
template <int Size>
inline TPayloadBuilder<Size>::TPayloadBuilder(EStatusCode StatusCode)
{
CborWriter.WriteContainerStart(ECborCode::Map, -1);
AddInteger("$status", int32(StatusCode));
}
////////////////////////////////////////////////////////////////////////////////
template <int Size>
template <int N>
inline TPayloadBuilder<Size>::TPayloadBuilder(const char (&Path)[N])
{
CborWriter.WriteContainerStart(ECborCode::Map, -1);
AddString("$request", "GET");
AddString("$path", Path, N - 1);
}
////////////////////////////////////////////////////////////////////////////////
template <int Size>
template <int N>
inline void TPayloadBuilder<Size>::AddInteger(const char (&Name)[N], int64 Value)
{
CborWriter.WriteValue(Name, N - 1);
CborWriter.WriteValue(Value);
}
////////////////////////////////////////////////////////////////////////////////
template <int Size>
template <int N>
inline void TPayloadBuilder<Size>::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 <int Size>
template <int N>
void TPayloadBuilder<Size>::AddString(const char(& Name)[N], const FStringView& String)
{
CborWriter.WriteValue(Name, N - 1);
CborWriter.WriteValue(String);
}
////////////////////////////////////////////////////////////////////////////////
template<int Size>
template<int N>
inline void TPayloadBuilder<Size>::AddStringArray(const char(&Name)[N], const TArray<FString>& Values)
{
CborWriter.WriteValue(Name, N - 1);
CborWriter.WriteContainerStart(ECborCode::Array, Values.Num());
for (const FString& Elem : Values)
{
CborWriter.WriteValue(Elem);
}
}
////////////////////////////////////////////////////////////////////////////////
template <int Size>
template <int N, typename T>
void TPayloadBuilder<Size>::AddArray(const char(& Name)[N], const TArrayView<T>& Values)
{
CborWriter.WriteValue(Name, N - 1);
CborWriter.WriteContainerStart(ECborCode::Array, Values.Num());
for (const T& Elem : Values)
{
CborWriter.WriteValue(Elem);
}
}
////////////////////////////////////////////////////////////////////////////////
template <int Size>
inline FPayload TPayloadBuilder<Size>::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 <int N>
FUtf8StringView GetString(const char* Key, const char (&Default)[N]) const;
bool GetStringArray(const char* Key, TArray<FString>& OutArray) const;
const uint8* GetData() const;
uint32 GetSize() const;
uint8* Reserve(uint32 Size);
private:
template <typename Type, typename LambdaType>
Type GetValue(const char* Key, Type Default, LambdaType&& Lambda) const;
TArray<uint8> 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<FString>& 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 <typename Type, typename LambdaType>
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<uint32>(Context.AsLength());
uint32 Offset = static_cast<uint32>(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<uint32>(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<int32>(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<uint32>(Value);
}
);
}
////////////////////////////////////////////////////////////////////////////////
template <int N>
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<uint32>(Context.AsLength());
const char* Data = (const char*)(Buffer.GetData() + Offset - Length);
return FUtf8StringView(Data, Length);
}
return DefaultView;
}
);
}
} // namespace Trace
} // namespace UE