diff options
| author | Stefan Boberg <[email protected]> | 2023-05-02 10:01:47 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-05-02 10:01:47 +0200 |
| commit | 075d17f8ada47e990fe94606c3d21df409223465 (patch) | |
| tree | e50549b766a2f3c354798a54ff73404217b4c9af /zencore/compactbinary.cpp | |
| parent | fix: bundle shouldn't append content zip to zen (diff) | |
| download | zen-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.cpp | 2299 |
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 |