aboutsummaryrefslogtreecommitdiff
path: root/src/zencore/compactbinaryyaml.cpp
diff options
context:
space:
mode:
authorzousar <[email protected]>2026-02-26 11:05:07 -0700
committerzousar <[email protected]>2026-02-26 11:05:07 -0700
commitd1e517434899bdb0a98a3d8a3a7764f7aa59518f (patch)
tree59cc5211c506c777b3be90aa8ad980cef31a2e53 /src/zencore/compactbinaryyaml.cpp
parentupdatefrontend (diff)
parentwork around doctest shutdown issues with static CRT (#784) (diff)
downloadzen-d1e517434899bdb0a98a3d8a3a7764f7aa59518f.tar.xz
zen-d1e517434899bdb0a98a3d8a3a7764f7aa59518f.zip
Merge branch 'main' into zs/web-ui-improvements
Diffstat (limited to 'src/zencore/compactbinaryyaml.cpp')
-rw-r--r--src/zencore/compactbinaryyaml.cpp427
1 files changed, 302 insertions, 125 deletions
diff --git a/src/zencore/compactbinaryyaml.cpp b/src/zencore/compactbinaryyaml.cpp
index 5122e952a..b308af418 100644
--- a/src/zencore/compactbinaryyaml.cpp
+++ b/src/zencore/compactbinaryyaml.cpp
@@ -14,11 +14,6 @@
#include <string_view>
#include <vector>
-ZEN_THIRD_PARTY_INCLUDES_START
-#include <ryml.hpp>
-#include <ryml_std.hpp>
-ZEN_THIRD_PARTY_INCLUDES_END
-
namespace zen {
//////////////////////////////////////////////////////////////////////////
@@ -26,193 +21,349 @@ namespace zen {
class CbYamlWriter
{
public:
- explicit CbYamlWriter(StringBuilderBase& InBuilder) : m_StrBuilder(InBuilder) { m_NodeStack.push_back(m_Tree.rootref()); }
+ explicit CbYamlWriter(StringBuilderBase& InBuilder) : m_Builder(InBuilder) {}
void WriteField(CbFieldView Field)
{
- ryml::NodeRef Node;
+ CbValue Accessor = Field.GetValue();
+ CbFieldType Type = Accessor.GetType();
- if (m_IsFirst)
+ switch (Type)
{
- Node = Top();
+ case CbFieldType::Object:
+ case CbFieldType::UniformObject:
+ WriteMapEntries(Field, 0);
+ break;
+ case CbFieldType::Array:
+ case CbFieldType::UniformArray:
+ WriteSeqEntries(Field, 0);
+ break;
+ default:
+ WriteScalarValue(Field);
+ m_Builder << '\n';
+ break;
+ }
+ }
+
+ void WriteMapEntry(CbFieldView Field, int32_t Indent)
+ {
+ WriteIndent(Indent);
+ WriteMapEntryContent(Field, Indent);
+ }
+
+ void WriteSeqEntry(CbFieldView Field, int32_t Indent)
+ {
+ CbValue Accessor = Field.GetValue();
+ CbFieldType Type = Accessor.GetType();
- m_IsFirst = false;
+ if (Type == CbFieldType::Object || Type == CbFieldType::UniformObject)
+ {
+ bool First = true;
+ for (CbFieldView MapChild : Field)
+ {
+ if (First)
+ {
+ WriteIndent(Indent);
+ m_Builder << "- ";
+ First = false;
+ }
+ else
+ {
+ WriteIndent(Indent + 1);
+ }
+ WriteMapEntryContent(MapChild, Indent + 1);
+ }
+ }
+ else if (Type == CbFieldType::Array || Type == CbFieldType::UniformArray)
+ {
+ WriteIndent(Indent);
+ m_Builder << "-\n";
+ WriteSeqEntries(Field, Indent + 1);
}
else
{
- Node = Top().append_child();
+ WriteIndent(Indent);
+ m_Builder << "- ";
+ WriteScalarValue(Field);
+ m_Builder << '\n';
}
+ }
- if (std::u8string_view Name = Field.GetU8Name(); !Name.empty())
+private:
+ void WriteMapEntries(CbFieldView MapField, int32_t Indent)
+ {
+ for (CbFieldView Child : MapField)
{
- Node.set_key_serialized(ryml::csubstr((const char*)Name.data(), Name.size()));
+ WriteIndent(Indent);
+ WriteMapEntryContent(Child, Indent);
}
+ }
+
+ void WriteMapEntryContent(CbFieldView Field, int32_t Indent)
+ {
+ std::u8string_view Name = Field.GetU8Name();
+ m_Builder << std::string_view(reinterpret_cast<const char*>(Name.data()), Name.size());
- switch (CbValue Accessor = Field.GetValue(); Accessor.GetType())
+ CbValue Accessor = Field.GetValue();
+ CbFieldType Type = Accessor.GetType();
+
+ if (IsContainer(Type))
{
- case CbFieldType::Null:
- Node.set_val("null");
- break;
- case CbFieldType::Object:
- case CbFieldType::UniformObject:
- Node |= ryml::MAP;
- m_NodeStack.push_back(Node);
- for (CbFieldView It : Field)
+ m_Builder << ":\n";
+ WriteFieldValue(Field, Indent + 1);
+ }
+ else
+ {
+ m_Builder << ": ";
+ WriteScalarValue(Field);
+ m_Builder << '\n';
+ }
+ }
+
+ void WriteSeqEntries(CbFieldView SeqField, int32_t Indent)
+ {
+ for (CbFieldView Child : SeqField)
+ {
+ CbValue Accessor = Child.GetValue();
+ CbFieldType Type = Accessor.GetType();
+
+ if (Type == CbFieldType::Object || Type == CbFieldType::UniformObject)
+ {
+ bool First = true;
+ for (CbFieldView MapChild : Child)
{
- WriteField(It);
+ if (First)
+ {
+ WriteIndent(Indent);
+ m_Builder << "- ";
+ First = false;
+ }
+ else
+ {
+ WriteIndent(Indent + 1);
+ }
+ WriteMapEntryContent(MapChild, Indent + 1);
}
- m_NodeStack.pop_back();
+ }
+ else if (Type == CbFieldType::Array || Type == CbFieldType::UniformArray)
+ {
+ WriteIndent(Indent);
+ m_Builder << "-\n";
+ WriteSeqEntries(Child, Indent + 1);
+ }
+ else
+ {
+ WriteIndent(Indent);
+ m_Builder << "- ";
+ WriteScalarValue(Child);
+ m_Builder << '\n';
+ }
+ }
+ }
+
+ void WriteFieldValue(CbFieldView Field, int32_t Indent)
+ {
+ CbValue Accessor = Field.GetValue();
+ CbFieldType Type = Accessor.GetType();
+
+ switch (Type)
+ {
+ case CbFieldType::Object:
+ case CbFieldType::UniformObject:
+ WriteMapEntries(Field, Indent);
break;
case CbFieldType::Array:
case CbFieldType::UniformArray:
- Node |= ryml::SEQ;
- m_NodeStack.push_back(Node);
- for (CbFieldView It : Field)
- {
- WriteField(It);
- }
- m_NodeStack.pop_back();
+ WriteSeqEntries(Field, Indent);
break;
- case CbFieldType::Binary:
- {
- ExtendableStringBuilder<256> Builder;
- const MemoryView Value = Accessor.AsBinary();
- 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);
-
- Node.set_key_serialized(Builder.c_str());
- }
+ case CbFieldType::CustomById:
+ WriteCustomById(Field.GetValue().AsCustomById(), Indent);
break;
- case CbFieldType::String:
- {
- const std::u8string_view U8String = Accessor.AsU8String();
- Node.set_val(ryml::csubstr((const char*)U8String.data(), U8String.size()));
- }
+ case CbFieldType::CustomByName:
+ WriteCustomByName(Field.GetValue().AsCustomByName(), Indent);
+ break;
+ default:
+ WriteScalarValue(Field);
+ m_Builder << '\n';
+ break;
+ }
+ }
+
+ void WriteScalarValue(CbFieldView Field)
+ {
+ CbValue Accessor = Field.GetValue();
+ switch (Accessor.GetType())
+ {
+ case CbFieldType::Null:
+ m_Builder << "null";
+ break;
+ case CbFieldType::BoolFalse:
+ m_Builder << "false";
+ break;
+ case CbFieldType::BoolTrue:
+ m_Builder << "true";
break;
case CbFieldType::IntegerPositive:
- Node << Accessor.AsIntegerPositive();
+ m_Builder << Accessor.AsIntegerPositive();
break;
case CbFieldType::IntegerNegative:
- Node << Accessor.AsIntegerNegative();
+ m_Builder << Accessor.AsIntegerNegative();
break;
case CbFieldType::Float32:
if (const float Value = Accessor.AsFloat32(); std::isfinite(Value))
- {
- Node << Value;
- }
+ m_Builder.Append(fmt::format("{}", Value));
else
- {
- Node << "null";
- }
+ m_Builder << "null";
break;
case CbFieldType::Float64:
if (const double Value = Accessor.AsFloat64(); std::isfinite(Value))
- {
- Node << Value;
- }
+ m_Builder.Append(fmt::format("{}", Value));
else
+ m_Builder << "null";
+ break;
+ case CbFieldType::String:
{
- Node << "null";
+ const std::u8string_view U8String = Accessor.AsU8String();
+ WriteString(std::string_view(reinterpret_cast<const char*>(U8String.data()), U8String.size()));
}
break;
- case CbFieldType::BoolFalse:
- Node << "false";
- break;
- case CbFieldType::BoolTrue:
- Node << "true";
+ case CbFieldType::Hash:
+ WriteString(Accessor.AsHash().ToHexString());
break;
case CbFieldType::ObjectAttachment:
case CbFieldType::BinaryAttachment:
- Node << Accessor.AsAttachment().ToHexString();
- break;
- case CbFieldType::Hash:
- Node << Accessor.AsHash().ToHexString();
+ WriteString(Accessor.AsAttachment().ToHexString());
break;
case CbFieldType::Uuid:
- Node << fmt::format("{}", Accessor.AsUuid());
+ WriteString(fmt::format("{}", Accessor.AsUuid()));
break;
case CbFieldType::DateTime:
- Node << DateTime(Accessor.AsDateTimeTicks()).ToIso8601();
+ WriteString(DateTime(Accessor.AsDateTimeTicks()).ToIso8601());
break;
case CbFieldType::TimeSpan:
if (const TimeSpan Span(Accessor.AsTimeSpanTicks()); Span.GetDays() == 0)
- {
- Node << Span.ToString("%h:%m:%s.%n");
- }
+ WriteString(Span.ToString("%h:%m:%s.%n"));
else
- {
- Node << Span.ToString("%d.%h:%m:%s.%n");
- }
+ WriteString(Span.ToString("%d.%h:%m:%s.%n"));
break;
case CbFieldType::ObjectId:
- Node << fmt::format("{}", Accessor.AsObjectId());
+ WriteString(fmt::format("{}", Accessor.AsObjectId()));
break;
- case CbFieldType::CustomById:
- {
- CbCustomById Custom = Accessor.AsCustomById();
+ case CbFieldType::Binary:
+ WriteBase64(Accessor.AsBinary());
+ break;
+ default:
+ ZEN_ASSERT_FORMAT(false, "invalid field type: {}", uint8_t(Accessor.GetType()));
+ break;
+ }
+ }
- Node |= ryml::MAP;
+ void WriteCustomById(CbCustomById Custom, int32_t Indent)
+ {
+ WriteIndent(Indent);
+ m_Builder << "Id: ";
+ m_Builder.Append(fmt::format("{}", Custom.Id));
+ m_Builder << '\n';
+
+ WriteIndent(Indent);
+ m_Builder << "Data: ";
+ WriteBase64(Custom.Data);
+ m_Builder << '\n';
+ }
- ryml::NodeRef IdNode = Node.append_child();
- IdNode.set_key("Id");
- IdNode.set_val_serialized(fmt::format("{}", Custom.Id));
+ void WriteCustomByName(CbCustomByName Custom, int32_t Indent)
+ {
+ WriteIndent(Indent);
+ m_Builder << "Name: ";
+ WriteString(std::string_view(reinterpret_cast<const char*>(Custom.Name.data()), Custom.Name.size()));
+ m_Builder << '\n';
+
+ WriteIndent(Indent);
+ m_Builder << "Data: ";
+ WriteBase64(Custom.Data);
+ m_Builder << '\n';
+ }
- ryml::NodeRef DataNode = Node.append_child();
- DataNode.set_key("Data");
+ void WriteBase64(MemoryView Value)
+ {
+ ZEN_ASSERT(Value.GetSize() <= 512 * 1024 * 1024);
+ ExtendableStringBuilder<256> Buf;
+ const uint32_t EncodedSize = Base64::GetEncodedDataSize(uint32_t(Value.GetSize()));
+ const size_t EncodedIndex = Buf.AddUninitialized(size_t(EncodedSize));
+ Base64::Encode(static_cast<const uint8_t*>(Value.GetData()), uint32_t(Value.GetSize()), Buf.Data() + EncodedIndex);
+ WriteString(Buf.ToView());
+ }
- ExtendableStringBuilder<256> Builder;
- const MemoryView& Value = Custom.Data;
- 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);
+ void WriteString(std::string_view Str)
+ {
+ if (NeedsQuoting(Str))
+ {
+ m_Builder << '\'';
+ for (char C : Str)
+ {
+ if (C == '\'')
+ m_Builder << "''";
+ else
+ m_Builder << C;
+ }
+ m_Builder << '\'';
+ }
+ else
+ {
+ m_Builder << Str;
+ }
+ }
- DataNode.set_val_serialized(Builder.c_str());
- }
- break;
- case CbFieldType::CustomByName:
- {
- CbCustomByName Custom = Accessor.AsCustomByName();
+ void WriteIndent(int32_t Indent)
+ {
+ for (int32_t I = 0; I < Indent; ++I)
+ m_Builder << " ";
+ }
- Node |= ryml::MAP;
+ static bool NeedsQuoting(std::string_view Str)
+ {
+ if (Str.empty())
+ return false;
- ryml::NodeRef NameNode = Node.append_child();
- NameNode.set_key("Name");
- std::string_view Name = std::string_view((const char*)Custom.Name.data(), Custom.Name.size());
- NameNode.set_val_serialized(std::string(Name));
+ char First = Str[0];
+ if (First == ' ' || First == '\n' || First == '\t' || First == '\r' || First == '*' || First == '&' || First == '%' ||
+ First == '@' || First == '`')
+ return true;
- ryml::NodeRef DataNode = Node.append_child();
- DataNode.set_key("Data");
+ if (Str.size() >= 2 && Str[0] == '<' && Str[1] == '<')
+ return true;
- ExtendableStringBuilder<256> Builder;
- const MemoryView& Value = Custom.Data;
- 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);
+ char Last = Str.back();
+ if (Last == ' ' || Last == '\n' || Last == '\t' || Last == '\r')
+ return true;
- DataNode.set_val_serialized(Builder.c_str());
- }
- break;
- default:
- ZEN_ASSERT_FORMAT(false, "invalid field type: {}", uint8_t(Accessor.GetType()));
- break;
+ for (char C : Str)
+ {
+ if (C == '#' || C == ':' || C == '-' || C == '?' || C == ',' || C == '\n' || C == '{' || C == '}' || C == '[' || C == ']' ||
+ C == '\'' || C == '"')
+ return true;
}
- if (m_NodeStack.size() == 1)
+ return false;
+ }
+
+ static bool IsContainer(CbFieldType Type)
+ {
+ switch (Type)
{
- std::string Yaml = ryml::emitrs_yaml<std::string>(m_Tree);
- m_StrBuilder << Yaml;
+ case CbFieldType::Object:
+ case CbFieldType::UniformObject:
+ case CbFieldType::Array:
+ case CbFieldType::UniformArray:
+ case CbFieldType::CustomById:
+ case CbFieldType::CustomByName:
+ return true;
+ default:
+ return false;
}
}
-private:
- StringBuilderBase& m_StrBuilder;
- bool m_IsFirst = true;
-
- ryml::Tree m_Tree;
- std::vector<ryml::NodeRef> m_NodeStack;
- ryml::NodeRef& Top() { return m_NodeStack.back(); }
+ StringBuilderBase& m_Builder;
};
void
@@ -229,6 +380,32 @@ CompactBinaryToYaml(const CbArrayView& Array, StringBuilderBase& Builder)
Writer.WriteField(Array.AsFieldView());
}
+void
+CompactBinaryToYaml(MemoryView Data, StringBuilderBase& InBuilder)
+{
+ std::vector<CbFieldView> Fields = ReadCompactBinaryStream(Data);
+ if (Fields.empty())
+ return;
+
+ CbYamlWriter Writer(InBuilder);
+ if (Fields.size() == 1)
+ {
+ Writer.WriteField(Fields[0]);
+ return;
+ }
+
+ if (Fields[0].HasName())
+ {
+ for (const CbFieldView& Field : Fields)
+ Writer.WriteMapEntry(Field, 0);
+ }
+ else
+ {
+ for (const CbFieldView& Field : Fields)
+ Writer.WriteSeqEntry(Field, 0);
+ }
+}
+
#if ZEN_WITH_TESTS
void
cbyaml_forcelink()