aboutsummaryrefslogtreecommitdiff
path: root/zencore/compactbinary.cpp
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2023-05-02 10:01:47 +0200
committerGitHub <[email protected]>2023-05-02 10:01:47 +0200
commit075d17f8ada47e990fe94606c3d21df409223465 (patch)
treee50549b766a2f3c354798a54ff73404217b4c9af /zencore/compactbinary.cpp
parentfix: bundle shouldn't append content zip to zen (diff)
downloadzen-075d17f8ada47e990fe94606c3d21df409223465.tar.xz
zen-075d17f8ada47e990fe94606c3d21df409223465.zip
moved source directories into `/src` (#264)
* moved source directories into `/src` * updated bundle.lua for new `src` path * moved some docs, icon * removed old test trees
Diffstat (limited to 'zencore/compactbinary.cpp')
-rw-r--r--zencore/compactbinary.cpp2299
1 files changed, 0 insertions, 2299 deletions
diff --git a/zencore/compactbinary.cpp b/zencore/compactbinary.cpp
deleted file mode 100644
index 0db9f02ea..000000000
--- a/zencore/compactbinary.cpp
+++ /dev/null
@@ -1,2299 +0,0 @@
-// Copyright Epic Games, Inc. All Rights Reserved.
-
-#include "zencore/compactbinary.h"
-
-#include <zencore/base64.h>
-#include <zencore/compactbinarybuilder.h>
-#include <zencore/compactbinaryvalidation.h>
-#include <zencore/compactbinaryvalue.h>
-#include <zencore/compress.h>
-#include <zencore/endian.h>
-#include <zencore/fmtutils.h>
-#include <zencore/stream.h>
-#include <zencore/string.h>
-#include <zencore/testing.h>
-#include <zencore/uid.h>
-
-#include <fmt/format.h>
-#include <string_view>
-
-#if ZEN_PLATFORM_WINDOWS
-# include <zencore/windows.h>
-#else
-# include <time.h>
-#endif
-
-ZEN_THIRD_PARTY_INCLUDES_START
-#include <json11.hpp>
-ZEN_THIRD_PARTY_INCLUDES_END
-
-namespace zen {
-
-const int DaysToMonth[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
-
-double
-GetJulianDay(uint64_t Ticks)
-{
- return (double)(1721425.5 + Ticks / TimeSpan::TicksPerDay);
-}
-
-bool
-IsLeapYear(int Year)
-{
- if ((Year % 4) == 0)
- {
- return (((Year % 100) != 0) || ((Year % 400) == 0));
- }
-
- return false;
-}
-
-static constexpr uint64_t
-GetPlatformToDateTimeBiasInSeconds()
-{
-#if ZEN_PLATFORM_WINDOWS
- const uint64_t PlatformEpochYear = 1601;
-#else
- const uint64_t PlatformEpochYear = 1970;
-#endif
- const uint64_t DateTimeEpochYear = 1;
- return uint64_t(double(PlatformEpochYear - DateTimeEpochYear) * 365.2425) * 86400;
-}
-
-uint64_t
-DateTime::NowTicks()
-{
- static constexpr uint64_t EpochBias = GetPlatformToDateTimeBiasInSeconds();
-
-#if ZEN_PLATFORM_WINDOWS
- FILETIME SysTime;
- GetSystemTimePreciseAsFileTime(&SysTime);
- return (EpochBias * TimeSpan::TicksPerSecond) + ((uint64_t(SysTime.dwHighDateTime) << 32) | SysTime.dwLowDateTime);
-#else
- int64_t SecondsSinceUnixEpoch = time(nullptr);
- return (EpochBias + SecondsSinceUnixEpoch) * TimeSpan::TicksPerSecond;
-#endif
-}
-
-DateTime
-DateTime::Now()
-{
- return DateTime{NowTicks()};
-}
-
-void
-DateTime::Set(int Year, int Month, int Day, int Hour, int Minute, int Second, int MilliSecond)
-{
- int TotalDays = 0;
-
- if ((Month > 2) && IsLeapYear(Year))
- {
- ++TotalDays;
- }
-
- --Year; // the current year is not a full year yet
- --Month; // the current month is not a full month yet
-
- TotalDays += Year * 365;
- TotalDays += Year / 4; // leap year day every four years...
- TotalDays -= Year / 100; // ...except every 100 years...
- TotalDays += Year / 400; // ...but also every 400 years
- TotalDays += DaysToMonth[Month]; // days in this year up to last month
- TotalDays += Day - 1; // days in this month minus today
-
- Ticks = TotalDays * TimeSpan::TicksPerDay + Hour * TimeSpan::TicksPerHour + Minute * TimeSpan::TicksPerMinute +
- Second * TimeSpan::TicksPerSecond + MilliSecond * TimeSpan::TicksPerMillisecond;
-}
-
-int
-DateTime::GetYear() const
-{
- int Year, Month, Day;
- GetDate(Year, Month, Day);
-
- return Year;
-}
-
-int
-DateTime::GetMonth() const
-{
- int Year, Month, Day;
- GetDate(Year, Month, Day);
-
- return Month;
-}
-
-int
-DateTime::GetDay() const
-{
- int Year, Month, Day;
- GetDate(Year, Month, Day);
-
- return Day;
-}
-
-int
-DateTime::GetHour() const
-{
- return (int)((Ticks / TimeSpan::TicksPerHour) % 24);
-}
-
-int
-DateTime::GetHour12() const
-{
- int Hour = GetHour();
-
- if (Hour < 1)
- {
- return 12;
- }
-
- if (Hour > 12)
- {
- return (Hour - 12);
- }
-
- return Hour;
-}
-
-int
-DateTime::GetMinute() const
-{
- return (int)((Ticks / TimeSpan::TicksPerMinute) % 60);
-}
-
-int
-DateTime::GetSecond() const
-{
- return (int)((Ticks / TimeSpan::TicksPerSecond) % 60);
-}
-
-int
-DateTime::GetMillisecond() const
-{
- return (int)((Ticks / TimeSpan::TicksPerMillisecond) % 1000);
-}
-
-void
-DateTime::GetDate(int& Year, int& Month, int& Day) const
-{
- // Based on FORTRAN code in:
- // Fliegel, H. F. and van Flandern, T. C.,
- // Communications of the ACM, Vol. 11, No. 10 (October 1968).
-
- int i, j, k, l, n;
-
- l = int(GetJulianDay(Ticks) + 0.5) + 68569;
- n = 4 * l / 146097;
- l = l - (146097 * n + 3) / 4;
- i = 4000 * (l + 1) / 1461001;
- l = l - 1461 * i / 4 + 31;
- j = 80 * l / 2447;
- k = l - 2447 * j / 80;
- l = j / 11;
- j = j + 2 - 12 * l;
- i = 100 * (n - 49) + i + l;
-
- Year = i;
- Month = j;
- Day = k;
-}
-
-std::string
-DateTime::ToString(const char* Format) const
-{
- ExtendableStringBuilder<32> Result;
- int Year, Month, Day;
-
- GetDate(Year, Month, Day);
-
- if (Format != nullptr)
- {
- while (*Format != '\0')
- {
- if ((*Format == '%') && (*(++Format) != '\0'))
- {
- switch (*Format)
- {
- // case 'a': Result.Append(IsMorning() ? TEXT("am") : TEXT("pm")); break;
- // case 'A': Result.Append(IsMorning() ? TEXT("AM") : TEXT("PM")); break;
- case 'd':
- Result.Append(fmt::format("{:02}", Day));
- break;
- // case 'D': Result.Appendf(TEXT("%03i"), GetDayOfYear()); break;
- case 'm':
- Result.Append(fmt::format("{:02}", Month));
- break;
- case 'y':
- Result.Append(fmt::format("{:02}", Year % 100));
- break;
- case 'Y':
- Result.Append(fmt::format("{:04}", Year));
- break;
- case 'h':
- Result.Append(fmt::format("{:02}", GetHour12()));
- break;
- case 'H':
- Result.Append(fmt::format("{:02}", GetHour()));
- break;
- case 'M':
- Result.Append(fmt::format("{:02}", GetMinute()));
- break;
- case 'S':
- Result.Append(fmt::format("{:02}", GetSecond()));
- break;
- case 's':
- Result.Append(fmt::format("{:03}", GetMillisecond()));
- break;
- default:
- Result.Append(*Format);
- }
- }
- else
- {
- Result.Append(*Format);
- }
-
- // move to the next one
- Format++;
- }
- }
-
- return Result.ToString();
-}
-
-std::string
-DateTime::ToIso8601() const
-{
- return ToString("%Y-%m-%dT%H:%M:%S.%sZ");
-}
-
-void
-TimeSpan::Set(int Days, int Hours, int Minutes, int Seconds, int FractionNano)
-{
- int64_t TotalTicks = 0;
-
- TotalTicks += Days * TicksPerDay;
- TotalTicks += Hours * TicksPerHour;
- TotalTicks += Minutes * TicksPerMinute;
- TotalTicks += Seconds * TicksPerSecond;
- TotalTicks += FractionNano / NanosecondsPerTick;
-
- Ticks = TotalTicks;
-}
-
-std::string
-TimeSpan::ToString(const char* Format) const
-{
- StringBuilder<128> Result;
-
- Result.Append((int64_t(Ticks) < 0) ? '-' : '+');
-
- while (*Format != '\0')
- {
- if ((*Format == '%') && (*++Format != '\0'))
- {
- switch (*Format)
- {
- case 'd':
- Result.Append(fmt::format("{}", GetDays()));
- break;
- case 'D':
- Result.Append(fmt::format("{:08}", GetDays()));
- break;
- case 'h':
- Result.Append(fmt::format("{:02}", GetHours()));
- break;
- case 'm':
- Result.Append(fmt::format("{:02}", GetMinutes()));
- break;
- case 's':
- Result.Append(fmt::format("{:02}", GetSeconds()));
- break;
- case 'f':
- Result.Append(fmt::format("{:03}", GetFractionMilli()));
- break;
- case 'u':
- Result.Append(fmt::format("{:06}", GetFractionMicro()));
- break;
- case 't':
- Result.Append(fmt::format("{:07}", GetFractionTicks()));
- break;
- case 'n':
- Result.Append(fmt::format("{:09}", GetFractionNano()));
- break;
- default:
- Result.Append(*Format);
- }
- }
- else
- {
- Result.Append(*Format);
- }
-
- ++Format;
- }
-
- return Result.ToString();
-}
-
-std::string
-TimeSpan::ToString() const
-{
- if (GetDays() == 0)
- {
- return ToString("%h:%m:%s.%f");
- }
-
- return ToString("%d.%h:%m:%s.%f");
-}
-
-StringBuilderBase&
-Guid::ToString(StringBuilderBase& Sb) const
-{
- char Buf[128];
- snprintf(Buf, sizeof Buf, "%08x-%04x-%04x-%04x-%04x%08x", A, B >> 16, B & 0xFFFF, C >> 16, C & 0xFFFF, D);
- Sb << Buf;
-
- return Sb;
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-namespace CompactBinaryPrivate {
- static constexpr const uint8_t GEmptyObjectPayload[] = {uint8_t(CbFieldType::Object), 0x00};
- static constexpr const uint8_t GEmptyArrayPayload[] = {uint8_t(CbFieldType::Array), 0x01, 0x00};
-} // namespace CompactBinaryPrivate
-
-//////////////////////////////////////////////////////////////////////////
-
-CbFieldView::CbFieldView(const void* DataPointer, CbFieldType FieldType)
-{
- const uint8_t* Bytes = static_cast<const uint8_t*>(DataPointer);
- const CbFieldType LocalType = CbFieldTypeOps::HasFieldType(FieldType) ? (CbFieldType(*Bytes++) | CbFieldType::HasFieldType) : FieldType;
-
- uint32_t NameLenByteCount = 0;
- const uint64_t NameLen64 = CbFieldTypeOps::HasFieldName(LocalType) ? ReadVarUInt(Bytes, NameLenByteCount) : 0;
- Bytes += NameLen64 + NameLenByteCount;
-
- Type = LocalType;
- NameLen = uint32_t(std::clamp<uint64_t>(NameLen64, 0, ~uint32_t(0)));
- Payload = Bytes;
-}
-
-void
-CbFieldView::IterateAttachments(std::function<void(CbFieldView)> Visitor) const
-{
- switch (CbFieldTypeOps::GetType(Type))
- {
- case CbFieldType::Object:
- case CbFieldType::UniformObject:
- return CbObjectView::FromFieldView(*this).IterateAttachments(Visitor);
- case CbFieldType::Array:
- case CbFieldType::UniformArray:
- return CbArrayView::FromFieldView(*this).IterateAttachments(Visitor);
- case CbFieldType::ObjectAttachment:
- case CbFieldType::BinaryAttachment:
- return Visitor(*this);
- default:
- return;
- }
-}
-
-CbObjectView
-CbFieldView::AsObjectView()
-{
- if (CbFieldTypeOps::IsObject(Type))
- {
- Error = CbFieldError::None;
- return CbObjectView::FromFieldView(*this);
- }
- else
- {
- Error = CbFieldError::TypeError;
- return CbObjectView();
- }
-}
-
-CbArrayView
-CbFieldView::AsArrayView()
-{
- if (CbFieldTypeOps::IsArray(Type))
- {
- Error = CbFieldError::None;
- return CbArrayView::FromFieldView(*this);
- }
- else
- {
- Error = CbFieldError::TypeError;
- return CbArrayView();
- }
-}
-
-MemoryView
-CbFieldView::AsBinaryView(const MemoryView Default)
-{
- if (CbValue Accessor = GetValue(); CbFieldTypeOps::IsBinary(Accessor.GetType()))
- {
- Error = CbFieldError::None;
- return Accessor.AsBinary();
- }
- else
- {
- Error = CbFieldError::TypeError;
- return Default;
- }
-}
-
-std::string_view
-CbFieldView::AsString(const std::string_view Default)
-{
- if (CbValue Accessor = GetValue(); CbFieldTypeOps::IsString(Accessor.GetType()))
- {
- Error = CbFieldError::None;
- return Accessor.AsString();
- }
- else
- {
- Error = CbFieldError::TypeError;
- return Default;
- }
-}
-
-std::u8string_view
-CbFieldView::AsU8String(const std::u8string_view Default)
-{
- if (CbValue Accessor = GetValue(); CbFieldTypeOps::IsString(Accessor.GetType()))
- {
- Error = CbFieldError::None;
- return Accessor.AsU8String();
- }
- else
- {
- Error = CbFieldError::TypeError;
- return Default;
- }
-}
-
-uint64_t
-CbFieldView::AsInteger(const uint64_t Default, const CompactBinaryPrivate::IntegerParams Params)
-{
- if (CbValue Accessor = GetValue(); CbFieldTypeOps::IsInteger(Accessor.GetType()))
- {
- return Accessor.AsInteger(Params, &Error, Default);
- }
- else
- {
- Error = CbFieldError::TypeError;
- return Default;
- }
-}
-
-float
-CbFieldView::AsFloat(const float Default)
-{
- switch (CbValue Accessor = GetValue(); Accessor.GetType())
- {
- case CbFieldType::IntegerPositive:
- case CbFieldType::IntegerNegative:
- {
- const uint64_t IsNegative = uint8_t(Accessor.GetType()) & 1;
- constexpr uint64_t OutOfRangeMask = ~((uint64_t(1) << /*FLT_MANT_DIG*/ 24) - 1);
-
- uint32_t MagnitudeByteCount;
- const int64_t Magnitude = ReadVarUInt(Accessor.GetData(), MagnitudeByteCount) + IsNegative;
- const uint64_t IsInRange = !(Magnitude & OutOfRangeMask);
- Error = IsInRange ? CbFieldError::None : CbFieldError::RangeError;
- return IsInRange ? float(IsNegative ? -Magnitude : Magnitude) : Default;
- }
- case CbFieldType::Float32:
- {
- Error = CbFieldError::None;
- return Accessor.AsFloat32();
- }
- case CbFieldType::Float64:
- Error = CbFieldError::RangeError;
- return Default;
- default:
- Error = CbFieldError::TypeError;
- return Default;
- }
-}
-
-double
-CbFieldView::AsDouble(const double Default)
-{
- switch (CbValue Accessor = GetValue(); Accessor.GetType())
- {
- case CbFieldType::IntegerPositive:
- case CbFieldType::IntegerNegative:
- {
- const uint64_t IsNegative = uint8_t(Accessor.GetType()) & 1;
- constexpr uint64_t OutOfRangeMask = ~((uint64_t(1) << /*DBL_MANT_DIG*/ 53) - 1);
-
- uint32_t MagnitudeByteCount;
- const int64_t Magnitude = ReadVarUInt(Accessor.GetData(), MagnitudeByteCount) + IsNegative;
- const uint64_t IsInRange = !(Magnitude & OutOfRangeMask);
- Error = IsInRange ? CbFieldError::None : CbFieldError::RangeError;
- return IsInRange ? double(IsNegative ? -Magnitude : Magnitude) : Default;
- }
- case CbFieldType::Float32:
- {
- Error = CbFieldError::None;
- return Accessor.AsFloat32();
- }
- case CbFieldType::Float64:
- {
- Error = CbFieldError::None;
- return Accessor.AsFloat64();
- }
- default:
- Error = CbFieldError::TypeError;
- return Default;
- }
-}
-
-bool
-CbFieldView::AsBool(const bool bDefault)
-{
- CbValue Accessor = GetValue();
- const bool IsBool = CbFieldTypeOps::IsBool(Accessor.GetType());
- Error = IsBool ? CbFieldError::None : CbFieldError::TypeError;
- return (uint8_t(IsBool) & Accessor.AsBool()) | ((!IsBool) & bDefault);
-}
-
-IoHash
-CbFieldView::AsObjectAttachment(const IoHash& Default)
-{
- if (CbValue Accessor = GetValue(); CbFieldTypeOps::IsObjectAttachment(Accessor.GetType()))
- {
- Error = CbFieldError::None;
- return Accessor.AsObjectAttachment();
- }
- else
- {
- Error = CbFieldError::TypeError;
- return Default;
- }
-}
-
-IoHash
-CbFieldView::AsBinaryAttachment(const IoHash& Default)
-{
- if (CbValue Accessor = GetValue(); CbFieldTypeOps::IsBinaryAttachment(Accessor.GetType()))
- {
- Error = CbFieldError::None;
- return Accessor.AsBinaryAttachment();
- }
- else
- {
- Error = CbFieldError::TypeError;
- return Default;
- }
-}
-
-IoHash
-CbFieldView::AsAttachment(const IoHash& Default)
-{
- if (CbValue Accessor = GetValue(); CbFieldTypeOps::IsAttachment(Accessor.GetType()))
- {
- Error = CbFieldError::None;
- return Accessor.AsAttachment();
- }
- else
- {
- Error = CbFieldError::TypeError;
- return Default;
- }
-}
-
-IoHash
-CbFieldView::AsHash(const IoHash& Default)
-{
- if (CbValue Accessor = GetValue(); CbFieldTypeOps::IsHash(Accessor.GetType()))
- {
- Error = CbFieldError::None;
- return Accessor.AsHash();
- }
- else
- {
- Error = CbFieldError::TypeError;
- return Default;
- }
-}
-
-Guid
-CbFieldView::AsUuid()
-{
- return AsUuid(Guid());
-}
-
-Guid
-CbFieldView::AsUuid(const Guid& Default)
-{
- if (CbValue Accessor = GetValue(); CbFieldTypeOps::IsUuid(Accessor.GetType()))
- {
- Error = CbFieldError::None;
- return Accessor.AsUuid();
- }
- else
- {
- Error = CbFieldError::TypeError;
- return Default;
- }
-}
-
-Oid
-CbFieldView::AsObjectId()
-{
- return AsObjectId(Oid());
-}
-
-Oid
-CbFieldView::AsObjectId(const Oid& Default)
-{
- if (CbValue Accessor = GetValue(); CbFieldTypeOps::IsObjectId(Accessor.GetType()))
- {
- Error = CbFieldError::None;
- return Accessor.AsObjectId();
- }
- else
- {
- Error = CbFieldError::TypeError;
- return Default;
- }
-}
-
-CbCustomById
-CbFieldView::AsCustomById(CbCustomById Default)
-{
- if (CbValue Accessor = GetValue(); CbFieldTypeOps::IsCustomById(Accessor.GetType()))
- {
- Error = CbFieldError::None;
- return Accessor.AsCustomById();
- }
- else
- {
- Error = CbFieldError::TypeError;
- return Default;
- }
-}
-
-CbCustomByName
-CbFieldView::AsCustomByName(CbCustomByName Default)
-{
- if (CbValue Accessor = GetValue(); CbFieldTypeOps::IsCustomByName(Accessor.GetType()))
- {
- Error = CbFieldError::None;
- return Accessor.AsCustomByName();
- }
- else
- {
- Error = CbFieldError::TypeError;
- return Default;
- }
-}
-
-int64_t
-CbFieldView::AsDateTimeTicks(const int64_t Default)
-{
- if (CbValue Accessor = GetValue(); CbFieldTypeOps::IsDateTime(Accessor.GetType()))
- {
- Error = CbFieldError::None;
- return Accessor.AsDateTimeTicks();
- }
- else
- {
- Error = CbFieldError::TypeError;
- return Default;
- }
-}
-
-DateTime
-CbFieldView::AsDateTime()
-{
- return DateTime(AsDateTimeTicks(0));
-}
-
-DateTime
-CbFieldView::AsDateTime(DateTime Default)
-{
- return DateTime(AsDateTimeTicks(Default.GetTicks()));
-}
-
-int64_t
-CbFieldView::AsTimeSpanTicks(const int64_t Default)
-{
- if (CbValue Accessor = GetValue(); CbFieldTypeOps::IsTimeSpan(Accessor.GetType()))
- {
- Error = CbFieldError::None;
- return Accessor.AsTimeSpanTicks();
- }
- else
- {
- Error = CbFieldError::TypeError;
- return Default;
- }
-}
-
-TimeSpan
-CbFieldView::AsTimeSpan()
-{
- return TimeSpan(AsTimeSpanTicks(0));
-}
-
-TimeSpan
-CbFieldView::AsTimeSpan(TimeSpan Default)
-{
- return TimeSpan(AsTimeSpanTicks(Default.GetTicks()));
-}
-
-uint64_t
-CbFieldView::GetSize() const
-{
- return sizeof(CbFieldType) + GetViewNoType().GetSize();
-}
-
-uint64_t
-CbFieldView::GetPayloadSize() const
-{
- switch (CbFieldTypeOps::GetType(Type))
- {
- case CbFieldType::None:
- case CbFieldType::Null:
- return 0;
- case CbFieldType::Object:
- case CbFieldType::UniformObject:
- case CbFieldType::Array:
- case CbFieldType::UniformArray:
- case CbFieldType::Binary:
- case CbFieldType::String:
- {
- uint32_t PayloadSizeByteCount;
- const uint64_t PayloadSize = ReadVarUInt(Payload, PayloadSizeByteCount);
- return PayloadSize + PayloadSizeByteCount;
- }
- case CbFieldType::IntegerPositive:
- case CbFieldType::IntegerNegative:
- return MeasureVarUInt(Payload);
- case CbFieldType::Float32:
- return 4;
- case CbFieldType::Float64:
- return 8;
- case CbFieldType::BoolFalse:
- case CbFieldType::BoolTrue:
- return 0;
- case CbFieldType::ObjectAttachment:
- case CbFieldType::BinaryAttachment:
- case CbFieldType::Hash:
- return 20;
- case CbFieldType::Uuid:
- return 16;
- case CbFieldType::ObjectId:
- return 12;
- case CbFieldType::DateTime:
- case CbFieldType::TimeSpan:
- return 8;
- default:
- return 0;
- }
-}
-
-IoHash
-CbFieldView::GetHash() const
-{
- IoHashStream HashStream;
- GetHash(HashStream);
- return HashStream.GetHash();
-}
-
-void
-CbFieldView::GetHash(IoHashStream& Hash) const
-{
- const CbFieldType SerializedType = CbFieldTypeOps::GetSerializedType(Type);
- Hash.Append(&SerializedType, sizeof(SerializedType));
- auto View = GetViewNoType();
- Hash.Append(View.GetData(), View.GetSize());
-}
-
-bool
-CbFieldView::Equals(const CbFieldView& Other) const
-{
- return CbFieldTypeOps::GetSerializedType(Type) == CbFieldTypeOps::GetSerializedType(Other.Type) &&
- GetViewNoType().EqualBytes(Other.GetViewNoType());
-}
-
-void
-CbFieldView::CopyTo(MutableMemoryView Buffer) const
-{
- const MemoryView Source = GetViewNoType();
- ZEN_ASSERT(Buffer.GetSize() == sizeof(CbFieldType) + Source.GetSize());
- // TEXT("A buffer of %" UINT64_FMT " bytes was provided when %" UINT64_FMT " bytes are required"),
- // Buffer.GetSize(),
- // sizeof(CbFieldType) + Source.GetSize());
- *static_cast<CbFieldType*>(Buffer.GetData()) = CbFieldTypeOps::GetSerializedType(Type);
- Buffer.RightChopInline(sizeof(CbFieldType));
- memcpy(Buffer.GetData(), Source.GetData(), Source.GetSize());
-}
-
-void
-CbFieldView::CopyTo(BinaryWriter& Ar) const
-{
- const MemoryView SourceView = GetViewNoType();
- CbFieldType SerializedType = CbFieldTypeOps::GetSerializedType(Type);
- const MemoryView TypeView(reinterpret_cast<const uint8_t*>(&SerializedType), sizeof(SerializedType));
- Ar.Write({TypeView, SourceView});
-}
-
-MemoryView
-CbFieldView::GetView() const
-{
- const uint32_t TypeSize = CbFieldTypeOps::HasFieldType(Type) ? sizeof(CbFieldType) : 0;
- const uint32_t NameSize = CbFieldTypeOps::HasFieldName(Type) ? NameLen + MeasureVarUInt(NameLen) : 0;
- const uint64_t PayloadSize = GetPayloadSize();
- return MemoryView(static_cast<const uint8_t*>(Payload) - TypeSize - NameSize, TypeSize + NameSize + PayloadSize);
-}
-
-MemoryView
-CbFieldView::GetViewNoType() const
-{
- const uint32_t NameSize = CbFieldTypeOps::HasFieldName(Type) ? NameLen + MeasureVarUInt(NameLen) : 0;
- const uint64_t PayloadSize = GetPayloadSize();
- return MemoryView(static_cast<const uint8_t*>(Payload) - NameSize, NameSize + PayloadSize);
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-CbArrayView::CbArrayView() : CbFieldView(CompactBinaryPrivate::GEmptyArrayPayload)
-{
-}
-
-uint64_t
-CbArrayView::Num() const
-{
- const uint8_t* PayloadBytes = static_cast<const uint8_t*>(GetPayload());
- PayloadBytes += MeasureVarUInt(PayloadBytes);
- uint32_t NumByteCount;
- return ReadVarUInt(PayloadBytes, NumByteCount);
-}
-
-CbFieldViewIterator
-CbArrayView::CreateViewIterator() const
-{
- const uint8_t* PayloadBytes = static_cast<const uint8_t*>(GetPayload());
- uint32_t PayloadSizeByteCount;
- const uint64_t PayloadSize = ReadVarUInt(PayloadBytes, PayloadSizeByteCount);
- PayloadBytes += PayloadSizeByteCount;
- const uint64_t NumByteCount = MeasureVarUInt(PayloadBytes);
- if (PayloadSize > NumByteCount)
- {
- const void* const PayloadEnd = PayloadBytes + PayloadSize;
- PayloadBytes += NumByteCount;
- const CbFieldType UniformType =
- CbFieldTypeOps::GetType(GetType()) == CbFieldType::UniformArray ? CbFieldType(*PayloadBytes++) : CbFieldType::HasFieldType;
- return CbFieldViewIterator::MakeRange(MemoryView(PayloadBytes, PayloadEnd), UniformType);
- }
- return CbFieldViewIterator();
-}
-
-void
-CbArrayView::VisitFields(ICbVisitor&)
-{
-}
-
-uint64_t
-CbArrayView::GetSize() const
-{
- return sizeof(CbFieldType) + GetPayloadSize();
-}
-
-IoHash
-CbArrayView::GetHash() const
-{
- IoHashStream Hash;
- GetHash(Hash);
- return Hash.GetHash();
-}
-
-void
-CbArrayView::GetHash(IoHashStream& HashStream) const
-{
- const CbFieldType SerializedType = CbFieldTypeOps::GetType(GetType());
- HashStream.Append(&SerializedType, sizeof(SerializedType));
- auto _ = GetPayloadView();
- HashStream.Append(_.GetData(), _.GetSize());
-}
-
-bool
-CbArrayView::Equals(const CbArrayView& Other) const
-{
- return CbFieldTypeOps::GetType(GetType()) == CbFieldTypeOps::GetType(Other.GetType()) &&
- GetPayloadView().EqualBytes(Other.GetPayloadView());
-}
-
-void
-CbArrayView::CopyTo(MutableMemoryView Buffer) const
-{
- const MemoryView Source = GetPayloadView();
- ZEN_ASSERT(Buffer.GetSize() == sizeof(CbFieldType) + Source.GetSize());
- // TEXT("Buffer is %" UINT64_FMT " bytes but %" UINT64_FMT " is required."),
- // Buffer.GetSize(),
- // sizeof(CbFieldType) + Source.GetSize());
-
- *static_cast<CbFieldType*>(Buffer.GetData()) = CbFieldTypeOps::GetType(GetType());
- Buffer.RightChopInline(sizeof(CbFieldType));
- memcpy(Buffer.GetData(), Source.GetData(), Source.GetSize());
-}
-
-void
-CbArrayView::CopyTo(BinaryWriter& Ar) const
-{
- const MemoryView SourceView = GetPayloadView();
- CbFieldType SerializedType = CbFieldTypeOps::GetSerializedType(GetType());
- const MemoryView TypeView(reinterpret_cast<const uint8_t*>(&SerializedType), sizeof(SerializedType));
- Ar.Write({TypeView, SourceView});
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-CbObjectView::CbObjectView() : CbFieldView(CompactBinaryPrivate::GEmptyObjectPayload)
-{
-}
-
-CbFieldViewIterator
-CbObjectView::CreateViewIterator() const
-{
- const uint8_t* PayloadBytes = static_cast<const uint8_t*>(GetPayload());
- uint32_t PayloadSizeByteCount;
- const uint64_t PayloadSize = ReadVarUInt(PayloadBytes, PayloadSizeByteCount);
-
- PayloadBytes += PayloadSizeByteCount;
-
- if (PayloadSize)
- {
- const void* const PayloadEnd = PayloadBytes + PayloadSize;
- const CbFieldType UniformType =
- CbFieldTypeOps::GetType(GetType()) == CbFieldType::UniformObject ? CbFieldType(*PayloadBytes++) : CbFieldType::HasFieldType;
- return CbFieldViewIterator::MakeRange(MemoryView(PayloadBytes, PayloadEnd), UniformType);
- }
-
- return CbFieldViewIterator();
-}
-
-void
-CbObjectView::VisitFields(ICbVisitor&)
-{
-}
-
-CbFieldView
-CbObjectView::FindView(const std::string_view Name) const
-{
- for (const CbFieldView& Field : *this)
- {
- if (Name == Field.GetName())
- {
- return Field;
- }
- }
- return CbFieldView();
-}
-
-CbFieldView
-CbObjectView::FindViewIgnoreCase(const std::string_view Name) const
-{
- for (const CbFieldView& Field : *this)
- {
- if (Name == Field.GetName())
- {
- return Field;
- }
- }
- return CbFieldView();
-}
-
-CbObjectView::operator bool() const
-{
- return GetSize() > sizeof(CompactBinaryPrivate::GEmptyObjectPayload);
-}
-
-uint64_t
-CbObjectView::GetSize() const
-{
- return sizeof(CbFieldType) + GetPayloadSize();
-}
-
-IoHash
-CbObjectView::GetHash() const
-{
- IoHashStream Hash;
- GetHash(Hash);
- return Hash.GetHash();
-}
-
-void
-CbObjectView::GetHash(IoHashStream& HashStream) const
-{
- const CbFieldType SerializedType = CbFieldTypeOps::GetType(GetType());
- HashStream.Append(&SerializedType, sizeof(SerializedType));
- HashStream.Append(GetPayloadView());
-}
-
-bool
-CbObjectView::Equals(const CbObjectView& Other) const
-{
- return CbFieldTypeOps::GetType(GetType()) == CbFieldTypeOps::GetType(Other.GetType()) &&
- GetPayloadView().EqualBytes(Other.GetPayloadView());
-}
-
-void
-CbObjectView::CopyTo(MutableMemoryView Buffer) const
-{
- const MemoryView Source = GetPayloadView();
- ZEN_ASSERT(Buffer.GetSize() == (sizeof(CbFieldType) + Source.GetSize()));
- // TEXT("Buffer is %" UINT64_FMT " bytes but %" UINT64_FMT " is required."),
- // Buffer.GetSize(),
- // sizeof(CbFieldType) + Source.GetSize());
- *static_cast<CbFieldType*>(Buffer.GetData()) = CbFieldTypeOps::GetType(GetType());
- Buffer.RightChopInline(sizeof(CbFieldType));
- memcpy(Buffer.GetData(), Source.GetData(), Source.GetSize());
-}
-
-void
-CbObjectView::CopyTo(BinaryWriter& Ar) const
-{
- const MemoryView SourceView = GetPayloadView();
- CbFieldType SerializedType = CbFieldTypeOps::GetSerializedType(GetType());
- const MemoryView TypeView(reinterpret_cast<const uint8_t*>(&SerializedType), sizeof(SerializedType));
- Ar.Write({TypeView, SourceView});
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-template<typename FieldType>
-uint64_t
-TCbFieldIterator<FieldType>::GetRangeSize() const
-{
- MemoryView View;
- if (TryGetSerializedRangeView(View))
- {
- return View.GetSize();
- }
- else
- {
- uint64_t Size = 0;
- for (CbFieldViewIterator It(*this); It; ++It)
- {
- Size += It.GetSize();
- }
- return Size;
- }
-}
-
-template<typename FieldType>
-IoHash
-TCbFieldIterator<FieldType>::GetRangeHash() const
-{
- IoHashStream Hash;
- GetRangeHash(Hash);
- return IoHash(Hash.GetHash());
-}
-
-template<typename FieldType>
-void
-TCbFieldIterator<FieldType>::GetRangeHash(IoHashStream& Hash) const
-{
- MemoryView View;
- if (TryGetSerializedRangeView(View))
- {
- Hash.Append(View.GetData(), View.GetSize());
- }
- else
- {
- for (CbFieldViewIterator It(*this); It; ++It)
- {
- It.GetHash(Hash);
- }
- }
-}
-
-template<typename FieldType>
-void
-TCbFieldIterator<FieldType>::CopyRangeTo(MutableMemoryView InBuffer) const
-{
- MemoryView Source;
- if (TryGetSerializedRangeView(Source))
- {
- ZEN_ASSERT(InBuffer.GetSize() == Source.GetSize());
- // TEXT("Buffer is %" UINT64_FMT " bytes but %" UINT64_FMT " is required."),
- // InBuffer.GetSize(),
- // Source.GetSize());
- memcpy(InBuffer.GetData(), Source.GetData(), Source.GetSize());
- }
- else
- {
- for (CbFieldViewIterator It(*this); It; ++It)
- {
- const uint64_t Size = It.GetSize();
- It.CopyTo(InBuffer.Left(Size));
- InBuffer.RightChopInline(Size);
- }
- }
-}
-
-template class TCbFieldIterator<CbFieldView>;
-template class TCbFieldIterator<CbField>;
-
-template<typename FieldType>
-void
-TCbFieldIterator<FieldType>::IterateRangeAttachments(std::function<void(CbFieldView)> Visitor) const
-{
- if (CbFieldTypeOps::HasFieldType(FieldType::GetType()))
- {
- // Always iterate over non-uniform ranges because we do not know if they contain an attachment.
- for (CbFieldViewIterator It(*this); It; ++It)
- {
- if (CbFieldTypeOps::MayContainAttachments(It.GetType()))
- {
- It.IterateAttachments(Visitor);
- }
- }
- }
- else
- {
- // Only iterate over uniform ranges if the uniform type may contain an attachment.
- if (CbFieldTypeOps::MayContainAttachments(FieldType::GetType()))
- {
- for (CbFieldViewIterator It(*this); It; ++It)
- {
- It.IterateAttachments(Visitor);
- }
- }
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-CbFieldIterator
-CbFieldIterator::CloneRange(const CbFieldViewIterator& It)
-{
- MemoryView View;
- if (It.TryGetSerializedRangeView(View))
- {
- return MakeRange(SharedBuffer::Clone(View));
- }
- else
- {
- UniqueBuffer Buffer = UniqueBuffer::Alloc(It.GetRangeSize());
- It.CopyRangeTo(MutableMemoryView(Buffer.GetData(), Buffer.GetSize()));
- return MakeRange(SharedBuffer(std::move(Buffer)));
- }
-}
-
-SharedBuffer
-CbFieldIterator::GetRangeBuffer() const
-{
- const MemoryView RangeView = GetRangeView();
- const SharedBuffer& OuterBuffer = GetOuterBuffer();
- return OuterBuffer.GetView() == RangeView ? OuterBuffer : SharedBuffer::MakeView(RangeView, OuterBuffer);
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-uint64_t
-MeasureCompactBinary(MemoryView View, CbFieldType Type)
-{
- uint64_t Size;
- return TryMeasureCompactBinary(View, Type, Size, Type) ? Size : 0;
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-bool
-TryMeasureCompactBinary(MemoryView View, CbFieldType& OutType, uint64_t& OutSize, CbFieldType Type)
-{
- uint64_t Size = 0;
-
- if (CbFieldTypeOps::HasFieldType(Type))
- {
- if (View.GetSize() == 0)
- {
- OutType = CbFieldType::None;
- OutSize = 1;
- return false;
- }
-
- Type = *static_cast<const CbFieldType*>(View.GetData());
- View.RightChopInline(1);
- Size += 1;
- }
-
- bool bDynamicSize = false;
- uint64_t FixedSize = 0;
- switch (CbFieldTypeOps::GetType(Type))
- {
- case CbFieldType::Null:
- break;
- case CbFieldType::Object:
- case CbFieldType::UniformObject:
- case CbFieldType::Array:
- case CbFieldType::UniformArray:
- case CbFieldType::Binary:
- case CbFieldType::String:
- case CbFieldType::IntegerPositive:
- case CbFieldType::IntegerNegative:
- bDynamicSize = true;
- break;
- case CbFieldType::Float32:
- FixedSize = 4;
- break;
- case CbFieldType::Float64:
- FixedSize = 8;
- break;
- case CbFieldType::BoolFalse:
- case CbFieldType::BoolTrue:
- break;
- case CbFieldType::ObjectAttachment:
- case CbFieldType::BinaryAttachment:
- case CbFieldType::Hash:
- FixedSize = 20;
- break;
- case CbFieldType::Uuid:
- FixedSize = 16;
- break;
- case CbFieldType::ObjectId:
- FixedSize = 12;
- break;
- case CbFieldType::DateTime:
- case CbFieldType::TimeSpan:
- FixedSize = 8;
- break;
- case CbFieldType::None:
- default:
- OutType = CbFieldType::None;
- OutSize = 0;
- return false;
- }
-
- OutType = Type;
-
- if (CbFieldTypeOps::HasFieldName(Type))
- {
- if (View.GetSize() == 0)
- {
- OutSize = Size + 1;
- return false;
- }
-
- uint32_t NameLenByteCount = MeasureVarUInt(View.GetData());
- if (View.GetSize() < NameLenByteCount)
- {
- OutSize = Size + NameLenByteCount;
- return false;
- }
-
- const uint64_t NameLen = ReadVarUInt(View.GetData(), NameLenByteCount);
- const uint64_t NameSize = NameLen + NameLenByteCount;
-
- if (bDynamicSize && View.GetSize() < NameSize)
- {
- OutSize = Size + NameSize;
- return false;
- }
-
- View.RightChopInline(NameSize);
- Size += NameSize;
- }
-
- switch (CbFieldTypeOps::GetType(Type))
- {
- case CbFieldType::Object:
- case CbFieldType::UniformObject:
- case CbFieldType::Array:
- case CbFieldType::UniformArray:
- case CbFieldType::Binary:
- case CbFieldType::String:
- if (View.GetSize() == 0)
- {
- OutSize = Size + 1;
- return false;
- }
- else
- {
- uint32_t PayloadSizeByteCount = MeasureVarUInt(View.GetData());
- if (View.GetSize() < PayloadSizeByteCount)
- {
- OutSize = Size + PayloadSizeByteCount;
- return false;
- }
- const uint64_t PayloadSize = ReadVarUInt(View.GetData(), PayloadSizeByteCount);
- OutSize = Size + PayloadSize + PayloadSizeByteCount;
- }
- return true;
-
- case CbFieldType::IntegerPositive:
- case CbFieldType::IntegerNegative:
- if (View.GetSize() == 0)
- {
- OutSize = Size + 1;
- return false;
- }
- OutSize = Size + MeasureVarUInt(View.GetData());
- return true;
-
- default:
- OutSize = Size + FixedSize;
- return true;
- }
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-CbField
-LoadCompactBinary(BinaryReader& Ar, BufferAllocator Allocator)
-{
- std::vector<uint8_t> HeaderBytes;
- CbFieldType FieldType;
- uint64_t FieldSize = 1;
-
- for (const int64_t StartPos = Ar.CurrentOffset(); FieldSize > 0;)
- {
- // Read in small increments until the total field size is known, to avoid reading too far.
- const int32_t ReadSize = int32_t(FieldSize - HeaderBytes.size());
- if (Ar.CurrentOffset() + ReadSize > Ar.GetSize())
- {
- break;
- }
-
- const size_t ReadOffset = HeaderBytes.size();
- HeaderBytes.resize(ReadOffset + ReadSize);
-
- Ar.Read(HeaderBytes.data() + ReadOffset, ReadSize);
- if (TryMeasureCompactBinary(MakeMemoryView(HeaderBytes), FieldType, FieldSize))
- {
- if (FieldSize <= uint64_t(Ar.Size() - StartPos))
- {
- UniqueBuffer Buffer = Allocator(FieldSize);
- ZEN_ASSERT(Buffer.GetSize() == FieldSize);
- MutableMemoryView View = Buffer.GetMutableView();
- memcpy(View.GetData(), HeaderBytes.data(), HeaderBytes.size());
- View.RightChopInline(HeaderBytes.size());
- if (!View.IsEmpty())
- {
- // Read the remainder of the field.
- Ar.Read(View.GetData(), View.GetSize());
- }
- if (ValidateCompactBinary(Buffer, CbValidateMode::Default) == CbValidateError::None)
- {
- return CbField(SharedBuffer(std::move(Buffer)));
- }
- }
- break;
- }
- }
- return CbField();
-}
-
-CbObject
-LoadCompactBinaryObject(IoBuffer&& Payload)
-{
- return CbObject{SharedBuffer(std::move(Payload))};
-}
-
-CbObject
-LoadCompactBinaryObject(const IoBuffer& Payload)
-{
- return CbObject{SharedBuffer(Payload)};
-}
-
-CbObject
-LoadCompactBinaryObject(CompressedBuffer&& Payload)
-{
- return CbObject{SharedBuffer(Payload.DecompressToComposite().Flatten())};
-}
-
-CbObject
-LoadCompactBinaryObject(const CompressedBuffer& Payload)
-{
- return CbObject{SharedBuffer(Payload.DecompressToComposite().Flatten())};
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-void
-SaveCompactBinary(BinaryWriter& Ar, const CbFieldView& Field)
-{
- Field.CopyTo(Ar);
-}
-
-void
-SaveCompactBinary(BinaryWriter& Ar, const CbArrayView& Array)
-{
- Array.CopyTo(Ar);
-}
-
-void
-SaveCompactBinary(BinaryWriter& Ar, const CbObjectView& Object)
-{
- Object.CopyTo(Ar);
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-class CbJsonWriter
-{
-public:
- explicit CbJsonWriter(StringBuilderBase& InBuilder) : Builder(InBuilder) { NewLineAndIndent << LINE_TERMINATOR_ANSI; }
-
- void WriteField(CbFieldView Field)
- {
- using namespace std::literals;
-
- WriteOptionalComma();
- WriteOptionalNewLine();
-
- if (std::u8string_view Name = Field.GetU8Name(); !Name.empty())
- {
- AppendQuotedString(Name);
- Builder << ": "sv;
- }
-
- switch (CbValue Accessor = Field.GetValue(); Accessor.GetType())
- {
- case CbFieldType::Null:
- Builder << "null"sv;
- break;
- case CbFieldType::Object:
- case CbFieldType::UniformObject:
- {
- Builder << '{';
- NewLineAndIndent << '\t';
- NeedsNewLine = true;
- for (CbFieldView It : Field)
- {
- WriteField(It);
- }
- NewLineAndIndent.RemoveSuffix(1);
- if (NeedsComma)
- {
- WriteOptionalNewLine();
- }
- Builder << '}';
- }
- break;
- case CbFieldType::Array:
- case CbFieldType::UniformArray:
- {
- Builder << '[';
- NewLineAndIndent << '\t';
- NeedsNewLine = true;
- for (CbFieldView It : Field)
- {
- WriteField(It);
- }
- NewLineAndIndent.RemoveSuffix(1);
- if (NeedsComma)
- {
- WriteOptionalNewLine();
- }
- Builder << ']';
- }
- break;
- case CbFieldType::Binary:
- AppendBase64String(Accessor.AsBinary());
- break;
- case CbFieldType::String:
- AppendQuotedString(Accessor.AsU8String());
- break;
- case CbFieldType::IntegerPositive:
- Builder << Accessor.AsIntegerPositive();
- break;
- case CbFieldType::IntegerNegative:
- Builder << Accessor.AsIntegerNegative();
- break;
- case CbFieldType::Float32:
- {
- const float Value = Accessor.AsFloat32();
- if (std::isfinite(Value))
- {
- Builder.Append(fmt::format("{:.9g}", Value));
- }
- else
- {
- Builder << "null"sv;
- }
- }
- break;
- case CbFieldType::Float64:
- {
- const double Value = Accessor.AsFloat64();
- if (std::isfinite(Value))
- {
- Builder.Append(fmt::format("{:.17g}", Value));
- }
- else
- {
- Builder << "null"sv;
- }
- }
- break;
- case CbFieldType::BoolFalse:
- Builder << "false"sv;
- break;
- case CbFieldType::BoolTrue:
- Builder << "true"sv;
- break;
- case CbFieldType::ObjectAttachment:
- case CbFieldType::BinaryAttachment:
- {
- Builder << '"';
- Accessor.AsAttachment().ToHexString(Builder);
- Builder << '"';
- }
- break;
- case CbFieldType::Hash:
- {
- Builder << '"';
- Accessor.AsHash().ToHexString(Builder);
- Builder << '"';
- }
- break;
- case CbFieldType::Uuid:
- {
- Builder << '"';
- Accessor.AsUuid().ToString(Builder);
- Builder << '"';
- }
- break;
- case CbFieldType::DateTime:
- Builder << '"' << DateTime(Accessor.AsDateTimeTicks()).ToIso8601() << '"';
- break;
- case CbFieldType::TimeSpan:
- {
- const TimeSpan Span(Accessor.AsTimeSpanTicks());
- if (Span.GetDays() == 0)
- {
- Builder << '"' << Span.ToString("%h:%m:%s.%n") << '"';
- }
- else
- {
- Builder << '"' << Span.ToString("%d.%h:%m:%s.%n") << '"';
- }
- break;
- }
- case CbFieldType::ObjectId:
- Builder << '"';
- Accessor.AsObjectId().ToString(Builder);
- Builder << '"';
- break;
- case CbFieldType::CustomById:
- {
- CbCustomById Custom = Accessor.AsCustomById();
- Builder << "{ \"Id\": ";
- Builder << Custom.Id;
- Builder << ", \"Data\": ";
- AppendBase64String(Custom.Data);
- Builder << " }";
- break;
- }
- case CbFieldType::CustomByName:
- {
- CbCustomByName Custom = Accessor.AsCustomByName();
- Builder << "{ \"Name\": ";
- AppendQuotedString(Custom.Name);
- Builder << ", \"Data\": ";
- AppendBase64String(Custom.Data);
- Builder << " }";
- break;
- }
- default:
- ZEN_ASSERT(false);
- break;
- }
-
- NeedsComma = true;
- NeedsNewLine = true;
- }
-
-private:
- void WriteOptionalComma()
- {
- if (NeedsComma)
- {
- NeedsComma = false;
- Builder << ',';
- }
- }
-
- void WriteOptionalNewLine()
- {
- if (NeedsNewLine)
- {
- NeedsNewLine = false;
- Builder << NewLineAndIndent;
- }
- }
-
- void AppendQuotedString(std::u8string_view Value)
- {
- using namespace std::literals;
-
- const AsciiSet EscapeSet(
- "\\\"\b\f\n\r\t"
- "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
- "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f");
-
- Builder << '\"';
- while (!Value.empty())
- {
- std::u8string_view Verbatim = AsciiSet::FindPrefixWithout(Value, EscapeSet);
- Builder << Verbatim;
-
- Value = Value.substr(Verbatim.size());
-
- std::u8string_view Escape = AsciiSet::FindPrefixWith(Value, EscapeSet);
- for (char Char : Escape)
- {
- switch (Char)
- {
- case '\\':
- Builder << "\\\\"sv;
- break;
- case '\"':
- Builder << "\\\""sv;
- break;
- case '\b':
- Builder << "\\b"sv;
- break;
- case '\f':
- Builder << "\\f"sv;
- break;
- case '\n':
- Builder << "\\n"sv;
- break;
- case '\r':
- Builder << "\\r"sv;
- break;
- case '\t':
- Builder << "\\t"sv;
- break;
- default:
- Builder << Char;
- break;
- }
- }
- Value = Value.substr(Escape.size());
- }
- Builder << '\"';
- }
-
- void AppendBase64String(MemoryView Value)
- {
- Builder << '"';
- ZEN_ASSERT(Value.GetSize() <= 512 * 1024 * 1024);
- const uint32_t EncodedSize = Base64::GetEncodedDataSize(uint32_t(Value.GetSize()));
- const size_t EncodedIndex = Builder.AddUninitialized(size_t(EncodedSize));
- Base64::Encode(static_cast<const uint8_t*>(Value.GetData()), uint32_t(Value.GetSize()), Builder.Data() + EncodedIndex);
- }
-
-private:
- StringBuilderBase& Builder;
- ExtendableStringBuilder<32> NewLineAndIndent;
- bool NeedsComma{false};
- bool NeedsNewLine{false};
-};
-
-void
-CompactBinaryToJson(const CbObjectView& Object, StringBuilderBase& Builder)
-{
- CbJsonWriter Writer(Builder);
- Writer.WriteField(Object.AsFieldView());
-}
-
-void
-CompactBinaryToJson(const CbArrayView& Array, StringBuilderBase& Builder)
-{
- CbJsonWriter Writer(Builder);
- Writer.WriteField(Array.AsFieldView());
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-class CbJsonReader
-{
-public:
- static CbFieldIterator Read(std::string_view JsonText, std::string& Error)
- {
- using namespace json11;
-
- const Json Json = Json::parse(std::string(JsonText), Error);
-
- if (Error.empty())
- {
- CbWriter Writer;
- if (ReadField(Writer, Json, std::string_view(), Error))
- {
- return Writer.Save();
- }
- }
-
- return CbFieldIterator();
- }
-
-private:
- static bool ReadField(CbWriter& Writer, const json11::Json& Json, const std::string_view FieldName, std::string& Error)
- {
- using namespace json11;
-
- switch (Json.type())
- {
- case Json::Type::OBJECT:
- {
- if (FieldName.empty())
- {
- Writer.BeginObject();
- }
- else
- {
- Writer.BeginObject(FieldName);
- }
-
- for (const auto& Kv : Json.object_items())
- {
- const std::string& Name = Kv.first;
- const json11::Json& Item = Kv.second;
-
- if (ReadField(Writer, Item, Name, Error) == false)
- {
- return false;
- }
- }
-
- Writer.EndObject();
- }
- break;
- case Json::Type::ARRAY:
- {
- if (FieldName.empty())
- {
- Writer.BeginArray();
- }
- else
- {
- Writer.BeginArray(FieldName);
- }
-
- for (const json11::Json& Item : Json.array_items())
- {
- if (ReadField(Writer, Item, std::string_view(), Error) == false)
- {
- return false;
- }
- }
-
- Writer.EndArray();
- }
- break;
- case Json::Type::NUL:
- {
- if (FieldName.empty())
- {
- Writer.AddNull();
- }
- else
- {
- Writer.AddNull(FieldName);
- }
- }
- break;
- case Json::Type::BOOL:
- {
- if (FieldName.empty())
- {
- Writer.AddBool(Json.bool_value());
- }
- else
- {
- Writer.AddBool(FieldName, Json.bool_value());
- }
- }
- break;
- case Json::Type::NUMBER:
- {
- if (FieldName.empty())
- {
- Writer.AddFloat(Json.number_value());
- }
- else
- {
- Writer.AddFloat(FieldName, Json.number_value());
- }
- }
- break;
- case Json::Type::STRING:
- {
- Oid Id;
- if (TryParseObjectId(Json.string_value(), Id))
- {
- if (FieldName.empty())
- {
- Writer.AddObjectId(Id);
- }
- else
- {
- Writer.AddObjectId(FieldName, Id);
- }
-
- return true;
- }
-
- IoHash Hash;
- if (TryParseIoHash(Json.string_value(), Hash))
- {
- if (FieldName.empty())
- {
- Writer.AddHash(Hash);
- }
- else
- {
- Writer.AddHash(FieldName, Hash);
- }
-
- return true;
- }
-
- if (FieldName.empty())
- {
- Writer.AddString(Json.string_value());
- }
- else
- {
- Writer.AddString(FieldName, Json.string_value());
- }
- }
- break;
- default:
- break;
- }
-
- return true;
- }
-
- static constexpr AsciiSet HexCharSet = AsciiSet("0123456789abcdefABCDEF");
-
- static bool TryParseObjectId(std::string_view Str, Oid& Id)
- {
- using namespace std::literals;
-
- if (Str.size() == Oid::StringLength && AsciiSet::HasOnly(Str, HexCharSet))
- {
- Id = Oid::FromHexString(Str);
- return true;
- }
-
- if (Str.starts_with("0x"sv))
- {
- return TryParseObjectId(Str.substr(2), Id);
- }
-
- return false;
- }
-
- static bool TryParseIoHash(std::string_view Str, IoHash& Hash)
- {
- using namespace std::literals;
-
- if (Str.size() == IoHash::StringLength && AsciiSet::HasOnly(Str, HexCharSet))
- {
- Hash = IoHash::FromHexString(Str);
- return true;
- }
-
- if (Str.starts_with("0x"sv))
- {
- return TryParseIoHash(Str.substr(2), Hash);
- }
-
- return false;
- }
-};
-
-CbFieldIterator
-LoadCompactBinaryFromJson(std::string_view Json, std::string& Error)
-{
- if (Json.empty() == false)
- {
- return CbJsonReader::Read(Json, Error);
- }
-
- return CbFieldIterator();
-}
-
-CbFieldIterator
-LoadCompactBinaryFromJson(std::string_view Json)
-{
- std::string Error;
- return LoadCompactBinaryFromJson(Json, Error);
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-#if ZEN_WITH_TESTS
-void
-uson_forcelink()
-{
-}
-
-TEST_CASE("uson")
-{
- using namespace std::literals;
-
- SUBCASE("CbField")
- {
- constexpr CbFieldView DefaultField;
- static_assert(!DefaultField.HasName(), "Error in HasName()");
- static_assert(!DefaultField.HasValue(), "Error in HasValue()");
- static_assert(!DefaultField.HasError(), "Error in HasError()");
- static_assert(DefaultField.GetError() == CbFieldError::None, "Error in GetError()");
-
- CHECK(DefaultField.GetSize() == 1);
- CHECK(DefaultField.GetName().size() == 0);
- CHECK(DefaultField.HasName() == false);
- CHECK(DefaultField.HasValue() == false);
- CHECK(DefaultField.HasError() == false);
- CHECK(DefaultField.GetError() == CbFieldError::None);
-
- const uint8_t Type = (uint8_t)CbFieldType::None;
- CHECK(DefaultField.GetHash() == IoHash::HashBuffer(&Type, sizeof Type));
-
- CHECK(DefaultField.GetView() == MemoryView{});
- MemoryView SerializedView;
- CHECK(DefaultField.TryGetSerializedView(SerializedView) == false);
- }
-
- SUBCASE("CbField(None)")
- {
- CbFieldView NoneField(nullptr, CbFieldType::None);
- CHECK(NoneField.GetSize() == 1);
- CHECK(NoneField.GetName().size() == 0);
- CHECK(NoneField.HasName() == false);
- CHECK(NoneField.HasValue() == false);
- CHECK(NoneField.HasError() == false);
- CHECK(NoneField.GetError() == CbFieldError::None);
- CHECK(NoneField.GetHash() == CbFieldView().GetHash());
- CHECK(NoneField.GetView() == MemoryView());
- MemoryView SerializedView;
- CHECK(NoneField.TryGetSerializedView(SerializedView) == false);
- }
-
- SUBCASE("CbField(None|Type|Name)")
- {
- constexpr CbFieldType FieldType = CbFieldType::None | CbFieldType::HasFieldName;
- const char NoneBytes[] = {char(FieldType), 4, 'N', 'a', 'm', 'e'};
- CbFieldView NoneField(NoneBytes);
-
- CHECK(NoneField.GetSize() == sizeof(NoneBytes));
- CHECK(NoneField.GetName().compare("Name"sv) == 0);
- CHECK(NoneField.HasName() == true);
- CHECK(NoneField.HasValue() == false);
- CHECK(NoneField.GetHash() == IoHash::HashBuffer(NoneBytes, sizeof NoneBytes));
- CHECK(NoneField.GetView() == MemoryView(NoneBytes, sizeof NoneBytes));
- MemoryView SerializedView;
- CHECK(NoneField.TryGetSerializedView(SerializedView) == true);
- CHECK(SerializedView == MemoryView(NoneBytes, sizeof NoneBytes));
-
- uint8_t CopyBytes[sizeof(NoneBytes)];
- NoneField.CopyTo(MutableMemoryView(CopyBytes, sizeof CopyBytes));
- CHECK(MemoryView(NoneBytes, sizeof NoneBytes).EqualBytes(MemoryView(CopyBytes, sizeof CopyBytes)));
- }
-
- SUBCASE("CbField(None|Type)")
- {
- constexpr CbFieldType FieldType = CbFieldType::None;
- const char NoneBytes[] = {char(FieldType)};
- CbFieldView NoneField(NoneBytes);
-
- CHECK(NoneField.GetSize() == sizeof NoneBytes);
- CHECK(NoneField.GetName().size() == 0);
- CHECK(NoneField.HasName() == false);
- CHECK(NoneField.HasValue() == false);
- CHECK(NoneField.GetHash() == CbFieldView().GetHash());
- CHECK(NoneField.GetView() == MemoryView(NoneBytes, sizeof NoneBytes));
- MemoryView SerializedView;
- CHECK(NoneField.TryGetSerializedView(SerializedView) == true);
- CHECK(SerializedView == MemoryView(NoneBytes, sizeof NoneBytes));
- }
-
- SUBCASE("CbField(None|Name)")
- {
- constexpr CbFieldType FieldType = CbFieldType::None | CbFieldType::HasFieldName;
- const char NoneBytes[] = {char(FieldType), 4, 'N', 'a', 'm', 'e'};
- CbFieldView NoneField(NoneBytes + 1, FieldType);
- CHECK(NoneField.GetSize() == uint64_t(sizeof NoneBytes));
- CHECK(NoneField.GetName().compare("Name") == 0);
- CHECK(NoneField.HasName() == true);
- CHECK(NoneField.HasValue() == false);
- CHECK(NoneField.GetHash() == IoHash::HashBuffer(NoneBytes, sizeof NoneBytes));
- CHECK(NoneField.GetView() == MemoryView(NoneBytes + 1, sizeof NoneBytes - 1));
- MemoryView SerializedView;
- CHECK(NoneField.TryGetSerializedView(SerializedView) == false);
-
- uint8_t CopyBytes[sizeof(NoneBytes)];
- NoneField.CopyTo(MutableMemoryView(CopyBytes, sizeof CopyBytes));
- CHECK(MemoryView(NoneBytes, sizeof NoneBytes).EqualBytes(MemoryView(CopyBytes, sizeof CopyBytes)));
- }
-
- SUBCASE("CbField(None|EmptyName)")
- {
- constexpr CbFieldType FieldType = CbFieldType::None | CbFieldType::HasFieldName;
- const uint8_t NoneBytes[] = {uint8_t(FieldType), 0};
- CbFieldView NoneField(NoneBytes + 1, FieldType);
- CHECK(NoneField.GetSize() == sizeof NoneBytes);
- CHECK(NoneField.GetName().empty() == true);
- CHECK(NoneField.HasName() == true);
- CHECK(NoneField.HasValue() == false);
- CHECK(NoneField.GetHash() == IoHash::HashBuffer(NoneBytes, sizeof NoneBytes));
- CHECK(NoneField.GetView() == MemoryView(NoneBytes + 1, sizeof NoneBytes - 1));
- MemoryView SerializedView;
- CHECK(NoneField.TryGetSerializedView(SerializedView) == false);
- }
-
- static_assert(!std::is_constructible<CbFieldView, const CbObjectView&>::value, "Invalid constructor for CbField");
- static_assert(!std::is_assignable<CbFieldView, const CbObjectView&>::value, "Invalid assignment for CbField");
- static_assert(!std::is_convertible<CbFieldView, CbObjectView>::value, "Invalid conversion to CbObject");
- static_assert(!std::is_assignable<CbObjectView, const CbFieldView&>::value, "Invalid assignment for CbObject");
-
- static_assert(std::is_constructible<CbField>::value, "Missing constructor for CbField");
- static_assert(std::is_constructible<CbField, const CbField&>::value, "Missing constructor for CbField");
- static_assert(std::is_constructible<CbField, CbField&&>::value, "Missing constructor for CbField");
-}
-
-TEST_CASE("uson.null")
-{
- using namespace std::literals;
-
- SUBCASE("CbField(Null)")
- {
- CbFieldView NullField(nullptr, CbFieldType::Null);
- CHECK(NullField.GetSize() == 1);
- CHECK(NullField.IsNull() == true);
- CHECK(NullField.HasValue() == true);
- CHECK(NullField.HasError() == false);
- CHECK(NullField.GetError() == CbFieldError::None);
- const uint8_t Null[]{uint8_t(CbFieldType::Null)};
- CHECK(NullField.GetHash() == IoHash::HashBuffer(Null, sizeof Null));
- }
-
- SUBCASE("CbField(None)")
- {
- CbFieldView Field;
- CHECK(Field.IsNull() == false);
- }
-}
-
-TEST_CASE("uson.json")
-{
- SUBCASE("string")
- {
- CbObjectWriter Writer;
- Writer << "KeyOne"
- << "ValueOne";
- Writer << "KeyTwo"
- << "ValueTwo";
- CbObject Obj = Writer.Save();
-
- StringBuilder<128> Sb;
- const char* JsonText = Obj.ToJson(Sb).Data();
-
- std::string JsonError;
- json11::Json Json = json11::Json::parse(JsonText, JsonError);
-
- const std::string ValueOne = Json["KeyOne"].string_value();
- const std::string ValueTwo = Json["KeyTwo"].string_value();
-
- CHECK(JsonError.empty());
- CHECK(ValueOne == "ValueOne");
- CHECK(ValueTwo == "ValueTwo");
- }
-
- SUBCASE("number")
- {
- const float ExpectedFloatValue = 21.21f;
- const double ExpectedDoubleValue = 42.42;
-
- CbObjectWriter Writer;
- Writer << "Float" << ExpectedFloatValue;
- Writer << "Double" << ExpectedDoubleValue;
-
- CbObject Obj = Writer.Save();
-
- StringBuilder<128> Sb;
- const char* JsonText = Obj.ToJson(Sb).Data();
-
- std::string JsonError;
- json11::Json Json = json11::Json::parse(JsonText, JsonError);
-
- const float FloatValue = float(Json["Float"].number_value());
- const double DoubleValue = Json["Double"].number_value();
-
- CHECK(JsonError.empty());
- CHECK(FloatValue == Approx(ExpectedFloatValue));
- CHECK(DoubleValue == Approx(ExpectedDoubleValue));
- }
-
- SUBCASE("number.nan")
- {
- const float FloatNan = std::numeric_limits<float>::quiet_NaN();
- const double DoubleNan = std::numeric_limits<double>::quiet_NaN();
-
- CbObjectWriter Writer;
- Writer << "FloatNan" << FloatNan;
- Writer << "DoubleNan" << DoubleNan;
-
- CbObject Obj = Writer.Save();
-
- StringBuilder<128> Sb;
- const char* JsonText = Obj.ToJson(Sb).Data();
-
- std::string JsonError;
- json11::Json Json = json11::Json::parse(JsonText, JsonError);
-
- const double FloatValue = Json["FloatNan"].number_value();
- const double DoubleValue = Json["DoubleNan"].number_value();
-
- CHECK(JsonError.empty());
- CHECK(FloatValue == 0);
- CHECK(DoubleValue == 0);
- }
-}
-
-TEST_CASE("uson.datetime")
-{
- using namespace std::literals;
-
- {
- DateTime D1600(1601, 1, 1);
- CHECK_EQ(D1600.GetYear(), 1601);
- CHECK_EQ(D1600.GetMonth(), 1);
- CHECK_EQ(D1600.GetDay(), 1);
- CHECK_EQ(D1600.GetHour(), 0);
- CHECK_EQ(D1600.GetMinute(), 0);
- CHECK_EQ(D1600.GetSecond(), 0);
-
- CHECK_EQ(D1600.ToIso8601(), "1601-01-01T00:00:00.000Z"sv);
- }
-
- {
- DateTime D72(1972, 2, 23, 17, 30, 10);
- CHECK_EQ(D72.GetYear(), 1972);
- CHECK_EQ(D72.GetMonth(), 2);
- CHECK_EQ(D72.GetDay(), 23);
- CHECK_EQ(D72.GetHour(), 17);
- CHECK_EQ(D72.GetMinute(), 30);
- CHECK_EQ(D72.GetSecond(), 10);
- }
-}
-
-TEST_CASE("json.uson")
-{
- using namespace std::literals;
- using namespace json11;
-
- SUBCASE("empty")
- {
- CbFieldIterator It = LoadCompactBinaryFromJson(""sv);
- CHECK(It.HasValue() == false);
- }
-
- SUBCASE("object")
- {
- const Json JsonObject = Json::object{{"Null", nullptr},
- {"String", "Value1"},
- {"Bool", true},
- {"Number", 46.2},
- {"Array", Json::array{1, 2, 3}},
- {"Object",
- Json::object{
- {"String", "Value2"},
- }}};
-
- CbObject Cb = LoadCompactBinaryFromJson(JsonObject.dump()).AsObject();
-
- CHECK(Cb["Null"].IsNull());
- CHECK(Cb["String"].AsString() == "Value1"sv);
- CHECK(Cb["Bool"].AsBool());
- CHECK(Cb["Number"].AsDouble() == 46.2);
- CHECK(Cb["Object"].IsObject());
- CbObjectView Object = Cb["Object"].AsObjectView();
- CHECK(Object["String"].AsString() == "Value2"sv);
- }
-
- SUBCASE("array")
- {
- const Json JsonArray = Json::array{42, 43, 44};
- CbArray Cb = LoadCompactBinaryFromJson(JsonArray.dump()).AsArray();
-
- auto It = Cb.CreateIterator();
- CHECK((*It).AsDouble() == 42);
- It++;
- CHECK((*It).AsDouble() == 43);
- It++;
- CHECK((*It).AsDouble() == 44);
- }
-
- SUBCASE("objectid")
- {
- const Oid& Id = Oid::NewOid();
-
- StringBuilder<64> Sb;
- Id.ToString(Sb);
-
- Json JsonObject = Json::object{{"value", Sb.ToString()}};
- CbObject Cb = LoadCompactBinaryFromJson(JsonObject.dump()).AsObject();
-
- CHECK(Cb["value"sv].IsObjectId());
- CHECK(Cb["value"sv].AsObjectId() == Id);
-
- Sb.Reset();
- Sb << "0x";
- Id.ToString(Sb);
-
- JsonObject = Json::object{{"value", Sb.ToString()}};
- Cb = LoadCompactBinaryFromJson(JsonObject.dump()).AsObject();
-
- CHECK(Cb["value"sv].IsObjectId());
- CHECK(Cb["value"sv].AsObjectId() == Id);
- }
-
- SUBCASE("iohash")
- {
- const uint8_t Data[] = {
- 1,
- 2,
- 3,
- 4,
- 5,
- 6,
- 7,
- 8,
- 9,
- };
-
- const IoHash Hash = IoHash::HashBuffer(Data, sizeof(Data));
-
- Json JsonObject = Json::object{{"value", Hash.ToHexString()}};
- CbObject Cb = LoadCompactBinaryFromJson(JsonObject.dump()).AsObject();
-
- CHECK(Cb["value"sv].IsHash());
- CHECK(Cb["value"sv].AsHash() == Hash);
-
- JsonObject = Json::object{{"value", "0x" + Hash.ToHexString()}};
- Cb = LoadCompactBinaryFromJson(JsonObject.dump()).AsObject();
-
- CHECK(Cb["value"sv].IsHash());
- CHECK(Cb["value"sv].AsHash() == Hash);
- }
-}
-
-#endif
-
-} // namespace zen