326 lines
7.4 KiB
C++
326 lines
7.4 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "CborReader.h"
|
|
|
|
#include "Math/Float16.h"
|
|
|
|
FCborReader::FCborReader(FArchive* InStream, ECborEndianness InReaderEndianness)
|
|
: Stream(InStream)
|
|
, Endianness(InReaderEndianness)
|
|
{
|
|
check(InStream != nullptr);
|
|
ContextStack.Emplace();
|
|
}
|
|
|
|
FCborReader::~FCborReader()
|
|
{
|
|
check(ContextStack.Num() > 0 && (ContextStack[0].IsDummy() || ContextStack[0].IsError()));
|
|
}
|
|
|
|
const FArchive* FCborReader::GetArchive() const
|
|
{
|
|
return Stream;
|
|
}
|
|
|
|
bool FCborReader::IsError() const
|
|
{
|
|
// the dummy context holds previous error
|
|
return ContextStack[0].IsError();
|
|
}
|
|
|
|
FCborHeader FCborReader::GetError() const
|
|
{
|
|
// the dummy context holds previous error
|
|
return ContextStack[0].Header;
|
|
}
|
|
|
|
const FCborContext& FCborReader::GetContext() const
|
|
{
|
|
return ContextStack.Top();
|
|
}
|
|
|
|
bool FCborReader::ReadNext(FCborContext& OutContext)
|
|
{
|
|
OutContext.Reset();
|
|
|
|
// if an error happened, successive read are also errors
|
|
if (IsError())
|
|
{
|
|
OutContext.Header = GetError();
|
|
return false;
|
|
}
|
|
|
|
// Invalid stream error
|
|
if (Stream == nullptr)
|
|
{
|
|
OutContext.Header = SetError(ECborCode::ErrorStreamFailure);
|
|
return false;
|
|
}
|
|
|
|
// Set accurate endianness handling
|
|
ScopedCborArchiveEndianness ScopedArchiveEndianness(*Stream, Endianness);
|
|
|
|
// Current parent
|
|
FCborContext& ParentContext = ContextStack.Top();
|
|
|
|
// Check if we reached container end, if so output as if we read a break code
|
|
if (ParentContext.IsFiniteContainer() && ParentContext.Length == 0)
|
|
{
|
|
OutContext.Header.Set(ECborCode::Break);
|
|
// Report 0 Length
|
|
OutContext.Length = ParentContext.Length;
|
|
// Report parent context container type
|
|
OutContext.RawTextValue.Add((char)ParentContext.MajorType());
|
|
// Done with parent context
|
|
check(ContextStack.Num() > 1);
|
|
ContextStack.Pop();
|
|
return true;
|
|
}
|
|
|
|
// Done reading
|
|
if (Stream->AtEnd())
|
|
{
|
|
OutContext.Header = (ParentContext.RawCode() == ECborCode::Dummy) ? FCborHeader(ECborCode::StreamEnd) : SetError(ECborCode::ErrorContext);
|
|
return false;
|
|
}
|
|
|
|
// Read the cbor header
|
|
*Stream << OutContext.Header;
|
|
|
|
// Check for break item
|
|
if (OutContext.IsBreak())
|
|
{
|
|
// Got a break item out of a indefinite context
|
|
if (!ParentContext.IsIndefiniteContainer())
|
|
{
|
|
OutContext.Header = SetError(ECborCode::ErrorBreak);
|
|
return false;
|
|
}
|
|
|
|
// Odd number of item read
|
|
if (ParentContext.MajorType() == ECborCode::Map && (ParentContext.Length & 1))
|
|
{
|
|
OutContext.Header = SetError(ECborCode::ErrorMapContainer);
|
|
return false;
|
|
}
|
|
// Report Length
|
|
OutContext.Length = ParentContext.Length;
|
|
// Report parent context container type
|
|
OutContext.RawTextValue.Add((char)ParentContext.MajorType());
|
|
// Done with parent context
|
|
check(ContextStack.Num() > 1);
|
|
ContextStack.Pop();
|
|
return true;
|
|
}
|
|
|
|
// if the type is indefinite, we increment the length of the parent context
|
|
if (ParentContext.IsIndefiniteContainer())
|
|
{
|
|
++ParentContext.Length;
|
|
|
|
// If we have an indefinite string but current context type doesn't match flag an error
|
|
if (ParentContext.IsString() && ParentContext.MajorType() != OutContext.MajorType())
|
|
{
|
|
OutContext.Header = SetError(ECborCode::ErrorStringNesting);
|
|
return false;
|
|
}
|
|
}
|
|
// Otherwise the length was set when we read the parent context, decrement it, container end if flagged when reaching 0
|
|
else if (ParentContext.IsFiniteContainer())
|
|
{
|
|
--ParentContext.Length;
|
|
}
|
|
|
|
// Read item
|
|
switch (OutContext.MajorType())
|
|
{
|
|
case ECborCode::Uint:
|
|
OutContext.UIntValue = ReadUIntValue(OutContext, *Stream);
|
|
break;
|
|
case ECborCode::Int:
|
|
OutContext.UIntValue = ~ReadUIntValue(OutContext, *Stream);
|
|
break;
|
|
case ECborCode::ByteString:
|
|
// fall through
|
|
case ECborCode::TextString:
|
|
// if we have an indefinite string item, push the context
|
|
if (OutContext.IsIndefiniteContainer())
|
|
{
|
|
OutContext.Length = 0;
|
|
ContextStack.Push(OutContext);
|
|
}
|
|
// Otherwise read the string length in bytes, then serialize the raw context in the byte array
|
|
else
|
|
{
|
|
OutContext.Length = ReadUIntValue(OutContext, *Stream);
|
|
if (OutContext.Length > (uint64)(MAX_int32 - 1))
|
|
{
|
|
OutContext.Header = SetError(ECborCode::ErrorStringLength);
|
|
return false;
|
|
}
|
|
int32 StringLength = (int32)OutContext.Length;
|
|
OutContext.RawTextValue.SetNumUninitialized(StringLength + 1); // Length doesn't count the null terminating character
|
|
Stream->Serialize(OutContext.RawTextValue.GetData(), OutContext.Length);
|
|
OutContext.RawTextValue[StringLength] = '\0';
|
|
}
|
|
break;
|
|
case ECborCode::Array:
|
|
OutContext.Length = OutContext.AdditionalValue() == ECborCode::Indefinite ? 0 : ReadUIntValue(OutContext, *Stream);
|
|
ContextStack.Push(OutContext);
|
|
break;
|
|
case ECborCode::Map:
|
|
OutContext.Length = OutContext.AdditionalValue() == ECborCode::Indefinite ? 0 : ReadUIntValue(OutContext, *Stream) * 2;
|
|
ContextStack.Push(OutContext);
|
|
break;
|
|
case ECborCode::Tag:
|
|
OutContext.UIntValue = ReadUIntValue(OutContext, *Stream);
|
|
break;
|
|
case ECborCode::Prim:
|
|
ReadPrimValue(OutContext, *Stream);
|
|
break;
|
|
}
|
|
|
|
if (OutContext.IsError())
|
|
{
|
|
SetError(OutContext.RawCode());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool FCborReader::SkipContainer(ECborCode ContainerType)
|
|
{
|
|
// Invalid stream error
|
|
if (Stream == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (GetContext().MajorType() != ContainerType)
|
|
{
|
|
return false;
|
|
}
|
|
uint32 Depth = 0;
|
|
FCborContext Context;
|
|
while (ReadNext(Context))
|
|
{
|
|
if (Context.IsBreak() && Depth-- == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (Context.IsContainer())
|
|
{
|
|
++Depth;
|
|
}
|
|
}
|
|
return !IsError();
|
|
}
|
|
|
|
uint64 FCborReader::ReadUIntValue(FCborContext& Context, FArchive& Ar)
|
|
{
|
|
uint64 AdditionalValue = (uint8)Context.AdditionalValue();
|
|
switch (Context.AdditionalValue())
|
|
{
|
|
case ECborCode::Value_1Byte:
|
|
{
|
|
uint8 Temp;
|
|
Ar << Temp;
|
|
AdditionalValue = Temp;
|
|
}
|
|
break;
|
|
case ECborCode::Value_2Bytes:
|
|
{
|
|
uint16 Temp;
|
|
Ar << Temp;
|
|
AdditionalValue = Temp;
|
|
}
|
|
break;
|
|
case ECborCode::Value_4Bytes:
|
|
{
|
|
uint32 Temp;
|
|
Ar << Temp;
|
|
AdditionalValue = Temp;
|
|
}
|
|
break;
|
|
case ECborCode::Value_8Bytes:
|
|
{
|
|
uint64 Temp;
|
|
Ar << Temp;
|
|
AdditionalValue = Temp;
|
|
}
|
|
break;
|
|
case ECborCode::Unused_28:
|
|
// Fall through
|
|
case ECborCode::Unused_29:
|
|
// Fall through
|
|
case ECborCode::Unused_30:
|
|
// Fall through
|
|
case ECborCode::Indefinite:
|
|
// Error
|
|
Context.Header.Set(ECborCode::ErrorReservedItem);
|
|
break;
|
|
default:
|
|
// Use value directly, Noop
|
|
break;
|
|
}
|
|
return AdditionalValue;
|
|
}
|
|
|
|
void FCborReader::ReadPrimValue(FCborContext& Context, FArchive& Ar)
|
|
{
|
|
switch (Context.AdditionalValue())
|
|
{
|
|
case ECborCode::False:
|
|
Context.BoolValue = false;
|
|
break;
|
|
case ECborCode::True:
|
|
Context.BoolValue = true;
|
|
break;
|
|
case ECborCode::Null:
|
|
// fall through
|
|
case ECborCode::Undefined:
|
|
// noop
|
|
break;
|
|
case ECborCode::Value_1Byte:
|
|
{
|
|
uint8 Temp;
|
|
Ar << Temp;
|
|
}
|
|
break;
|
|
case ECborCode::Value_2Bytes:
|
|
{
|
|
FFloat16 Temp;
|
|
Ar << Temp;
|
|
// Use built in FFloat16 converter to assign to our float type.
|
|
Context.FloatValue = Temp;
|
|
}
|
|
break;
|
|
case ECborCode::Value_4Bytes:
|
|
{
|
|
float Temp;
|
|
Ar << Temp;
|
|
Context.FloatValue = Temp;
|
|
}
|
|
break;
|
|
case ECborCode::Value_8Bytes:
|
|
{
|
|
double Temp;
|
|
Ar << Temp;
|
|
Context.DoubleValue = Temp;
|
|
}
|
|
break;
|
|
default:
|
|
// Error other values are unused, break item should have been processed elsewhere
|
|
Context.Header.Set(ECborCode::ErrorReservedItem);
|
|
break;
|
|
}
|
|
}
|
|
|
|
FCborHeader FCborReader::SetError(ECborCode ErrorCode)
|
|
{
|
|
FCborContext& Dummy = ContextStack[0];
|
|
Dummy.Header.Set(ErrorCode);
|
|
return Dummy.Header;
|
|
}
|