Files
UnrealEngineUWP/Engine/Source/Programs/UnrealTraceServer/src/Cbor.cpp
Martin Ridgers 255b05c611 UnrealTraceServer source.
[CL 17213236 by Martin Ridgers in ue5-main branch]
2021-08-18 07:36:31 -04:00

682 lines
16 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Pch.h"
#include "Cbor.h"
#include "Utils.h"
// {{{1 misc -------------------------------------------------------------------
enum ECborMajorType
{
Integer = 0 << 5,
NegativeInteger = 1 << 5,
ByteString = 2 << 5,
TextString = 3 << 5,
Array = 4 << 5,
Map = 5 << 5,
Tag = 6 << 5,
Float_Simple = 7 << 5,
};
// {{{1 context ----------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
void FCborContext::Reset()
{
NextCursor = nullptr;
}
////////////////////////////////////////////////////////////////////////////////
ECborType FCborContext::GetType() const
{
return Type;
}
////////////////////////////////////////////////////////////////////////////////
int64 FCborContext::GetLength() const
{
return Param;
}
////////////////////////////////////////////////////////////////////////////////
FStringView FCborContext::AsString() const
{
if (GetType() != ECborType::String)
{
return FStringView();
}
return FStringView((const char*)(NextCursor - Param), Param);
}
////////////////////////////////////////////////////////////////////////////////
int64 FCborContext::AsInteger() const
{
return Param;
}
////////////////////////////////////////////////////////////////////////////////
void FCborContext::SetError()
{
Type = ECborType::Error;
Param = -1;
NextCursor = nullptr;
}
// {{{1 writer -----------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
FCborWriter::FCborWriter(FInlineBuffer& InBuffer)
: Buffer(InBuffer)
{
}
////////////////////////////////////////////////////////////////////////////////
void FCborWriter::OpenMap(int32 ItemCount)
{
if (ItemCount != -1)
{
WriteParam(ECborMajorType::Map, ItemCount);
return;
}
uint8 Initial = ECborMajorType::Map | 31;
Buffer.Append(&Initial, sizeof(Initial));
}
////////////////////////////////////////////////////////////////////////////////
void FCborWriter::OpenArray(int32 ItemCount)
{
if (ItemCount != -1)
{
WriteParam(ECborMajorType::Array, ItemCount);
return;
}
uint8 Initial = ECborMajorType::Array | 31;
Buffer.Append(&Initial, sizeof(Initial));
}
////////////////////////////////////////////////////////////////////////////////
void FCborWriter::Close()
{
uint8 Initial = ECborMajorType::Float_Simple | 31;
Buffer.Append(&Initial, sizeof(Initial));
}
////////////////////////////////////////////////////////////////////////////////
void FCborWriter::WriteString(const char* Value, int32 Length)
{
Length = (Length < 0) ? int32(strlen(Value)) : Length;
WriteParam(ECborMajorType::TextString, Length);
Buffer.Append(Value, Length);
}
////////////////////////////////////////////////////////////////////////////////
void FCborWriter::WriteInteger(int64 Value)
{
uint32 Negative = (Value < 0);
if (Negative)
{
Value = ~Value;
}
uint32 MajorType = Negative ? ECborMajorType::NegativeInteger : ECborMajorType::Integer;
WriteParam(MajorType, Value);
}
////////////////////////////////////////////////////////////////////////////////
void FCborWriter::WriteParam(uint32 MajorType, uint64 Value)
{
if (Value <= 23)
{
uint8 Initial = MajorType | uint8(Value);
Buffer.Append(&Initial, 1);
return;
}
uint32 BytesPow2 = 3;
BytesPow2 -= int32(Value <= 0xffff'ffffu);
BytesPow2 -= int32(Value <= 0x0000'ffffu);
BytesPow2 -= int32(Value <= 0x0000'00ffu);
Value = ((Value & 0xffff'ffff'0000'0000ull) >> 32) | ((Value & 0x0000'0000'ffff'ffffull) << 32);
Value = ((Value & 0xffff'0000'ffff'0000ull) >> 16) | ((Value & 0x0000'ffff'0000'ffffull) << 16);
Value = ((Value & 0xff00'ff00'ff00'ff00ull) >> 8) | ((Value & 0x00ff'00ff'00ff'00ffull) << 8);
Value >>= 64 - (8 << BytesPow2);
struct {
uint8 Initial;
uint8 Payload[sizeof(Value)];
} Packet = {
uint8(MajorType | (24 + BytesPow2)),
};
memcpy(Packet.Payload, &Value, sizeof(Value));
Buffer.Append(&Packet, 1 + (1 << BytesPow2));
}
// {{{1 reader -----------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
FCborReader::FCborReader(const uint8* Data, uint32 DataSize)
: Base(Data)
, DataEnd(Data + DataSize)
{
}
////////////////////////////////////////////////////////////////////////////////
int64 FCborReader::ReadParam(const uint8*& Cursor)
{
uint32 Additional = *Cursor & 0b0001'1111;
++Cursor;
// [0-23] literal values
if (uint32(23 - Additional) <= 23)
{
return Additional;
}
// [24-27] value in subsequent bytes
else if (uint32(27 - Additional) <= 27)
{
uint32 Bytes = 1 << (Additional - 24);
if (Cursor + Bytes > DataEnd)
{
return -2;
}
int64 Value = 0;
do
{
Value <<= 8;
Value |= *Cursor;
++Cursor;
}
while (--Bytes);
return Value;
}
// 31: indeterminate length
else if (Additional == 31)
{
return -1;
}
// ...something went wrong
return -2;
}
////////////////////////////////////////////////////////////////////////////////
bool FCborReader::ReadContainer(FCborContext& Context, const uint8* Cursor)
{
bool bIsMap = (*Cursor >= ECborMajorType::Map);
int64 Param = ReadParam(Cursor);
if (Param <= -2)
{
Context.SetError();
return false;
}
Context.Type = bIsMap ? ECborType::Map : ECborType::Array;
Context.NextCursor = Cursor;
Context.Param = Param;
return true;
}
////////////////////////////////////////////////////////////////////////////////
bool FCborReader::ReadInteger(FCborContext& Context, const uint8* Cursor)
{
bool bIsNegative = (*Cursor >= ECborMajorType::NegativeInteger);
int64 Param = ReadParam(Cursor);
if (Param <= -1)
{
Context.SetError();
return false;
}
Context.Type = ECborType::Integer;
Context.NextCursor = Cursor;
Context.Param = bIsNegative ? ~Param : Param;
return true;
}
////////////////////////////////////////////////////////////////////////////////
bool FCborReader::ReadString(FCborContext& Context, const uint8* Cursor)
{
int64 Param = ReadParam(Cursor);
if (Param <= -2)
{
Context.SetError();
return false;
}
else if (Param >= 0)
{
if ((Cursor + Param) > DataEnd)
{
Context.SetError();
return false;
}
Cursor += Param;
}
Context.Type = ECborType::String;
Context.NextCursor = Cursor;
Context.Param = Param;
return true;
}
////////////////////////////////////////////////////////////////////////////////
bool FCborReader::ReadNext(FCborContext& Context)
{
const uint8* Cursor = (Context.NextCursor == nullptr) ? Base : Context.NextCursor;
if (Cursor >= DataEnd)
{
Context.Type = ECborType::Eof;
Context.Param = -1;
return false;
}
int64 Param;
switch (*Cursor & 0b1110'0000)
{
case ECborMajorType::Integer:
case ECborMajorType::NegativeInteger:
return ReadInteger(Context, Cursor);
case ECborMajorType::ByteString:
case ECborMajorType::TextString:
return ReadString(Context, Cursor);
case ECborMajorType::Array:
case ECborMajorType::Map:
return ReadContainer(Context, Cursor);
case ECborMajorType::Tag:
/* unsupported */
break;
case ECborMajorType::Float_Simple:
Param = *Cursor & 0b0001'1111;
++Cursor;
switch (Param)
{
case 24:
if (Cursor >= DataEnd)
{
Context.SetError();
return false;
}
switch (*Cursor)
{
case 20: // false
case 21: // true
case 22: // null
case 23: // undefined
break;
}
break;
case 25: // half
case 26: // float
case 27: // double
/* if (Cursor + float_size > DataEnd) "basil!" / Context.SetError() */
break;
case 31:
Context.Type = ECborType::End;
Context.NextCursor = Cursor;
Context.Param = -1;
return false; // "false" to facilitate ReadNext() in loops
}
// 31 = break
break;
}
return false;
}
// {{{1 test-read --------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
#if defined(_MSC_VER)
# define DEBUG_BREAK() do { __debugbreak(); } while (0)
#else
# define DEBUG_BREAK() do { __builtin_trap(); } while (0)
#endif
#define REQUIRE(x) \
do { \
if (!(x)) \
DEBUG_BREAK(); \
} while (false)
////////////////////////////////////////////////////////////////////////////////
static void TestCborReader_Integer()
{
struct
{
int64 Expected;
uint32 TruthSize;
uint8 Truth[9];
} Cases[] = {
{ 0, 1, { 0x00 } },
{ 0, 2, { 0x18, 0x00 } },
{ 0, 3, { 0x19, 0x00, 0x00 } },
{ 0, 5, { 0x1a, 0x00, 0x00, 0x00, 0x00 } },
{ 0, 9, { 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ 1, 1, { 0x01 } },
{ 23, 1, { 0x17 } },
{ 24, 2, { 0x18, 0x18 } },
{ 255, 2, { 0x18, 0xff } },
{ 256, 3, { 0x19, 0x01, 0x00 } },
{ 65535, 3, { 0x19, 0xff, 0xff } },
{ 65536, 5, { 0x1a, 0x00, 0x01, 0x00, 0x00 } },
{ 123456, 5, { 0x1a, 0x00, 0x01, 0xe2, 0x40 } },
{ 123456789, 5, { 0x1a, 0x07, 0x5b, 0xcd, 0x15 } },
{ -1, 1, { 0x20 } },
{ -2, 1, { 0x21 } },
{ -10, 1, { 0x29 } },
{ -24, 1, { 0x37 } },
{ -25, 2, { 0x38, 0x18 } },
{ -500, 3, { 0x39, 0x01, 0xf3 } },
{ -67000, 5, { 0x3a, 0x00, 0x01, 0x05, 0xb7 } },
{ -123456789, 5, { 0x3a, 0x07, 0x5b, 0xcd, 0x14 } },
};
for (const auto& Case : Cases)
{
FCborContext Context;
FCborReader Reader(Case.Truth, Case.TruthSize);
REQUIRE(Reader.ReadNext(Context));
REQUIRE(Context.GetType() == ECborType::Integer);
REQUIRE(Context.AsInteger() == Case.Expected);
REQUIRE(!Reader.ReadNext(Context));
}
}
////////////////////////////////////////////////////////////////////////////////
static void TestCborReader_String()
{
// "string"
{
const uint8 Truth[] = { 0x66, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67 };
const char* Expected = "string";
FCborContext Context;
FCborReader Reader(Truth, TS_ARRAY_COUNT(Truth));
REQUIRE(Reader.ReadNext(Context));
REQUIRE(Context.GetType() == ECborType::String);
REQUIRE(Context.AsString().Compare(Expected) == 0);
REQUIRE(!Reader.ReadNext(Context));
}
// ""
{
const uint8 Truth[] = { 0x60 };
const char* Expected = "";
FCborContext Context;
FCborReader Reader(Truth, TS_ARRAY_COUNT(Truth));
REQUIRE(Reader.ReadNext(Context));
REQUIRE(Context.GetType() == ECborType::String);
REQUIRE(Context.AsString().Compare(Expected) == 0);
REQUIRE(!Reader.ReadNext(Context));
}
// "stringstringstringstringstringstringstringstringstringstring"
{
const uint8 Truth[] = {
0x78, 0x3c,
0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
0x73, 0x74, 0x72, 0x69, 0x6e, 0x67
};
const char* Expected = "stringstringstringstringstringstringstringstringstringstring";
FCborContext Context;
FCborReader Reader(Truth, TS_ARRAY_COUNT(Truth));
REQUIRE(Reader.ReadNext(Context));
REQUIRE(Context.GetType() == ECborType::String);
REQUIRE(Context.AsString().Compare(Expected) == 0);
REQUIRE(!Reader.ReadNext(Context));
}
}
////////////////////////////////////////////////////////////////////////////////
static void TestCborReader_Map()
{
// {}
{
const uint8 Truth[] = { 0xa0 };
FCborContext Context;
FCborReader Reader(Truth, TS_ARRAY_COUNT(Truth));
REQUIRE(Reader.ReadNext(Context));
REQUIRE(Context.GetType() == ECborType::Map);
REQUIRE(Context.GetLength() == 0);
REQUIRE(!Reader.ReadNext(Context));
}
// {...}
{
const uint8 Truth[] = { 0xbf, 0xff };
FCborContext Context;
FCborReader Reader(Truth, TS_ARRAY_COUNT(Truth));
REQUIRE(Reader.ReadNext(Context));
REQUIRE(Context.GetType() == ECborType::Map);
REQUIRE(Context.GetLength() == -1);
REQUIRE(!Reader.ReadNext(Context));
REQUIRE(Context.GetType() == ECborType::End);
REQUIRE(!Reader.ReadNext(Context));
REQUIRE(Context.GetType() == ECborType::Eof);
}
// { "key":"value", 0:1 }
{
const uint8 Truth[] = {
0xa2,
0x63, 0x6b, 0x65, 0x79,
0x65, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x00, 0x01
};
FCborContext Context;
FCborReader Reader(Truth, TS_ARRAY_COUNT(Truth));
REQUIRE(Reader.ReadNext(Context));
REQUIRE(Context.GetType() == ECborType::Map);
REQUIRE(Context.GetLength() == 2);
REQUIRE(Reader.ReadNext(Context));
REQUIRE(Context.GetType() == ECborType::String);
REQUIRE(Context.AsString().Compare("key") == 0);
REQUIRE(Reader.ReadNext(Context));
REQUIRE(Context.GetType() == ECborType::String);
REQUIRE(Context.AsString().Compare("value") == 0);
REQUIRE(Reader.ReadNext(Context));
REQUIRE(Context.GetType() == ECborType::Integer);
REQUIRE(Context.AsInteger() == 0);
REQUIRE(Reader.ReadNext(Context));
REQUIRE(Context.GetType() == ECborType::Integer);
REQUIRE(Context.AsInteger() == 1);
REQUIRE(!Reader.ReadNext(Context));
REQUIRE(Context.GetType() == ECborType::Eof);
}
}
// {{{1 test-write -------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
static void TestCborWriter_Integer()
{
struct
{
int64 Content;
uint32 TruthSize;
uint8 Truth[9];
} Cases[] = {
{ 0, 1, { 0x00 } },
{ 1, 1, { 0x01 } },
{ 23, 1, { 0x17 } },
{ 24, 2, { 0x18, 0x18 } },
{ 255, 2, { 0x18, 0xff } },
{ 256, 3, { 0x19, 0x01, 0x00 } },
{ 65535, 3, { 0x19, 0xff, 0xff } },
{ 65536, 5, { 0x1a, 0x00, 0x01, 0x00, 0x00 } },
{ 123456, 5, { 0x1a, 0x00, 0x01, 0xe2, 0x40 } },
{ 123456789, 5, { 0x1a, 0x07, 0x5b, 0xcd, 0x15 } },
{ -1, 1, { 0x20 } },
{ -2, 1, { 0x21 } },
{ -10, 1, { 0x29 } },
{ -24, 1, { 0x37 } },
{ -25, 2, { 0x38, 0x18 } },
{ -500, 3, { 0x39, 0x01, 0xf3 } },
{ -67000, 5, { 0x3a, 0x00, 0x01, 0x05, 0xb7 } },
{ -123456789, 5, { 0x3a, 0x07, 0x5b, 0xcd, 0x14 } },
};
for (const auto& Case : Cases)
{
TInlineBuffer<4> Buffer;
FCborWriter Writer(Buffer);
Writer.WriteInteger(Case.Content);
REQUIRE(Buffer->GetSize() == Case.TruthSize);
REQUIRE(memcmp(Buffer->GetData(), Case.Truth, Case.TruthSize) == 0);
}
}
////////////////////////////////////////////////////////////////////////////////
static void TestCborWriter_String()
{
struct
{
const char* Value;
uint32 TruthSize;
const uint8 Truth[64];
} Cases[] = {
{ "string", 7, { 0x66, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67 } },
{ "", 1, { 0x60 } },
{
"stringstringstringstringstringstringstringstringstringstring",
62,
{ 0x78, 0x3c,
0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67 },
},
};
for (const auto& Case : Cases)
{
TInlineBuffer<8> Buffer;
FCborWriter Writer(Buffer);
Writer.WriteString(Case.Value);
REQUIRE(Buffer->GetSize() == Case.TruthSize);
REQUIRE(memcmp(Buffer->GetData(), Case.Truth, Case.TruthSize) == 0);
}
}
////////////////////////////////////////////////////////////////////////////////
void TestCborWriter_Map()
{
// {}
{
const uint8 Truth[] = { 0xa0 };
TInlineBuffer<4> Buffer;
FCborWriter Writer(Buffer);
Writer.OpenMap(0);
REQUIRE(memcmp(Buffer->GetData(), Truth, sizeof(Truth)) == 0);
}
// {...}
{
const uint8 Truth[] = { 0xbf, 0xff };
TInlineBuffer<4> Buffer;
FCborWriter Writer(Buffer);
Writer.OpenMap();
Writer.Close();
REQUIRE(memcmp(Buffer->GetData(), Truth, sizeof(Truth)) == 0);
}
// { "key":"value", 0:1 }
{
const uint8 Truth[] = {
0xa2,
0x63, 0x6b, 0x65, 0x79,
0x65, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x00, 0x01
};
TInlineBuffer<4> Buffer;
FCborWriter Writer(Buffer);
Writer.OpenMap(2);
Writer.WriteString("key");
Writer.WriteString("value", 5);
Writer.WriteInteger(0);
Writer.WriteInteger(1);
REQUIRE(memcmp(Buffer->GetData(), Truth, sizeof(Truth)) == 0);
}
}
// {{{1 test -------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
void TestCbor()
{
TestCborReader_Integer();
TestCborReader_String();
TestCborReader_Map();
TestCborWriter_Integer();
TestCborWriter_String();
TestCborWriter_Map();
// [-1]
//const uint8 Truth[] = { 0x81, 0x20 };
// float
//const uint8 Truth[] = { 0xfb 0x3f 0xb9 0x99 0x99 0x99 0x99 0x99 0x9a }; // primitive(4591870180066957722)
}
/* vim: set noexpandtab foldlevel=1 : */