diff options
author | Devtools Arcadia <arcadia-devtools@yandex-team.ru> | 2022-02-07 18:08:42 +0300 |
---|---|---|
committer | Devtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net> | 2022-02-07 18:08:42 +0300 |
commit | 1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch) | |
tree | e26c9fed0de5d9873cce7e00bc214573dc2195b7 /library/cpp/yson/node | |
download | ydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz |
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'library/cpp/yson/node')
-rw-r--r-- | library/cpp/yson/node/node.cpp | 915 | ||||
-rw-r--r-- | library/cpp/yson/node/node.h | 523 | ||||
-rw-r--r-- | library/cpp/yson/node/node_builder.cpp | 96 | ||||
-rw-r--r-- | library/cpp/yson/node/node_builder.h | 46 | ||||
-rw-r--r-- | library/cpp/yson/node/node_io.cpp | 154 | ||||
-rw-r--r-- | library/cpp/yson/node/node_io.h | 40 | ||||
-rw-r--r-- | library/cpp/yson/node/node_ut.cpp | 484 | ||||
-rw-r--r-- | library/cpp/yson/node/node_visitor.cpp | 152 | ||||
-rw-r--r-- | library/cpp/yson/node/node_visitor.h | 37 | ||||
-rw-r--r-- | library/cpp/yson/node/pybind/node.cpp | 105 | ||||
-rw-r--r-- | library/cpp/yson/node/pybind/node.h | 9 | ||||
-rw-r--r-- | library/cpp/yson/node/pybind/ya.make | 16 | ||||
-rw-r--r-- | library/cpp/yson/node/serialize.cpp | 101 | ||||
-rw-r--r-- | library/cpp/yson/node/serialize.h | 45 | ||||
-rw-r--r-- | library/cpp/yson/node/ut/ya.make | 12 | ||||
-rw-r--r-- | library/cpp/yson/node/ya.make | 25 |
16 files changed, 2760 insertions, 0 deletions
diff --git a/library/cpp/yson/node/node.cpp b/library/cpp/yson/node/node.cpp new file mode 100644 index 0000000000..b39e070718 --- /dev/null +++ b/library/cpp/yson/node/node.cpp @@ -0,0 +1,915 @@ +#include "node.h" + +#include "node_io.h" + +#include <library/cpp/yson/writer.h> + +#include <util/generic/overloaded.h> + +namespace NYT { + +//////////////////////////////////////////////////////////////////////////////// + +bool TNode::TNull::operator==(const TNull&) const { + return true; +} + +//////////////////////////////////////////////////////////////////////////////// + +bool TNode::TUndefined::operator==(const TUndefined&) const { + return true; +} + +//////////////////////////////////////////////////////////////////////////////// + +namespace NNodeCmp { + +bool IsComparableType(const TNode::EType type) { + switch (type) { + case TNode::String: + case TNode::Int64: + case TNode::Uint64: + case TNode::Double: + case TNode::Bool: + case TNode::Null: + case TNode::Undefined: + return true; + default: + return false; + } +} + +bool operator<(const TNode& lhs, const TNode& rhs) +{ + if (!lhs.GetAttributes().Empty() || !rhs.GetAttributes().Empty()) { + ythrow TNode::TTypeError() << "Unsupported attributes comparison"; + } + + if (!IsComparableType(lhs.GetType()) || !IsComparableType(rhs.GetType())) { + ythrow TNode::TTypeError() << "Unsupported types for comparison: " << lhs.GetType() << " with " << rhs.GetType(); + } + + if (lhs.GetType() != rhs.GetType()) { + return lhs.GetType() < rhs.GetType(); + } + + switch (lhs.GetType()) { + case TNode::String: + return lhs.AsString() < rhs.AsString(); + case TNode::Int64: + return lhs.AsInt64() < rhs.AsInt64(); + case TNode::Uint64: + return lhs.AsUint64() < rhs.AsUint64(); + case TNode::Double: + return lhs.AsDouble() < rhs.AsDouble(); + case TNode::Bool: + return lhs.AsBool() < rhs.AsBool(); + case TNode::Null: + case TNode::Undefined: + return false; + default: + Y_FAIL("Unexpected type: %d", lhs.GetType()); + } +} + +bool operator>(const TNode& lhs, const TNode& rhs) +{ + return rhs < lhs; +} + +bool operator<=(const TNode& lhs, const TNode& rhs) +{ + return !(lhs > rhs); +} + +bool operator>=(const TNode& lhs, const TNode& rhs) +{ + return !(lhs < rhs); +} + +} // namespace NNodeCmp + +//////////////////////////////////////////////////////////////////////////////// + +TNode::TNode() + : Value_(TUndefined{}) +{ } + +TNode::TNode(const char* s) + : Value_(TString(s)) +{ } + +TNode::TNode(TStringBuf s) + : Value_(TString(s)) +{ } + +TNode::TNode(std::string_view s) + : Value_(TString(s)) +{ } + +TNode::TNode(const std::string& s) + : Value_(TString(s)) +{ } + +TNode::TNode(TString s) + : Value_(std::move(s)) +{ } + +TNode::TNode(int i) + : Value_(static_cast<i64>(i)) +{ } + + +TNode::TNode(unsigned int ui) + : Value_(static_cast<ui64>(ui)) +{ } + +TNode::TNode(long i) + : Value_(static_cast<i64>(i)) +{ } + +TNode::TNode(unsigned long ui) + : Value_(static_cast<ui64>(ui)) +{ } + +TNode::TNode(long long i) + : Value_(static_cast<i64>(i)) +{ } + +TNode::TNode(unsigned long long ui) + : Value_(static_cast<ui64>(ui)) +{ } + +TNode::TNode(double d) + : Value_(d) +{ } + +TNode::TNode(bool b) + : Value_(b) +{ } + +TNode::TNode(TMapType map) + : Value_(std::move(map)) +{ } + +TNode::TNode(const TNode& rhs) + : TNode() +{ + if (rhs.Attributes_) { + CreateAttributes(); + *Attributes_ = *rhs.Attributes_; + } + Value_ = rhs.Value_; +} + +TNode& TNode::operator=(const TNode& rhs) +{ + if (this != &rhs) { + TNode tmp = rhs; + Move(std::move(tmp)); + } + return *this; +} + +TNode::TNode(TNode&& rhs) noexcept + : TNode() +{ + Move(std::move(rhs)); +} + +TNode& TNode::operator=(TNode&& rhs) noexcept +{ + if (this != &rhs) { + TNode tmp = std::move(rhs); + Move(std::move(tmp)); + } + return *this; +} + +TNode::~TNode() = default; + +void TNode::Clear() +{ + ClearAttributes(); + Value_ = TUndefined(); +} + +bool TNode::IsString() const +{ + return std::holds_alternative<TString>(Value_); +} + +bool TNode::IsInt64() const +{ + return std::holds_alternative<i64>(Value_); +} + +bool TNode::IsUint64() const +{ + return std::holds_alternative<ui64>(Value_); +} + +bool TNode::IsDouble() const +{ + return std::holds_alternative<double>(Value_); +} + +bool TNode::IsBool() const +{ + return std::holds_alternative<bool>(Value_); +} + +bool TNode::IsList() const +{ + return std::holds_alternative<TListType>(Value_); +} + +bool TNode::IsMap() const +{ + return std::holds_alternative<TMapType>(Value_); +} + +bool TNode::IsEntity() const +{ + return IsNull(); +} + +bool TNode::IsNull() const +{ + return std::holds_alternative<TNull>(Value_); +} + +bool TNode::IsUndefined() const +{ + return std::holds_alternative<TUndefined>(Value_); +} + +bool TNode::HasValue() const +{ + return !IsNull() && !IsUndefined(); +} + +bool TNode::Empty() const +{ + switch (GetType()) { + case String: + return std::get<TString>(Value_).empty(); + case List: + return std::get<TListType>(Value_).empty(); + case Map: + return std::get<TMapType>(Value_).empty(); + default: + ythrow TTypeError() << "Empty() called for type " << GetType(); + } +} + +size_t TNode::Size() const +{ + switch (GetType()) { + case String: + return std::get<TString>(Value_).size(); + case List: + return std::get<TListType>(Value_).size(); + case Map: + return std::get<TMapType>(Value_).size(); + default: + ythrow TTypeError() << "Size() called for type " << GetType(); + } +} + +TNode::EType TNode::GetType() const +{ + return std::visit(TOverloaded{ + [](const TUndefined&) { return Undefined; }, + [](const TString&) { return String; }, + [](i64) { return Int64; }, + [](ui64) { return Uint64; }, + [](double) { return Double; }, + [](bool) { return Bool; }, + [](const TListType&) { return List; }, + [](const TMapType&) { return Map; }, + [](const TNull&) { return Null; } + }, Value_); +} + +const TString& TNode::AsString() const +{ + CheckType(String); + return std::get<TString>(Value_); +} + +i64 TNode::AsInt64() const +{ + CheckType(Int64); + return std::get<i64>(Value_); +} + +ui64 TNode::AsUint64() const +{ + CheckType(Uint64); + return std::get<ui64>(Value_); +} + +double TNode::AsDouble() const +{ + CheckType(Double); + return std::get<double>(Value_); +} + +bool TNode::AsBool() const +{ + CheckType(Bool); + return std::get<bool>(Value_); +} + +const TNode::TListType& TNode::AsList() const +{ + CheckType(List); + return std::get<TListType>(Value_); +} + +const TNode::TMapType& TNode::AsMap() const +{ + CheckType(Map); + return std::get<TMapType>(Value_); +} + +TNode::TListType& TNode::AsList() +{ + CheckType(List); + return std::get<TListType>(Value_); +} + +TNode::TMapType& TNode::AsMap() +{ + CheckType(Map); + return std::get<TMapType>(Value_); +} + +const TString& TNode::UncheckedAsString() const noexcept +{ + return std::get<TString>(Value_); +} + +i64 TNode::UncheckedAsInt64() const noexcept +{ + return std::get<i64>(Value_); +} + +ui64 TNode::UncheckedAsUint64() const noexcept +{ + return std::get<ui64>(Value_); +} + +double TNode::UncheckedAsDouble() const noexcept +{ + return std::get<double>(Value_); +} + +bool TNode::UncheckedAsBool() const noexcept +{ + return std::get<bool>(Value_); +} + +const TNode::TListType& TNode::UncheckedAsList() const noexcept +{ + return std::get<TListType>(Value_); +} + +const TNode::TMapType& TNode::UncheckedAsMap() const noexcept +{ + return std::get<TMapType>(Value_); +} + +TNode::TListType& TNode::UncheckedAsList() noexcept +{ + return std::get<TListType>(Value_); +} + +TNode::TMapType& TNode::UncheckedAsMap() noexcept +{ + return std::get<TMapType>(Value_); +} + +TNode TNode::CreateList() +{ + TNode node; + node.Value_ = TListType{}; + return node; +} + +TNode TNode::CreateList(TListType list) +{ + TNode node; + node.Value_ = std::move(list); + return node; +} + +TNode TNode::CreateMap() +{ + TNode node; + node.Value_ = TMapType{}; + return node; +} + +TNode TNode::CreateMap(TMapType map) +{ + TNode node; + node.Value_ = std::move(map); + return node; +} + +TNode TNode::CreateEntity() +{ + TNode node; + node.Value_ = TNull{}; + return node; +} + +const TNode& TNode::operator[](size_t index) const +{ + CheckType(List); + return std::get<TListType>(Value_)[index]; +} + +TNode& TNode::operator[](size_t index) +{ + CheckType(List); + return std::get<TListType>(Value_)[index]; +} + +const TNode& TNode::At(size_t index) const { + CheckType(List); + const auto& list = std::get<TListType>(Value_); + if (index >= list.size()) { + ythrow TLookupError() << "List out-of-range: requested index=" << index << ", but size=" << list.size(); + } + return list[index]; +} + +TNode& TNode::At(size_t index) { + CheckType(List); + auto& list = std::get<TListType>(Value_); + if (index >= list.size()) { + ythrow TLookupError() << "List out-of-range: requested index=" << index << ", but size=" << list.size(); + } + return list[index]; +} + +TNode& TNode::Add() & +{ + AssureList(); + return std::get<TListType>(Value_).emplace_back(); +} + +TNode TNode::Add() && +{ + return std::move(Add()); +} + +TNode& TNode::Add(const TNode& node) & +{ + AssureList(); + std::get<TListType>(Value_).emplace_back(node); + return *this; +} + +TNode TNode::Add(const TNode& node) && +{ + return std::move(Add(node)); +} + +TNode& TNode::Add(TNode&& node) & +{ + AssureList(); + std::get<TListType>(Value_).emplace_back(std::move(node)); + return *this; +} + +TNode TNode::Add(TNode&& node) && +{ + return std::move(Add(std::move(node))); +} + +bool TNode::HasKey(const TStringBuf key) const +{ + CheckType(Map); + return std::get<TMapType>(Value_).contains(key); +} + +TNode& TNode::operator()(const TString& key, const TNode& value) & +{ + AssureMap(); + std::get<TMapType>(Value_)[key] = value; + return *this; +} + +TNode TNode::operator()(const TString& key, const TNode& value) && +{ + return std::move(operator()(key, value)); +} + +TNode& TNode::operator()(const TString& key, TNode&& value) & +{ + AssureMap(); + std::get<TMapType>(Value_)[key] = std::move(value); + return *this; +} + +TNode TNode::operator()(const TString& key, TNode&& value) && +{ + return std::move(operator()(key, std::move(value))); +} + +const TNode& TNode::operator[](const TStringBuf key) const +{ + CheckType(Map); + static TNode notFound; + const auto& map = std::get<TMapType>(Value_); + TMapType::const_iterator i = map.find(key); + if (i == map.end()) { + return notFound; + } else { + return i->second; + } +} + +TNode& TNode::operator[](const TStringBuf key) +{ + AssureMap(); + return std::get<TMapType>(Value_)[key]; +} + +const TNode& TNode::At(const TStringBuf key) const { + CheckType(Map); + const auto& map = std::get<TMapType>(Value_); + TMapType::const_iterator i = map.find(key); + if (i == map.end()) { + ythrow TLookupError() << "Cannot find key " << key; + } else { + return i->second; + } +} + +TNode& TNode::At(const TStringBuf key) { + CheckType(Map); + auto& map = std::get<TMapType>(Value_); + TMapType::iterator i = map.find(key); + if (i == map.end()) { + ythrow TLookupError() << "Cannot find key " << key; + } else { + return i->second; + } +} + +const TString& TNode::ChildAsString(const TStringBuf key) const { + const auto& node = At(key); + try { + return node.AsString(); + } catch (TTypeError& e) { + e << ", during getting key=" << key; + throw e; + } catch (...) { + ythrow TTypeError() << CurrentExceptionMessage() << ", during getting key=" << key; + } +} + +i64 TNode::ChildAsInt64(const TStringBuf key) const { + const auto& node = At(key); + try { + return node.AsInt64(); + } catch (TTypeError& e) { + e << ", during getting key=" << key; + throw e; + } catch (...) { + ythrow TTypeError() << CurrentExceptionMessage() << ", during getting key=" << key; + } +} + +ui64 TNode::ChildAsUint64(const TStringBuf key) const { + const auto& node = At(key); + try { + return node.AsUint64(); + } catch (TTypeError& e) { + e << ", during getting key=" << key; + throw e; + } catch (...) { + ythrow TTypeError() << CurrentExceptionMessage() << ", during getting key=" << key; + } +} + +double TNode::ChildAsDouble(const TStringBuf key) const { + const auto& node = At(key); + try { + return node.AsDouble(); + } catch (TTypeError& e) { + e << ", during getting key=" << key; + throw e; + } catch (...) { + ythrow TTypeError() << CurrentExceptionMessage() << ", during getting key=" << key; + } +} + +bool TNode::ChildAsBool(const TStringBuf key) const { + const auto& node = At(key); + try { + return node.AsBool(); + } catch (TTypeError& e) { + e << ", during getting key=" << key; + throw e; + } catch (...) { + ythrow TTypeError() << CurrentExceptionMessage() << ", during getting key=" << key; + } +} + +const TNode::TListType& TNode::ChildAsList(const TStringBuf key) const { + const auto& node = At(key); + try { + return node.AsList(); + } catch (TTypeError& e) { + e << ", during getting key=" << key; + throw e; + } catch (...) { + ythrow TTypeError() << CurrentExceptionMessage() << ", during getting key=" << key; + } +} + +const TNode::TMapType& TNode::ChildAsMap(const TStringBuf key) const { + const auto& node = At(key); + try { + return node.AsMap(); + } catch (TTypeError& e) { + e << ", during getting key=" << key; + throw e; + } catch (...) { + ythrow TTypeError() << CurrentExceptionMessage() << ", during getting key=" << key; + } +} + +TNode::TListType& TNode::ChildAsList(const TStringBuf key) { + auto& node = At(key); + try { + return node.AsList(); + } catch (TTypeError& e) { + e << ", during getting key=" << key; + throw e; + } catch (...) { + ythrow TTypeError() << CurrentExceptionMessage() << ", during getting key=" << key; + } +} + +TNode::TMapType& TNode::ChildAsMap(const TStringBuf key) { + auto& node = At(key); + try { + return node.AsMap(); + } catch (TTypeError& e) { + e << ", during getting key=" << key; + throw e; + } catch (...) { + ythrow TTypeError() << CurrentExceptionMessage() << ", during getting key=" << key; + } +} + +const TString& TNode::ChildAsString(size_t index) const { + const auto& node = At(index); + try { + return node.AsString(); + } catch (TTypeError& e) { + e << ", during getting index=" << index; + throw e; + } catch (...) { + ythrow TTypeError() << CurrentExceptionMessage() << ", during getting index=" << index; + } +} + +i64 TNode::ChildAsInt64(size_t index) const { + const auto& node = At(index); + try { + return node.AsInt64(); + } catch (TTypeError& e) { + e << ", during getting index=" << index; + throw e; + } catch (...) { + ythrow TTypeError() << CurrentExceptionMessage() << ", during getting index=" << index; + } +} + +ui64 TNode::ChildAsUint64(size_t index) const { + const auto& node = At(index); + try { + return node.AsUint64(); + } catch (TTypeError& e) { + e << ", during getting index=" << index; + throw e; + } catch (...) { + ythrow TTypeError() << CurrentExceptionMessage() << ", during getting index=" << index; + } +} + +double TNode::ChildAsDouble(size_t index) const { + const auto& node = At(index); + try { + return node.AsDouble(); + } catch (TTypeError& e) { + e << ", during getting index=" << index; + throw e; + } catch (...) { + ythrow TTypeError() << CurrentExceptionMessage() << ", during getting index=" << index; + } +} + +bool TNode::ChildAsBool(size_t index) const { + const auto& node = At(index); + try { + return node.AsBool(); + } catch (TTypeError& e) { + e << ", during getting index=" << index; + throw e; + } catch (...) { + ythrow TTypeError() << CurrentExceptionMessage() << ", during getting index=" << index; + } +} + +const TNode::TListType& TNode::ChildAsList(size_t index) const { + const auto& node = At(index); + try { + return node.AsList(); + } catch (TTypeError& e) { + e << ", during getting index=" << index; + throw e; + } catch (...) { + ythrow TTypeError() << CurrentExceptionMessage() << ", during getting index=" << index; + } +} + +const TNode::TMapType& TNode::ChildAsMap(size_t index) const { + const auto& node = At(index); + try { + return node.AsMap(); + } catch (TTypeError& e) { + e << ", during getting index=" << index; + throw e; + } catch (...) { + ythrow TTypeError() << CurrentExceptionMessage() << ", during getting index=" << index; + } +} + +TNode::TListType& TNode::ChildAsList(size_t index) { + auto& node = At(index); + try { + return node.AsList(); + } catch (TTypeError& e) { + e << ", during getting index=" << index; + throw e; + } catch (...) { + ythrow TTypeError() << CurrentExceptionMessage() << ", during getting index=" << index; + } +} + +TNode::TMapType& TNode::ChildAsMap(size_t index) { + auto& node = At(index); + try { + return node.AsMap(); + } catch (TTypeError& e) { + e << ", during getting index=" << index; + throw e; + } catch (...) { + ythrow TTypeError() << CurrentExceptionMessage() << ", during getting index=" << index; + } +} + +bool TNode::HasAttributes() const +{ + return Attributes_ && !Attributes_->Empty(); +} + +void TNode::ClearAttributes() +{ + if (Attributes_) { + Attributes_.Destroy(); + } +} + +const TNode& TNode::GetAttributes() const +{ + static TNode notFound = TNode::CreateMap(); + if (!Attributes_) { + return notFound; + } + return *Attributes_; +} + +TNode& TNode::Attributes() +{ + if (!Attributes_) { + CreateAttributes(); + } + return *Attributes_; +} + +void TNode::MoveWithoutAttributes(TNode&& rhs) +{ + Value_ = std::move(rhs.Value_); + rhs.Clear(); +} + +void TNode::Move(TNode&& rhs) +{ + Value_ = std::move(rhs.Value_); + Attributes_ = std::move(rhs.Attributes_); +} + +void TNode::CheckType(EType type) const +{ + Y_ENSURE_EX(GetType() == type, + TTypeError() << "TNode type " << type << " expected, actual type " << GetType(); + ); +} + +void TNode::AssureMap() +{ + if (std::holds_alternative<TUndefined>(Value_)) { + Value_ = TMapType(); + } else { + CheckType(Map); + } +} + +void TNode::AssureList() +{ + if (std::holds_alternative<TUndefined>(Value_)) { + Value_ = TListType(); + } else { + CheckType(List); + } +} + +void TNode::CreateAttributes() +{ + Attributes_ = MakeHolder<TNode>(); + Attributes_->Value_ = TMapType(); +} + +void TNode::Save(IOutputStream* out) const +{ + NodeToYsonStream(*this, out, NYson::EYsonFormat::Binary); +} + +void TNode::Load(IInputStream* in) +{ + Clear(); + *this = NodeFromYsonStream(in, ::NYson::EYsonType::Node); +} + +//////////////////////////////////////////////////////////////////////////////// + +bool operator==(const TNode& lhs, const TNode& rhs) +{ + if (std::holds_alternative<TNode::TUndefined>(lhs.Value_) || + std::holds_alternative<TNode::TUndefined>(rhs.Value_)) + { + // TODO: should try to remove this behaviour if nobody uses it. + return false; + } + + if (lhs.GetType() != rhs.GetType()) { + return false; + } + + if (lhs.Attributes_) { + if (rhs.Attributes_) { + if (*lhs.Attributes_ != *rhs.Attributes_) { + return false; + } + } else { + return false; + } + } else { + if (rhs.Attributes_) { + return false; + } + } + + return rhs.Value_ == lhs.Value_; +} + +bool operator!=(const TNode& lhs, const TNode& rhs) +{ + return !(lhs == rhs); +} + +bool GetBool(const TNode& node) +{ + if (node.IsBool()) { + return node.AsBool(); + } else if (node.IsString()) { + return node.AsString() == "true"; + } else { + ythrow TNode::TTypeError() + << "GetBool(): not a boolean or string type"; + } +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT diff --git a/library/cpp/yson/node/node.h b/library/cpp/yson/node/node.h new file mode 100644 index 0000000000..5f90f95df0 --- /dev/null +++ b/library/cpp/yson/node/node.h @@ -0,0 +1,523 @@ +#pragma once + +#include <util/generic/bt_exception.h> +#include <util/generic/cast.h> +#include <util/generic/hash.h> +#include <util/generic/variant.h> +#include <util/generic/vector.h> +#include <util/generic/yexception.h> +#include <util/generic/ylimits.h> +#include <util/string/cast.h> + +#include <cmath> +#include <variant> + +class IInputStream; +class IOutputStream; + +namespace NYT { + +//////////////////////////////////////////////////////////////////////////////// + +class TNode +{ +public: + class TLookupError + : public TWithBackTrace<yexception> + { }; + + class TTypeError + : public TWithBackTrace<yexception> + { }; + + enum EType { + Undefined = 0 /*"undefined"*/, + + // NOTE: string representation of all node types + // are compatible with server node type (except `Undefined' which is missing on server). + String = 1 /*"string_node"*/, + Int64 = 2 /*"int64_node"*/, + Uint64 = 3 /*"uint64_node"*/, + Double = 4 /*"double_node"*/, + Bool = 5 /*"boolean_node"*/, + List = 6 /*"list_node"*/, + Map = 7 /*"map_node"*/, + Null = 8 /*"null"*/, + }; + + using TListType = TVector<TNode>; + using TMapType = THashMap<TString, TNode>; + +private: + struct TNull { + bool operator==(const TNull&) const; + }; + + struct TUndefined { + bool operator==(const TUndefined&) const; + }; + + using TValue = std::variant< + bool, + i64, + ui64, + double, + TString, + TListType, + TMapType, + TNull, + TUndefined + >; + +public: + + TNode(); + TNode(const char* s); + TNode(TStringBuf s); + explicit TNode(std::string_view s); + explicit TNode(const std::string& s); + TNode(TString s); + TNode(int i); + + //this case made speccially for prevent mess cast of EType into TNode through TNode(int) constructor + //usual case of error SomeNode == TNode::Undefined <-- SomeNode indeed will be compared with TNode(0) without this method + //correct way is SomeNode.GetType() == TNode::Undefined + template<class T = EType> + Y_FORCE_INLINE TNode(EType) + { + static_assert(!std::is_same<T, EType>::value, "looks like a mistake, may be you forget .GetType()"); + } + + //this case made speccially for prevent mess cast of T* into TNode through implicit bool ctr + template<class T = int> + Y_FORCE_INLINE TNode(const T*) : TNode() { + static_assert(!std::is_same<T,T>::value, "looks like a mistake, and pointer have converted to bool"); + } + + TNode(unsigned int ui); + TNode(long i); + TNode(unsigned long ui); + TNode(long long i); + TNode(unsigned long long ui); + TNode(double d); + TNode(bool b); + TNode(TMapType map); + + TNode(const TNode& rhs); + TNode& operator=(const TNode& rhs); + + TNode(TNode&& rhs) noexcept; + TNode& operator=(TNode&& rhs) noexcept; + + ~TNode(); + + void Clear(); + + bool IsString() const; + bool IsInt64() const; + bool IsUint64() const; + bool IsDouble() const; + bool IsBool() const; + bool IsList() const; + bool IsMap() const; + + // `IsEntity' is deprecated use `IsNull' instead. + bool IsEntity() const; + bool IsNull() const; + bool IsUndefined() const; + // Returns true if TNode is neither Null, nor Undefined + bool HasValue() const; + + template<typename T> + bool IsOfType() const noexcept; + + // Int64, Uint64, Double, or Bool + bool IsArithmetic() const; + + bool Empty() const; + size_t Size() const; + + EType GetType() const; + + const TString& AsString() const; + i64 AsInt64() const; + ui64 AsUint64() const; + double AsDouble() const; + bool AsBool() const; + const TListType& AsList() const; + const TMapType& AsMap() const; + TListType& AsList(); + TMapType& AsMap(); + + const TString& UncheckedAsString() const noexcept; + i64 UncheckedAsInt64() const noexcept; + ui64 UncheckedAsUint64() const noexcept; + double UncheckedAsDouble() const noexcept; + bool UncheckedAsBool() const noexcept; + const TListType& UncheckedAsList() const noexcept; + const TMapType& UncheckedAsMap() const noexcept; + TListType& UncheckedAsList() noexcept; + TMapType& UncheckedAsMap() noexcept; + + // integer types cast + // makes overflow checks + template<typename T> + T IntCast() const; + + // integers <-> double <-> string + // makes overflow checks + template<typename T> + T ConvertTo() const; + + template<typename T> + T& As(); + + template<typename T> + const T& As() const; + + static TNode CreateList(); + static TNode CreateList(TListType list); + static TNode CreateMap(); + static TNode CreateMap(TMapType map); + static TNode CreateEntity(); + + const TNode& operator[](size_t index) const; + TNode& operator[](size_t index); + const TNode& At(size_t index) const; + TNode& At(size_t index); + + TNode& Add() &; + TNode Add() &&; + TNode& Add(const TNode& node) &; + TNode Add(const TNode& node) &&; + TNode& Add(TNode&& node) &; + TNode Add(TNode&& node) &&; + + bool HasKey(const TStringBuf key) const; + + TNode& operator()(const TString& key, const TNode& value) &; + TNode operator()(const TString& key, const TNode& value) &&; + TNode& operator()(const TString& key, TNode&& value) &; + TNode operator()(const TString& key, TNode&& value) &&; + + const TNode& operator[](const TStringBuf key) const; + TNode& operator[](const TStringBuf key); + const TNode& At(const TStringBuf key) const; + TNode& At(const TStringBuf key); + + // map getters + // works the same way like simple getters + const TString& ChildAsString(const TStringBuf key) const; + i64 ChildAsInt64(const TStringBuf key) const; + ui64 ChildAsUint64(const TStringBuf key) const; + double ChildAsDouble(const TStringBuf key) const; + bool ChildAsBool(const TStringBuf key) const; + const TListType& ChildAsList(const TStringBuf key) const; + const TMapType& ChildAsMap(const TStringBuf key) const; + TListType& ChildAsList(const TStringBuf key); + TMapType& ChildAsMap(const TStringBuf key); + + template<typename T> + T ChildIntCast(const TStringBuf key) const; + + template<typename T> + T ChildConvertTo(const TStringBuf key) const; + + template<typename T> + const T& ChildAs(const TStringBuf key) const; + + template<typename T> + T& ChildAs(const TStringBuf key); + + // list getters + // works the same way like simple getters + const TString& ChildAsString(size_t index) const; + i64 ChildAsInt64(size_t index) const; + ui64 ChildAsUint64(size_t index) const; + double ChildAsDouble(size_t index) const; + bool ChildAsBool(size_t index) const; + const TListType& ChildAsList(size_t index) const; + const TMapType& ChildAsMap(size_t index) const; + TListType& ChildAsList(size_t index); + TMapType& ChildAsMap(size_t index); + + template<typename T> + T ChildIntCast(size_t index) const; + + template<typename T> + T ChildConvertTo(size_t index) const; + + template<typename T> + const T& ChildAs(size_t index) const; + + template<typename T> + T& ChildAs(size_t index); + + + // attributes + bool HasAttributes() const; + void ClearAttributes(); + const TNode& GetAttributes() const; + TNode& Attributes(); + + void MoveWithoutAttributes(TNode&& rhs); + + // Serialize TNode using binary yson format. + // Methods for ysaveload. + void Save(IOutputStream* output) const; + void Load(IInputStream* input); + +private: + void Move(TNode&& rhs); + + void CheckType(EType type) const; + + void AssureMap(); + void AssureList(); + + void CreateAttributes(); + +private: + TValue Value_; + THolder<TNode> Attributes_; + + friend bool operator==(const TNode& lhs, const TNode& rhs); + friend bool operator!=(const TNode& lhs, const TNode& rhs); +}; + +bool operator==(const TNode& lhs, const TNode& rhs); +bool operator!=(const TNode& lhs, const TNode& rhs); + +bool GetBool(const TNode& node); + +inline bool TNode::IsArithmetic() const { + return IsInt64() || IsUint64() || IsDouble() || IsBool(); +} + +template<typename T> +inline T TNode::IntCast() const { + if constexpr (std::is_integral<T>::value) { + try { + switch (GetType()) { + case TNode::Uint64: + return SafeIntegerCast<T>(AsUint64()); + case TNode::Int64: + return SafeIntegerCast<T>(AsInt64()); + default: + ythrow TTypeError() << "IntCast() called for type " << GetType(); + } + } catch(TBadCastException& exc) { + ythrow TTypeError() << "TBadCastException during IntCast(): " << exc.what(); + } + } else { + static_assert(sizeof(T) != sizeof(T), "implemented only for std::is_integral types"); + } +} + +template<typename T> +inline T TNode::ConvertTo() const { + if constexpr (std::is_integral<T>::value) { + switch (GetType()) { + case NYT::TNode::String: + return ::FromString(AsString()); + case NYT::TNode::Int64: + case NYT::TNode::Uint64: + return IntCast<T>(); + case NYT::TNode::Double: + if (AsDouble() < Min<T>() || AsDouble() > MaxFloor<T>() || !std::isfinite(AsDouble())) { + ythrow TTypeError() << AsDouble() << " can't be converted to " << TypeName<T>(); + } + return AsDouble(); + case NYT::TNode::Bool: + return AsBool(); + case NYT::TNode::List: + case NYT::TNode::Map: + case NYT::TNode::Null: + case NYT::TNode::Undefined: + ythrow TTypeError() << "ConvertTo<" << TypeName<T>() << ">() called for type " << GetType(); + }; + } else { + static_assert(sizeof(T) != sizeof(T), "should have template specialization"); + } +} + +template<> +inline TString TNode::ConvertTo<TString>() const { + switch (GetType()) { + case NYT::TNode::String: + return AsString(); + case NYT::TNode::Int64: + return ::ToString(AsInt64()); + case NYT::TNode::Uint64: + return ::ToString(AsUint64()); + case NYT::TNode::Double: + return ::ToString(AsDouble()); + case NYT::TNode::Bool: + return ::ToString(AsBool()); + case NYT::TNode::List: + case NYT::TNode::Map: + case NYT::TNode::Null: + case NYT::TNode::Undefined: + ythrow TTypeError() << "ConvertTo<TString>() called for type " << GetType(); + } + Y_UNREACHABLE(); +} + +template<> +inline double TNode::ConvertTo<double>() const { + switch (GetType()) { + case NYT::TNode::String: + return ::FromString(AsString()); + case NYT::TNode::Int64: + return AsInt64(); + case NYT::TNode::Uint64: + return AsUint64(); + case NYT::TNode::Double: + return AsDouble(); + case NYT::TNode::Bool: + return AsBool(); + case NYT::TNode::List: + case NYT::TNode::Map: + case NYT::TNode::Null: + case NYT::TNode::Undefined: + ythrow TTypeError() << "ConvertTo<double>() called for type " << GetType(); + } +} + +template<> +inline bool TNode::ConvertTo<bool>() const { + switch (GetType()) { + case NYT::TNode::String: + return ::FromString(AsString()); + case NYT::TNode::Int64: + return AsInt64(); + case NYT::TNode::Uint64: + return AsUint64(); + case NYT::TNode::Double: + return AsDouble(); + case NYT::TNode::Bool: + return AsBool(); + case NYT::TNode::List: + case NYT::TNode::Map: + case NYT::TNode::Null: + case NYT::TNode::Undefined: + ythrow TTypeError() << "ConvertTo<bool>() called for type " << GetType(); + } +} + +template<typename T> +inline T TNode::ChildIntCast(const TStringBuf key) const { + const auto& node = At(key); + try { + return node.IntCast<T>(); + } catch (TTypeError& e) { + e << ", during getting key=" << key; + throw e; + } catch (...) { + ythrow TTypeError() << CurrentExceptionMessage() << ", during getting key=" << key; + } +} + +template<typename T> +inline T TNode::ChildIntCast(size_t index) const { + const auto& node = At(index); + try { + return node.IntCast<T>(); + } catch (TTypeError& e) { + e << ", during getting index=" << index; + throw e; + } catch (...) { + ythrow TTypeError() << CurrentExceptionMessage() << ", during getting index=" << index; + } +} + +template<typename T> +inline T TNode::ChildConvertTo(const TStringBuf key) const { + const auto& node = At(key); + try { + return node.ConvertTo<T>(); + } catch (TTypeError& e) { + e << ", during getting key=" << key; + throw e; + } catch (...) { + ythrow TTypeError() << CurrentExceptionMessage() << ", during getting key=" << key; + } +} + +template<typename T> +inline T TNode::ChildConvertTo(size_t index) const { + const auto& node = At(index); + try { + return node.ConvertTo<T>(); + } catch (TTypeError& e) { + e << ", during getting index=" << index; + throw e; + } catch (...) { + ythrow TTypeError() << CurrentExceptionMessage() << ", during getting index=" << index; + } +} + +template<typename T> +inline const T& TNode::ChildAs(const TStringBuf key) const { + const auto& node = At(key); + try { + return node.As<T>(); + } catch (TTypeError& e) { + e << ", during getting key=" << key; + throw e; + } catch (...) { + ythrow TTypeError() << CurrentExceptionMessage() << ", during getting key=" << key; + } +} + +template<typename T> +inline const T& TNode::ChildAs(size_t index) const { + const auto& node = At(index); + try { + return node.As<T>(); + } catch (TTypeError& e) { + e << ", during getting index=" << index; + throw e; + } catch (...) { + ythrow TTypeError() << CurrentExceptionMessage() << ", during getting index=" << index; + } +} + +template<typename T> +inline T& TNode::ChildAs(const TStringBuf key) { + return const_cast<T&>(static_cast<const TNode*>(this)->ChildAs<T>(key)); +} + +template<typename T> +inline T& TNode::ChildAs(size_t index) { + return const_cast<T&>(static_cast<const TNode*>(this)->ChildAs<T>(index)); +} + +template<typename T> +inline bool TNode::IsOfType() const noexcept { + return std::holds_alternative<T>(Value_); +} + +template<typename T> +inline T& TNode::As() { + return std::get<T>(Value_); +} + +template<typename T> +inline const T& TNode::As() const { + return std::get<T>(Value_); +} + +//////////////////////////////////////////////////////////////////////////////// + +namespace NNodeCmp { + bool operator<(const TNode& lhs, const TNode& rhs); + bool operator<=(const TNode& lhs, const TNode& rhs); + bool operator>(const TNode& lhs, const TNode& rhs); + bool operator>=(const TNode& lhs, const TNode& rhs); + bool IsComparableType(const TNode::EType type); +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT diff --git a/library/cpp/yson/node/node_builder.cpp b/library/cpp/yson/node/node_builder.cpp new file mode 100644 index 0000000000..b4431bc77a --- /dev/null +++ b/library/cpp/yson/node/node_builder.cpp @@ -0,0 +1,96 @@ +#include "node_builder.h" + +namespace NYT { + +//////////////////////////////////////////////////////////////////////////////// + +TNodeBuilder::TNodeBuilder(TNode* node) +{ + Stack_.push(node); +} + +void TNodeBuilder::OnStringScalar(TStringBuf value) +{ + AddNode(value, true); +} + +void TNodeBuilder::OnInt64Scalar(i64 value) +{ + AddNode(value, true); +} + +void TNodeBuilder::OnUint64Scalar(ui64 value) +{ + AddNode(value, true); +} + +void TNodeBuilder::OnDoubleScalar(double value) +{ + AddNode(value, true); +} + +void TNodeBuilder::OnBooleanScalar(bool value) +{ + AddNode(value, true); +} + +void TNodeBuilder::OnEntity() +{ + AddNode(TNode::CreateEntity(), true); +} + +void TNodeBuilder::OnBeginList() +{ + AddNode(TNode::CreateList(), false); +} + +void TNodeBuilder::OnListItem() +{ + Stack_.push(&Stack_.top()->Add()); +} + +void TNodeBuilder::OnEndList() +{ + Stack_.pop(); +} + +void TNodeBuilder::OnBeginMap() +{ + AddNode(TNode::CreateMap(), false); +} + +void TNodeBuilder::OnKeyedItem(TStringBuf key) +{ + Stack_.push(&(*Stack_.top())[TString(key)]); +} + +void TNodeBuilder::OnEndMap() +{ + Stack_.pop(); +} + +void TNodeBuilder::OnBeginAttributes() +{ + Stack_.push(&Stack_.top()->Attributes()); +} + +void TNodeBuilder::OnEndAttributes() +{ + Stack_.pop(); +} + +void TNodeBuilder::OnNode(TNode node) +{ + AddNode(std::move(node), true); +} + +void TNodeBuilder::AddNode(TNode value, bool pop) +{ + Stack_.top()->MoveWithoutAttributes(std::move(value)); + if (pop) + Stack_.pop(); +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT diff --git a/library/cpp/yson/node/node_builder.h b/library/cpp/yson/node/node_builder.h new file mode 100644 index 0000000000..69800016e0 --- /dev/null +++ b/library/cpp/yson/node/node_builder.h @@ -0,0 +1,46 @@ +#pragma once + +#include "node.h" + +#include <library/cpp/json/json_reader.h> + +#include <library/cpp/yson/consumer.h> + +#include <util/generic/stack.h> + +namespace NYT { + +//////////////////////////////////////////////////////////////////////////////// + +class TNodeBuilder + : public ::NYson::TYsonConsumerBase +{ +public: + TNodeBuilder(TNode* node); + + void OnStringScalar(TStringBuf) override; + void OnInt64Scalar(i64) override; + void OnUint64Scalar(ui64) override; + void OnDoubleScalar(double) override; + void OnBooleanScalar(bool) override; + void OnEntity() override; + void OnBeginList() override; + void OnListItem() override; + void OnEndList() override; + void OnBeginMap() override; + void OnKeyedItem(TStringBuf) override; + void OnEndMap() override; + void OnBeginAttributes() override; + void OnEndAttributes() override; + void OnNode(TNode node); + +private: + TStack<TNode*> Stack_; + +private: + inline void AddNode(TNode node, bool pop); +}; + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT diff --git a/library/cpp/yson/node/node_io.cpp b/library/cpp/yson/node/node_io.cpp new file mode 100644 index 0000000000..294a7f7217 --- /dev/null +++ b/library/cpp/yson/node/node_io.cpp @@ -0,0 +1,154 @@ +#include "node_io.h" + +#include "node_builder.h" +#include "node_visitor.h" + +#include <library/cpp/yson/json/json_writer.h> +#include <library/cpp/yson/parser.h> +#include <library/cpp/yson/writer.h> +#include <library/cpp/yson/json/yson2json_adapter.h> + +#include <library/cpp/json/json_reader.h> +#include <library/cpp/json/json_value.h> + +#include <util/stream/input.h> +#include <util/stream/output.h> +#include <util/stream/str.h> +#include <util/stream/mem.h> + +namespace NYT { + +static void WalkJsonTree(const NJson::TJsonValue& jsonValue, NJson::TJsonCallbacks* callbacks) +{ + using namespace NJson; + switch (jsonValue.GetType()) { + case JSON_NULL: + callbacks->OnNull(); + return; + case JSON_BOOLEAN: + callbacks->OnBoolean(jsonValue.GetBoolean()); + return; + case JSON_INTEGER: + callbacks->OnInteger(jsonValue.GetInteger()); + return; + case JSON_UINTEGER: + callbacks->OnUInteger(jsonValue.GetUInteger()); + return; + case JSON_DOUBLE: + callbacks->OnDouble(jsonValue.GetDouble()); + return; + case JSON_STRING: + callbacks->OnString(jsonValue.GetString()); + return; + case JSON_MAP: + { + callbacks->OnOpenMap(); + for (const auto& item : jsonValue.GetMap()) { + callbacks->OnMapKey(item.first); + WalkJsonTree(item.second, callbacks); + } + callbacks->OnCloseMap(); + } + return; + case JSON_ARRAY: + { + callbacks->OnOpenArray(); + for (const auto& item : jsonValue.GetArray()) { + WalkJsonTree(item, callbacks); + } + callbacks->OnCloseArray(); + } + return; + case JSON_UNDEFINED: + ythrow yexception() << "cannot consume undefined json value"; + return; + } + Y_UNREACHABLE(); +} + +static TNode CreateEmptyNodeByType(::NYson::EYsonType type) +{ + TNode result; + switch (type) { + case ::NYson::EYsonType::ListFragment: + result = TNode::CreateList(); + break; + case ::NYson::EYsonType::MapFragment: + result = TNode::CreateMap(); + break; + default: + break; + } + return result; +} + +TNode NodeFromYsonString(const TStringBuf input, ::NYson::EYsonType type) +{ + TMemoryInput stream(input); + return NodeFromYsonStream(&stream, type); +} + +TString NodeToYsonString(const TNode& node, NYson::EYsonFormat format) +{ + TStringStream stream; + NodeToYsonStream(node, &stream, format); + return stream.Str(); +} + +TString NodeToCanonicalYsonString(const TNode& node, NYson::EYsonFormat format) +{ + TStringStream stream; + NodeToCanonicalYsonStream(node, &stream, format); + return stream.Str(); +} + +TNode NodeFromYsonStream(IInputStream* input, ::NYson::EYsonType type) +{ + TNode result = CreateEmptyNodeByType(type); + + TNodeBuilder builder(&result); + ::NYson::TYsonParser parser(&builder, input, type); + parser.Parse(); + return result; +} + +void NodeToYsonStream(const TNode& node, IOutputStream* output, NYson::EYsonFormat format) +{ + ::NYson::TYsonWriter writer(output, format); + TNodeVisitor visitor(&writer); + visitor.Visit(node); +} + +void NodeToCanonicalYsonStream(const TNode& node, IOutputStream* output, NYson::EYsonFormat format) +{ + ::NYson::TYsonWriter writer(output, format); + TNodeVisitor visitor(&writer, /*sortMapKeys*/ true); + visitor.Visit(node); +} + +TNode NodeFromJsonString(const TStringBuf input) +{ + TMemoryInput stream(input); + + TNode result; + + TNodeBuilder builder(&result); + TYson2JsonCallbacksAdapter callbacks(&builder, /*throwException*/ true); + NJson::TJsonReaderConfig config; + config.DontValidateUtf8 = true; + NJson::ReadJson(&stream, &config, &callbacks); + return result; +} + +TNode NodeFromJsonValue(const NJson::TJsonValue& input) +{ + TNode result; + TNodeBuilder builder(&result); + TYson2JsonCallbacksAdapter callbacks(&builder, /*throwException*/ true); + WalkJsonTree(input, &callbacks); + return result; +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT diff --git a/library/cpp/yson/node/node_io.h b/library/cpp/yson/node/node_io.h new file mode 100644 index 0000000000..2ad23b658f --- /dev/null +++ b/library/cpp/yson/node/node_io.h @@ -0,0 +1,40 @@ +#pragma once + +#include "node.h" +#include <library/cpp/yson/public.h> + +namespace NJson { + class TJsonValue; +} // namespace NJson + +namespace NYT { + +//////////////////////////////////////////////////////////////////////////////// + +// Parse TNode from string in YSON format +TNode NodeFromYsonString(const TStringBuf input, ::NYson::EYsonType type = ::NYson::EYsonType::Node); + +// Serialize TNode to string in one of YSON formats with random order of maps' keys (don't use in tests) +TString NodeToYsonString(const TNode& node, ::NYson::EYsonFormat format = ::NYson::EYsonFormat::Text); + +// Same as the latter, but maps' keys are sorted lexicographically (to be used in tests) +TString NodeToCanonicalYsonString(const TNode& node, ::NYson::EYsonFormat format = ::NYson::EYsonFormat::Text); + +// Parse TNode from stream in YSON format +TNode NodeFromYsonStream(IInputStream* input, ::NYson::EYsonType type = ::NYson::EYsonType::Node); + +// Serialize TNode to stream in one of YSON formats with random order of maps' keys (don't use in tests) +void NodeToYsonStream(const TNode& node, IOutputStream* output, ::NYson::EYsonFormat format = ::NYson::EYsonFormat::Text); + +// Same as the latter, but maps' keys are sorted lexicographically (to be used in tests) +void NodeToCanonicalYsonStream(const TNode& node, IOutputStream* output, ::NYson::EYsonFormat format = ::NYson::EYsonFormat::Text); + +// Parse TNode from string in JSON format +TNode NodeFromJsonString(const TStringBuf input); + +// Convert TJsonValue to TNode +TNode NodeFromJsonValue(const NJson::TJsonValue& input); + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT diff --git a/library/cpp/yson/node/node_ut.cpp b/library/cpp/yson/node/node_ut.cpp new file mode 100644 index 0000000000..448e99f575 --- /dev/null +++ b/library/cpp/yson/node/node_ut.cpp @@ -0,0 +1,484 @@ +#include "node.h" +#include "node_io.h" + +#include <library/cpp/testing/unittest/registar.h> + +#include <util/ysaveload.h> + +using namespace NYT; + +template<> +void Out<NYT::TNode>(IOutputStream& s, const NYT::TNode& node) +{ + s << "TNode:" << NodeToYsonString(node); +} + +Y_UNIT_TEST_SUITE(YtNodeTest) { + Y_UNIT_TEST(TestConstsructors) { + TNode nodeEmpty; + UNIT_ASSERT_EQUAL(nodeEmpty.GetType(), TNode::Undefined); + + TNode nodeString("foobar"); + UNIT_ASSERT_EQUAL(nodeString.GetType(), TNode::String); + UNIT_ASSERT(nodeString.IsString()); + UNIT_ASSERT_VALUES_EQUAL(nodeString.AsString(), "foobar"); + + TNode nodeInt(int(54)); + UNIT_ASSERT_EQUAL(nodeInt.GetType(), TNode::Int64); + UNIT_ASSERT(nodeInt.IsInt64()); + UNIT_ASSERT(!nodeInt.IsUint64()); + UNIT_ASSERT_VALUES_EQUAL(nodeInt.AsInt64(), 54ull); + + TNode nodeUint(ui64(42)); + UNIT_ASSERT_EQUAL(nodeUint.GetType(), TNode::Uint64); + UNIT_ASSERT(nodeUint.IsUint64()); + UNIT_ASSERT(!nodeUint.IsInt64()); + UNIT_ASSERT_VALUES_EQUAL(nodeUint.AsUint64(), 42ull); + + TNode nodeDouble(double(2.3)); + UNIT_ASSERT_EQUAL(nodeDouble.GetType(), TNode::Double); + UNIT_ASSERT(nodeDouble.IsDouble()); + UNIT_ASSERT_VALUES_EQUAL(nodeDouble.AsDouble(), double(2.3)); + + TNode nodeBool(true); + UNIT_ASSERT_EQUAL(nodeBool.GetType(), TNode::Bool); + UNIT_ASSERT(nodeBool.IsBool()); + UNIT_ASSERT_VALUES_EQUAL(nodeBool.AsBool(), true); + + TNode nodeEntity = TNode::CreateEntity(); + UNIT_ASSERT_EQUAL(nodeEntity.GetType(), TNode::Null); + UNIT_ASSERT(nodeEntity.IsEntity()); + } + + Y_UNIT_TEST(TestPredicates) { + const TNode undefinedNode; + UNIT_ASSERT(undefinedNode.IsUndefined()); + UNIT_ASSERT(!undefinedNode.IsNull()); + UNIT_ASSERT(!undefinedNode.HasValue()); + + const TNode nullNode = TNode::CreateEntity(); + UNIT_ASSERT(!nullNode.IsUndefined()); + UNIT_ASSERT(nullNode.IsNull()); + UNIT_ASSERT(!nullNode.HasValue()); + + const TNode intNode(int(64)); + UNIT_ASSERT(!intNode.IsUndefined()); + UNIT_ASSERT(!intNode.IsNull()); + UNIT_ASSERT(intNode.HasValue()); + + const TNode stringNode("blah"); + UNIT_ASSERT(!stringNode.IsUndefined()); + UNIT_ASSERT(!stringNode.IsNull()); + UNIT_ASSERT(stringNode.HasValue()); + } + + Y_UNIT_TEST(TestComplexConstructors) { + const TNode listNode = TNode::CreateList({"one", 2, "tree"}); + const auto expectedListValue = std::vector<TNode>({"one", 2, "tree"}); + UNIT_ASSERT_VALUES_EQUAL(listNode.AsList(), expectedListValue); + + const TNode mapNode = TNode::CreateMap({{"one", 1}, {"two", 2u}}); + const auto expectedMapValue = THashMap<TString, TNode>({{"one", 1}, {"two", 2u}}); + UNIT_ASSERT_VALUES_EQUAL(mapNode.AsMap(), expectedMapValue); + } + + Y_UNIT_TEST(TestNodeMap) { + TNode nodeMap = TNode()("foo", "bar")("bar", "baz"); + UNIT_ASSERT(nodeMap.IsMap()); + UNIT_ASSERT_EQUAL(nodeMap.GetType(), TNode::Map); + UNIT_ASSERT_VALUES_EQUAL(nodeMap.Size(), 2); + + UNIT_ASSERT(nodeMap.HasKey("foo")); + UNIT_ASSERT(!nodeMap.HasKey("42")); + UNIT_ASSERT_EQUAL(nodeMap["foo"], TNode("bar")); + UNIT_ASSERT_EQUAL(nodeMap["bar"], TNode("baz")); + + // const version of operator[] + UNIT_ASSERT_EQUAL(static_cast<const TNode&>(nodeMap)["42"].GetType(), TNode::Undefined); + UNIT_ASSERT(!nodeMap.HasKey("42")); + + // nonconst version of operator[] + UNIT_ASSERT_EQUAL(nodeMap["42"].GetType(), TNode::Undefined); + UNIT_ASSERT(nodeMap.HasKey("42")); + + nodeMap("rock!!!", TNode() + ("Pink", "Floyd") + ("Purple", "Deep")); + + TNode copyNode; + copyNode = nodeMap; + UNIT_ASSERT_EQUAL(copyNode["foo"], TNode("bar")); + UNIT_ASSERT_EQUAL(copyNode["bar"], TNode("baz")); + UNIT_ASSERT(copyNode["42"].GetType() == TNode::Undefined); + UNIT_ASSERT_EQUAL(copyNode["rock!!!"]["Purple"], TNode("Deep")); + } + + Y_UNIT_TEST(TestNodeList) { + TNode nodeList = TNode().Add("foo").Add(42).Add(3.14); + UNIT_ASSERT(nodeList.IsList()); + UNIT_ASSERT_EQUAL(nodeList.GetType(), TNode::List); + UNIT_ASSERT_VALUES_EQUAL(nodeList.Size(), 3); + + UNIT_ASSERT_EQUAL(nodeList[1], TNode(42)); + nodeList.Add(TNode().Add("ls").Add("pwd")); + + TNode copyNode; + copyNode = nodeList; + UNIT_ASSERT_EQUAL(copyNode[0], TNode("foo")); + UNIT_ASSERT_EQUAL(copyNode[3][1], TNode("pwd")); + } + + Y_UNIT_TEST(TestInsertingMethodsFromTemporaryObjects) { + // check that .Add(...) doesn't return lvalue reference to temporary object + { + const TNode& nodeList = TNode().Add(0).Add("pass").Add(0); + UNIT_ASSERT_EQUAL(nodeList[1], TNode("pass")); + } + + // check that .operator()(...) doesn't return lvalue reference to temporary object + { + const TNode& nodeMap = TNode()("1", 0)("2", "pass")("3", 0); + UNIT_ASSERT_EQUAL(nodeMap["2"], TNode("pass")); + } + } + + Y_UNIT_TEST(TestAttributes) { + TNode node = TNode()("lee", 42)("faa", 54); + UNIT_ASSERT(!node.HasAttributes()); + node.Attributes()("foo", true)("bar", false); + UNIT_ASSERT(node.HasAttributes()); + + { + TNode copyNode; + UNIT_ASSERT(!copyNode.HasAttributes()); + copyNode = node; + UNIT_ASSERT(copyNode.HasAttributes()); + UNIT_ASSERT_EQUAL(copyNode.GetAttributes()["foo"], TNode(true)); + } + + { + TNode movedWithoutAttributes(42); + movedWithoutAttributes.Attributes()("one", 1)("two", 2); + movedWithoutAttributes.MoveWithoutAttributes(TNode(node)); + UNIT_ASSERT(movedWithoutAttributes.IsMap()); + UNIT_ASSERT_EQUAL(movedWithoutAttributes["lee"], TNode(42)); + UNIT_ASSERT_EQUAL(movedWithoutAttributes.GetAttributes()["one"], TNode(1)); + UNIT_ASSERT(!movedWithoutAttributes.GetAttributes().HasKey("foo")); + } + + { + TNode copyNode = node; + UNIT_ASSERT(copyNode.HasAttributes()); + UNIT_ASSERT(copyNode.GetAttributes().HasKey("foo")); + copyNode.ClearAttributes(); + UNIT_ASSERT(!copyNode.HasAttributes()); + UNIT_ASSERT(!copyNode.GetAttributes().HasKey("foo")); + } + + { + TNode copyNode = node; + UNIT_ASSERT(copyNode.HasAttributes()); + UNIT_ASSERT(copyNode.GetAttributes().HasKey("foo")); + copyNode.Clear(); + UNIT_ASSERT(!copyNode.HasAttributes()); + UNIT_ASSERT(!copyNode.GetAttributes().HasKey("foo")); + } + } + + Y_UNIT_TEST(TestEq) { + TNode nodeNoAttributes = TNode()("lee", 42)("faa", 54); + TNode node = nodeNoAttributes; + node.Attributes()("foo", true)("bar", false); + UNIT_ASSERT(node != nodeNoAttributes); + UNIT_ASSERT(nodeNoAttributes != node); + TNode copyNode = node; + UNIT_ASSERT(copyNode == node); + UNIT_ASSERT(node == copyNode); + } + + Y_UNIT_TEST(TestComparison) { + using namespace NYT::NNodeCmp; + { + TNode nodeNoAttributes = TNode()("lee", 42)("faa", 54); + TNode node = nodeNoAttributes; + node.Attributes()("foo", true)("bar", false); + UNIT_ASSERT_EXCEPTION(node > nodeNoAttributes, TNode::TTypeError); + UNIT_ASSERT_EXCEPTION(node >= nodeNoAttributes, TNode::TTypeError); + UNIT_ASSERT_EXCEPTION(nodeNoAttributes < node, TNode::TTypeError); + UNIT_ASSERT_EXCEPTION(nodeNoAttributes <= node, TNode::TTypeError); + } + { + TNode nodeMap = TNode()("map", 23); + TNode nodeList = TNode::CreateList(); + UNIT_ASSERT_EXCEPTION(nodeList > nodeMap, TNode::TTypeError); + UNIT_ASSERT_EXCEPTION(nodeMap < nodeList, TNode::TTypeError); + UNIT_ASSERT_EXCEPTION(nodeMap >= nodeMap, TNode::TTypeError); + UNIT_ASSERT_EXCEPTION(nodeList <= nodeList, TNode::TTypeError); + } + { + TNode node1("aaa"); + TNode node2("bbb"); + TNode node3("ccc"); + UNIT_ASSERT(node1 < node2); + UNIT_ASSERT(node1 <= node2); + UNIT_ASSERT(node1 < node3); + UNIT_ASSERT(node1 <= node3); + UNIT_ASSERT(!(node3 < node1)); + UNIT_ASSERT(!(node1 > node3)); + UNIT_ASSERT(!(node3 <= node1)); + UNIT_ASSERT(!(node1 >= node3)); + + UNIT_ASSERT(node3 > node2); + UNIT_ASSERT(node3 >= node2); + UNIT_ASSERT(node3 > node1); + UNIT_ASSERT(node3 >= node1); + + UNIT_ASSERT(node1 <= node1); + UNIT_ASSERT(node1 >= node1); + } + { + TNode node1(23); + TNode node2("bbb"); + TNode node3 = TNode::CreateEntity(); + + UNIT_ASSERT(node1 > node2); + UNIT_ASSERT(node1 >= node2); + UNIT_ASSERT(node2 < node1); + UNIT_ASSERT(node2 <= node1); + + UNIT_ASSERT(!(node1 < node2)); + UNIT_ASSERT(!(node1 <= node2)); + UNIT_ASSERT(!(node2 > node1)); + UNIT_ASSERT(!(node2 >= node1)); + + UNIT_ASSERT(node1 < node3); + UNIT_ASSERT(node2 < node3); + UNIT_ASSERT(node3 <= node3); + UNIT_ASSERT(!(node3 < node3)); + UNIT_ASSERT(!(node3 > node3)); + UNIT_ASSERT(!(node2 >= node3)); + } + } + + Y_UNIT_TEST(TestSaveLoad) { + TNode node = TNode()("foo", "bar")("baz", 42); + node.Attributes()["attr_name"] = "attr_value"; + + TString bytes; + { + TStringOutput s(bytes); + ::Save(&s, node); + } + + TNode nodeCopy; + { + TStringInput s(bytes); + ::Load(&s, nodeCopy); + } + + UNIT_ASSERT_VALUES_EQUAL(node, nodeCopy); + } + + Y_UNIT_TEST(TestIntCast) { + TNode node = 1ull << 31; + UNIT_ASSERT(node.IsUint64()); + UNIT_ASSERT_EXCEPTION(node.IntCast<i32>(), TNode::TTypeError); + UNIT_ASSERT(node.IntCast<ui32>() == static_cast<ui32>(node.AsUint64())); + UNIT_ASSERT(node.IntCast<i64>() == static_cast<i64>(node.AsUint64())); + UNIT_ASSERT(node.IntCast<ui64>() == node.AsUint64()); + + node = 1ull << 63; + UNIT_ASSERT(node.IsUint64()); + UNIT_ASSERT_EXCEPTION(node.IntCast<i64>(), TNode::TTypeError); + UNIT_ASSERT(node.IntCast<ui64>() == node.AsUint64()); + + node = 12345; + UNIT_ASSERT(node.IsInt64()); + UNIT_ASSERT_EXCEPTION(node.IntCast<i8>(), TNode::TTypeError); + UNIT_ASSERT_EXCEPTION(node.IntCast<ui8>(), TNode::TTypeError); + UNIT_ASSERT(node.IntCast<i16>() == static_cast<i16>(node.AsInt64())); + UNIT_ASSERT(node.IntCast<ui16>() == static_cast<ui16>(node.AsInt64())); + UNIT_ASSERT(node.IntCast<i32>() == static_cast<i32>(node.AsInt64())); + UNIT_ASSERT(node.IntCast<ui32>() == static_cast<ui32>(node.AsInt64())); + UNIT_ASSERT(node.IntCast<i64>() == node.AsInt64()); + UNIT_ASSERT(node.IntCast<ui64>() == static_cast<ui64>(node.AsInt64())); + + node = -5; + UNIT_ASSERT(node.IsInt64()); + UNIT_ASSERT(node.IntCast<i8>() == static_cast<i8>(node.AsInt64())); + UNIT_ASSERT(node.IntCast<i16>() == static_cast<i16>(node.AsInt64())); + UNIT_ASSERT(node.IntCast<i32>() == static_cast<i32>(node.AsInt64())); + UNIT_ASSERT(node.IntCast<i64>() == node.AsInt64()); + UNIT_ASSERT_EXCEPTION(node.IntCast<ui8>(), TNode::TTypeError); + UNIT_ASSERT_EXCEPTION(node.IntCast<ui16>(), TNode::TTypeError); + UNIT_ASSERT_EXCEPTION(node.IntCast<ui32>(), TNode::TTypeError); + UNIT_ASSERT_EXCEPTION(node.IntCast<ui64>(), TNode::TTypeError); + } + + Y_UNIT_TEST(TestConvertToString) { + UNIT_ASSERT_VALUES_EQUAL(TNode(5).ConvertTo<TString>(), "5"); + UNIT_ASSERT_VALUES_EQUAL(TNode(123432423).ConvertTo<TString>(), "123432423"); + UNIT_ASSERT_VALUES_EQUAL(TNode(123456789012345678ll).ConvertTo<TString>(), "123456789012345678"); + UNIT_ASSERT_VALUES_EQUAL(TNode(123456789012345678ull).ConvertTo<TString>(), "123456789012345678"); + UNIT_ASSERT_VALUES_EQUAL(TNode(-123456789012345678ll).ConvertTo<TString>(), "-123456789012345678"); + UNIT_ASSERT_VALUES_EQUAL(TNode(true).ConvertTo<TString>(), "1"); + UNIT_ASSERT_VALUES_EQUAL(TNode(false).ConvertTo<TString>(), "0"); + UNIT_ASSERT_VALUES_EQUAL(TNode(5.3).ConvertTo<TString>(), "5.3"); + } + + Y_UNIT_TEST(TestConvertFromString) { + UNIT_ASSERT_VALUES_EQUAL(TNode("123456789012345678").ConvertTo<ui64>(), 123456789012345678ull); + UNIT_ASSERT_VALUES_EQUAL(TNode("123456789012345678").ConvertTo<i64>(), 123456789012345678); + UNIT_ASSERT_VALUES_EQUAL(TNode(ToString(1ull << 63)).ConvertTo<ui64>(), 1ull << 63); + UNIT_ASSERT_EXCEPTION(TNode(ToString(1ull << 63)).ConvertTo<i64>(), TFromStringException); + UNIT_ASSERT_VALUES_EQUAL(TNode("5.34").ConvertTo<double>(), 5.34); + } + + Y_UNIT_TEST(TestConvertDoubleInt) { + UNIT_ASSERT_VALUES_EQUAL(TNode(5.3).ConvertTo<i8>(), 5); + UNIT_ASSERT_VALUES_EQUAL(TNode(5.3).ConvertTo<ui8>(), 5); + UNIT_ASSERT_VALUES_EQUAL(TNode(5.3).ConvertTo<i64>(), 5); + UNIT_ASSERT_VALUES_EQUAL(TNode(5.3).ConvertTo<ui64>(), 5); + + UNIT_ASSERT_VALUES_EQUAL(TNode(-5.3).ConvertTo<i8>(), -5); + UNIT_ASSERT_VALUES_EQUAL(TNode(-5.3).ConvertTo<i64>(), -5); + UNIT_ASSERT_EXCEPTION(TNode(-5.3).ConvertTo<ui8>(), TNode::TTypeError); + UNIT_ASSERT_EXCEPTION(TNode(-5.3).ConvertTo<ui64>(), TNode::TTypeError); + + UNIT_ASSERT_VALUES_EQUAL(TNode(127.0).ConvertTo<i8>(), 127); + UNIT_ASSERT_EXCEPTION(TNode(128.0).ConvertTo<i8>(), TNode::TTypeError); + UNIT_ASSERT_VALUES_EQUAL(TNode(255.0).ConvertTo<ui8>(), 255); + UNIT_ASSERT_EXCEPTION(TNode(256.0).ConvertTo<ui8>(), TNode::TTypeError); + UNIT_ASSERT_EXCEPTION(TNode(1e100).ConvertTo<i64>(), TNode::TTypeError); + UNIT_ASSERT_EXCEPTION(TNode(1e100).ConvertTo<ui64>(), TNode::TTypeError); + { + double v = 1ull << 63; + TNode node = v; + UNIT_ASSERT(node.IsDouble()); + UNIT_ASSERT_EXCEPTION(node.ConvertTo<i64>(), TNode::TTypeError); + UNIT_ASSERT_VALUES_EQUAL(node.ConvertTo<ui64>(), static_cast<ui64>(v)); + } + { + double v = (double)(1ull << 63) + (1ull << 63); + TNode node = v; + UNIT_ASSERT(node.IsDouble()); + UNIT_ASSERT_EXCEPTION(node.ConvertTo<i64>(), TNode::TTypeError); + UNIT_ASSERT_EXCEPTION(node.ConvertTo<ui64>(), TNode::TTypeError); + } + UNIT_ASSERT_EXCEPTION(TNode(NAN).ConvertTo<ui64>(), TNode::TTypeError); + UNIT_ASSERT_EXCEPTION(TNode(NAN).ConvertTo<i64>(), TNode::TTypeError); + + UNIT_ASSERT_EXCEPTION(TNode(INFINITY).ConvertTo<ui64>(), TNode::TTypeError); + UNIT_ASSERT_EXCEPTION(TNode(INFINITY).ConvertTo<i64>(), TNode::TTypeError); + } + + Y_UNIT_TEST(TestConvertToBool) { + UNIT_ASSERT_VALUES_EQUAL(TNode("true").ConvertTo<bool>(), true); + UNIT_ASSERT_VALUES_EQUAL(TNode("TRUE").ConvertTo<bool>(), true); + UNIT_ASSERT_VALUES_EQUAL(TNode("false").ConvertTo<bool>(), false); + UNIT_ASSERT_VALUES_EQUAL(TNode("FALSE").ConvertTo<bool>(), false); + UNIT_ASSERT_VALUES_EQUAL(TNode(1).ConvertTo<bool>(), true); + UNIT_ASSERT_VALUES_EQUAL(TNode(0).ConvertTo<bool>(), false); + UNIT_ASSERT_EXCEPTION(TNode("random").ConvertTo<bool>(), TFromStringException); + UNIT_ASSERT_EXCEPTION(TNode("").ConvertTo<bool>(), TFromStringException); + } + + Y_UNIT_TEST(TestCanonicalSerialization) { + auto node = TNode() + ("ca", "ca")("c", "c")("a", "a")("b", "b") + ("bb", TNode() + ("ii", "ii")("i", "i")("jj", "jj")); + node.Attributes() = TNode()("za", "za")("z", "z")("xxx", "xxx")("xx", "xx"); + UNIT_ASSERT_VALUES_EQUAL(NodeToCanonicalYsonString(node), + "<\"xx\"=\"xx\";\"xxx\"=\"xxx\";\"z\"=\"z\";\"za\"=\"za\">" + "{\"a\"=\"a\";\"b\"=\"b\";\"bb\"=" + "{\"i\"=\"i\";\"ii\"=\"ii\";\"jj\"=\"jj\"};" + "\"c\"=\"c\";\"ca\"=\"ca\"}"); + } + + Y_UNIT_TEST(OperatorEqualSubnode) { + TNode node; + node["a"]["b"] = "c"; + + node = node["a"]; + node = node["b"]; + + UNIT_ASSERT_VALUES_EQUAL(node.AsString(), "c"); + } + + Y_UNIT_TEST(TestMapGetters) { + auto node = TNode::CreateMap() + ("string", "7") + ("int64", 3) + ("uint64", 5u) + ("double", -3.5) + ("list", TNode::CreateList().Add(5)) + ("map", TNode::CreateMap()("key", "value")); + + UNIT_ASSERT_VALUES_EQUAL(node.ChildAs<TString>("string"), "7"); + UNIT_ASSERT_VALUES_EQUAL(node.ChildAsString("string"), "7"); + UNIT_ASSERT_VALUES_EQUAL(node.ChildConvertTo<i64>("string"), 7); + + UNIT_ASSERT_VALUES_EQUAL(node.ChildAs<i64>("int64"), 3); + UNIT_ASSERT_VALUES_EQUAL(node.ChildAsInt64("int64"), 3); + UNIT_ASSERT_VALUES_EQUAL(node.ChildIntCast<ui64>("int64"), 3u); + + UNIT_ASSERT_VALUES_EQUAL(node.ChildAs<ui64>("uint64"), 5u); + UNIT_ASSERT_VALUES_EQUAL(node.ChildAsUint64("uint64"), 5u); + UNIT_ASSERT_VALUES_EQUAL(node.ChildIntCast<i64>("uint64"), 5); + UNIT_ASSERT_VALUES_EQUAL(node.ChildConvertTo<TString>("uint64"), "5"); + + UNIT_ASSERT_VALUES_EQUAL(node.ChildAs<double>("double"), -3.5); + UNIT_ASSERT_VALUES_EQUAL(node.ChildAsDouble("double"), -3.5); + UNIT_ASSERT_VALUES_EQUAL(node.ChildConvertTo<TString>("double"), "-3.5"); + + UNIT_ASSERT_VALUES_EQUAL(node.ChildAs<TNode::TListType>("list")[0].AsInt64(), 5); + UNIT_ASSERT_VALUES_EQUAL(node.ChildAsList("list")[0].AsInt64(), 5); + + UNIT_ASSERT_VALUES_EQUAL(node.ChildAs<TNode::TMapType>("map")["key"].AsString(), "value"); + UNIT_ASSERT_VALUES_EQUAL(node.ChildAsMap("map")["key"].AsString(), "value"); + + // mutable accessor + auto& childString = node.ChildAs<TString>("string"); + childString = "yaddayadda"; + UNIT_ASSERT_VALUES_EQUAL(node.ChildAs<TString>("string"), "yaddayadda"); + } + + Y_UNIT_TEST(TestListGetters) { + auto node = TNode::CreateList() + .Add("7") + .Add(3) + .Add(5u) + .Add(-3.5) + .Add(TNode::CreateList().Add(5)) + .Add(TNode::CreateMap()("key", "value")); + + UNIT_ASSERT_VALUES_EQUAL(node.ChildAs<TString>(0), "7"); + UNIT_ASSERT_VALUES_EQUAL(node.ChildAsString(0), "7"); + UNIT_ASSERT_VALUES_EQUAL(node.ChildConvertTo<i64>(0), 7); + + UNIT_ASSERT_VALUES_EQUAL(node.ChildAs<i64>(1), 3); + UNIT_ASSERT_VALUES_EQUAL(node.ChildAsInt64(1), 3); + UNIT_ASSERT_VALUES_EQUAL(node.ChildIntCast<ui64>(1), 3u); + + UNIT_ASSERT_VALUES_EQUAL(node.ChildAs<ui64>(2), 5u); + UNIT_ASSERT_VALUES_EQUAL(node.ChildAsUint64(2), 5u); + UNIT_ASSERT_VALUES_EQUAL(node.ChildIntCast<i64>(2), 5); + UNIT_ASSERT_VALUES_EQUAL(node.ChildConvertTo<TString>(2), "5"); + + UNIT_ASSERT_VALUES_EQUAL(node.ChildAs<double>(3), -3.5); + UNIT_ASSERT_VALUES_EQUAL(node.ChildAsDouble(3), -3.5); + UNIT_ASSERT_VALUES_EQUAL(node.ChildConvertTo<TString>(3), "-3.5"); + + UNIT_ASSERT_VALUES_EQUAL(node.ChildAs<TNode::TListType>(4)[0].AsInt64(), 5); + UNIT_ASSERT_VALUES_EQUAL(node.ChildAsList(4)[0].AsInt64(), 5); + + UNIT_ASSERT_VALUES_EQUAL(node.ChildAs<TNode::TMapType>(5)["key"].AsString(), "value"); + UNIT_ASSERT_VALUES_EQUAL(node.ChildAsMap(5)["key"].AsString(), "value"); + + // mutable accessor + auto& childString = node.ChildAs<TString>(0); + childString = "yaddayadda"; + UNIT_ASSERT_VALUES_EQUAL(node.ChildAs<TString>(0), "yaddayadda"); + } +} diff --git a/library/cpp/yson/node/node_visitor.cpp b/library/cpp/yson/node/node_visitor.cpp new file mode 100644 index 0000000000..899fbfa02a --- /dev/null +++ b/library/cpp/yson/node/node_visitor.cpp @@ -0,0 +1,152 @@ +#include "node_visitor.h" + +#include <util/generic/algorithm.h> +#include <util/string/printf.h> + +namespace NYT { + +//////////////////////////////////////////////////////////////////////////////// + +namespace { + +template <typename Fun> +void Iterate(const TNode::TMapType& nodeMap, bool sortByKey, Fun action) +{ + if (sortByKey) { + TVector<TNode::TMapType::const_iterator> iterators; + for (auto it = nodeMap.begin(); it != nodeMap.end(); ++it) { + iterators.push_back(it); + } + SortBy(iterators, [](TNode::TMapType::const_iterator it) { return it->first; }); + for (const auto& it : iterators) { + action(*it); + } + } else { + ForEach(nodeMap.begin(), nodeMap.end(), action); + } +} + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// + +TNodeVisitor::TNodeVisitor(NYson::IYsonConsumer* consumer, bool sortMapKeys) + : Consumer_(consumer) + , SortMapKeys_(sortMapKeys) +{ } + +void TNodeVisitor::Visit(const TNode& node) +{ + VisitAny(node); +} + +void TNodeVisitor::VisitAny(const TNode& node) +{ + if (node.HasAttributes()) { + Consumer_->OnBeginAttributes(); + Iterate(node.GetAttributes().AsMap(), SortMapKeys_, [&](const std::pair<TString, TNode>& item) { + Consumer_->OnKeyedItem(item.first); + if (item.second.IsUndefined()) { + ythrow TNode::TTypeError() << "unable to visit attribute value of type " + << TNode::EType::Undefined << "; attribute name: `" << item.first << '\'' ; + } + VisitAny(item.second); + }); + Consumer_->OnEndAttributes(); + } + + switch (node.GetType()) { + case TNode::String: + VisitString(node); + break; + case TNode::Int64: + VisitInt64(node); + break; + case TNode::Uint64: + VisitUint64(node); + break; + case TNode::Double: + VisitDouble(node); + break; + case TNode::Bool: + VisitBool(node); + break; + case TNode::List: + VisitList(node.AsList()); + break; + case TNode::Map: + VisitMap(node.AsMap()); + break; + case TNode::Null: + VisitEntity(); + break; + case TNode::Undefined: + ythrow TNode::TTypeError() << "unable to visit TNode of type " << node.GetType(); + default: + Y_FAIL("Unexpected type: %d", node.GetType()); + } +} + +void TNodeVisitor::VisitString(const TNode& node) +{ + Consumer_->OnStringScalar(node.AsString()); +} + +void TNodeVisitor::VisitInt64(const TNode& node) +{ + Consumer_->OnInt64Scalar(node.AsInt64()); +} + +void TNodeVisitor::VisitUint64(const TNode& node) +{ + Consumer_->OnUint64Scalar(node.AsUint64()); +} + +void TNodeVisitor::VisitDouble(const TNode& node) +{ + Consumer_->OnDoubleScalar(node.AsDouble()); +} + +void TNodeVisitor::VisitBool(const TNode& node) +{ + Consumer_->OnBooleanScalar(node.AsBool()); +} + +void TNodeVisitor::VisitList(const TNode::TListType& nodeList) +{ + Consumer_->OnBeginList(); + size_t index = 0; + for (const auto& item : nodeList) { + Consumer_->OnListItem(); + if (item.IsUndefined()) { + ythrow TNode::TTypeError() << "unable to visit list node child of type " + << TNode::EType::Undefined << "; list index: " << index; + } + VisitAny(item); + ++index; + } + Consumer_->OnEndList(); +} + +void TNodeVisitor::VisitMap(const TNode::TMapType& nodeMap) +{ + Consumer_->OnBeginMap(); + Iterate(nodeMap, SortMapKeys_, [&](const std::pair<TString, TNode>& item) { + Consumer_->OnKeyedItem(item.first); + if (item.second.IsUndefined()) { + ythrow TNode::TTypeError() << "unable to visit map node child of type " + << TNode::EType::Undefined << "; map key: `" << item.first << '\'' ; + } + VisitAny(item.second); + }); + Consumer_->OnEndMap(); +} + +void TNodeVisitor::VisitEntity() +{ + Consumer_->OnEntity(); +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT diff --git a/library/cpp/yson/node/node_visitor.h b/library/cpp/yson/node/node_visitor.h new file mode 100644 index 0000000000..db25832309 --- /dev/null +++ b/library/cpp/yson/node/node_visitor.h @@ -0,0 +1,37 @@ +#pragma once + +#include "node.h" + +#include <library/cpp/yson/consumer.h> + +namespace NYT { + +//////////////////////////////////////////////////////////////////////////////// + +class TNodeVisitor +{ +public: + TNodeVisitor(NYson::IYsonConsumer* consumer, bool sortMapKeys = false); + + void Visit(const TNode& node); + void VisitMap(const TNode::TMapType& nodeMap); + void VisitList(const TNode::TListType& nodeMap); + +private: + NYson::IYsonConsumer* Consumer_; + bool SortMapKeys_; + +private: + void VisitAny(const TNode& node); + + void VisitString(const TNode& node); + void VisitInt64(const TNode& node); + void VisitUint64(const TNode& node); + void VisitDouble(const TNode& node); + void VisitBool(const TNode& node); + void VisitEntity(); +}; + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT diff --git a/library/cpp/yson/node/pybind/node.cpp b/library/cpp/yson/node/pybind/node.cpp new file mode 100644 index 0000000000..79beba3647 --- /dev/null +++ b/library/cpp/yson/node/pybind/node.cpp @@ -0,0 +1,105 @@ +#include "node.h" + +#include <library/cpp/yson/node/node.h> + +#include <library/cpp/pybind/cast.h> + +#include <Python.h> + +namespace NYT { + + PyObject* BuildPyObject(const TNode& node) { + switch (node.GetType()) { + case TNode::Bool: + return NPyBind::BuildPyObject(node.AsBool()); + case TNode::Int64: + return NPyBind::BuildPyObject(node.AsInt64()); + case TNode::Uint64: + return NPyBind::BuildPyObject(node.AsUint64()); + case TNode::Double: + return NPyBind::BuildPyObject(node.AsDouble()); + case TNode::String: + return NPyBind::BuildPyObject(node.AsString()); + case TNode::List: + return NPyBind::BuildPyObject(node.AsList()); + case TNode::Map: + return NPyBind::BuildPyObject(node.AsMap()); + case TNode::Null: + Py_RETURN_NONE; + case TNode::Undefined: + ythrow TNode::TTypeError() << "BuildPyObject called for undefined TNode"; + } + } + +} // namespace NYT + +namespace NPyBind { + + template <> + bool FromPyObject(PyObject* obj, NYT::TNode& res) { + if (obj == Py_None) { + res = NYT::TNode::CreateEntity(); + return true; + } + if (PyBool_Check(obj)) { + res = false; + return FromPyObject(obj, res.As<bool>()); + } + if (PyFloat_Check(obj)) { + res = 0.0; + return FromPyObject(obj, res.As<double>()); + } +#if PY_MAJOR_VERSION < 3 + if (PyString_Check(obj)) { + res = TString(); + return FromPyObject(obj, res.As<TString>()); + } +#else + if (PyUnicode_Check(obj)) { + res = TString(); + return FromPyObject(obj, res.As<TString>()); + } + if (PyBytes_Check(obj)) { + res = TString(); + return FromPyObject(obj, res.As<TString>()); + } +#endif + if (PyList_Check(obj)) { + res = NYT::TNode::CreateList(); + return FromPyObject(obj, res.AsList()); + } + if (PyDict_Check(obj)) { + res = NYT::TNode::CreateMap(); + return FromPyObject(obj, res.AsMap()); + } +#if PY_MAJOR_VERSION < 3 + if (PyInt_Check(obj)) { + auto valAsLong = PyInt_AsLong(obj); + if (valAsLong == -1 && PyErr_Occurred()) { + return false; + } + res = valAsLong; + return true; + } +#endif + if (PyLong_Check(obj)) { + int overflow = 0; + auto valAsLong = PyLong_AsLongAndOverflow(obj, &overflow); + if (!overflow) { + if (valAsLong == -1 && PyErr_Occurred()) { + return false; + } + res = valAsLong; + return true; + } + auto valAsULong = PyLong_AsUnsignedLong(obj); + if (valAsULong == static_cast<decltype(valAsULong)>(-1) && PyErr_Occurred()) { + return false; + } + res = valAsULong; + return true; + } + return false; + } + +} // namespace NPyBind diff --git a/library/cpp/yson/node/pybind/node.h b/library/cpp/yson/node/pybind/node.h new file mode 100644 index 0000000000..65f7236de6 --- /dev/null +++ b/library/cpp/yson/node/pybind/node.h @@ -0,0 +1,9 @@ +#pragma once + +#include <Python.h> + +#include <library/cpp/yson/node/node.h> + +namespace NYT { + PyObject* BuildPyObject(const TNode& val); +} diff --git a/library/cpp/yson/node/pybind/ya.make b/library/cpp/yson/node/pybind/ya.make new file mode 100644 index 0000000000..97b7583e96 --- /dev/null +++ b/library/cpp/yson/node/pybind/ya.make @@ -0,0 +1,16 @@ +PY23_NATIVE_LIBRARY() + +OWNER( + inngonch + g:yt +) + +PEERDIR( + library/cpp/pybind + library/cpp/yson/node +) +SRCS( + node.cpp +) + +END() diff --git a/library/cpp/yson/node/serialize.cpp b/library/cpp/yson/node/serialize.cpp new file mode 100644 index 0000000000..aeb467622b --- /dev/null +++ b/library/cpp/yson/node/serialize.cpp @@ -0,0 +1,101 @@ +#include "serialize.h" + +#include "node_visitor.h" + +#include <library/cpp/yson/consumer.h> + +namespace NYT { + +//////////////////////////////////////////////////////////////////////////////// + +void Serialize(const TString& value, NYson::IYsonConsumer* consumer) +{ + consumer->OnStringScalar(value); +} + +void Serialize(const TStringBuf& value, NYson::IYsonConsumer* consumer) +{ + consumer->OnStringScalar(value); +} + +void Serialize(const char* value, NYson::IYsonConsumer* consumer) +{ + consumer->OnStringScalar(value); +} + +void Deserialize(TString& value, const TNode& node) +{ + value = node.AsString(); +} + +#define SERIALIZE_SIGNED(type) \ +void Serialize(type value, NYson::IYsonConsumer* consumer) \ +{ \ + consumer->OnInt64Scalar(static_cast<i64>(value)); \ +} + +#define SERIALIZE_UNSIGNED(type) \ +void Serialize(type value, NYson::IYsonConsumer* consumer) \ +{ \ + consumer->OnUint64Scalar(static_cast<ui64>(value)); \ +} + +SERIALIZE_SIGNED(signed char); +SERIALIZE_SIGNED(short); +SERIALIZE_SIGNED(int); +SERIALIZE_SIGNED(long); +SERIALIZE_SIGNED(long long); + +SERIALIZE_UNSIGNED(unsigned char); +SERIALIZE_UNSIGNED(unsigned short); +SERIALIZE_UNSIGNED(unsigned int); +SERIALIZE_UNSIGNED(unsigned long); +SERIALIZE_UNSIGNED(unsigned long long); + +#undef SERIALIZE_SIGNED +#undef SERIALIZE_UNSIGNED + +void Deserialize(i64& value, const TNode& node) +{ + value = node.AsInt64(); +} + +void Deserialize(ui64& value, const TNode& node) +{ + value = node.AsUint64(); +} + +void Serialize(double value, NYson::IYsonConsumer* consumer) +{ + consumer->OnDoubleScalar(value); +} + +void Deserialize(double& value, const TNode& node) +{ + value = node.AsDouble(); +} + +void Serialize(bool value, NYson::IYsonConsumer* consumer) +{ + consumer->OnBooleanScalar(value); +} + +void Deserialize(bool& value, const TNode& node) +{ + value = node.AsBool(); +} + +void Serialize(const TNode& node, NYson::IYsonConsumer* consumer) +{ + TNodeVisitor visitor(consumer); + visitor.Visit(node); +} + +void Deserialize(TNode& value, const TNode& node) +{ + value = node; +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT diff --git a/library/cpp/yson/node/serialize.h b/library/cpp/yson/node/serialize.h new file mode 100644 index 0000000000..99b598a44c --- /dev/null +++ b/library/cpp/yson/node/serialize.h @@ -0,0 +1,45 @@ +#pragma once + +#include "node.h" + +namespace NYT { + +namespace NYson { +struct IYsonConsumer; +} // namespace NYson + +//////////////////////////////////////////////////////////////////////////////// + +void Serialize(const TString& value, NYson::IYsonConsumer* consumer); +void Serialize(const TStringBuf& value, NYson::IYsonConsumer* consumer); +void Serialize(const char* value, NYson::IYsonConsumer* consumer); +void Deserialize(TString& value, const TNode& node); + +void Serialize(signed char value, NYson::IYsonConsumer* consumer); +void Serialize(short value, NYson::IYsonConsumer* consumer); +void Serialize(int value, NYson::IYsonConsumer* consumer); +void Serialize(long value, NYson::IYsonConsumer* consumer); +void Serialize(long long value, NYson::IYsonConsumer* consumer); +void Deserialize(i64& value, const TNode& node); + +void Serialize(unsigned char value, NYson::IYsonConsumer* consumer); +void Serialize(unsigned short value, NYson::IYsonConsumer* consumer); +void Serialize(unsigned int value, NYson::IYsonConsumer* consumer); +void Serialize(unsigned long value, NYson::IYsonConsumer* consumer); +void Serialize(unsigned long long value, NYson::IYsonConsumer* consumer); +void Deserialize(ui64& value, const TNode& node); + +void Serialize(double value, NYson::IYsonConsumer* consumer); +void Deserialize(double& value, const TNode& node); + +void Serialize(bool value, NYson::IYsonConsumer* consumer); +void Deserialize(bool& value, const TNode& node); + +void Serialize(const TNode& node, NYson::IYsonConsumer* consumer); +void Deserialize(TNode& value, const TNode& node); + +void Serialize(const THashMap<TString, TString>& renameColumns, NYson::IYsonConsumer* consumer); + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT diff --git a/library/cpp/yson/node/ut/ya.make b/library/cpp/yson/node/ut/ya.make new file mode 100644 index 0000000000..f49a0bf7df --- /dev/null +++ b/library/cpp/yson/node/ut/ya.make @@ -0,0 +1,12 @@ +UNITTEST_FOR(library/cpp/yson/node) + +OWNER( + ermolovd + g:yt +) + +SRCS( + node_ut.cpp +) + +END() diff --git a/library/cpp/yson/node/ya.make b/library/cpp/yson/node/ya.make new file mode 100644 index 0000000000..a082b293c4 --- /dev/null +++ b/library/cpp/yson/node/ya.make @@ -0,0 +1,25 @@ +LIBRARY() + +GENERATE_ENUM_SERIALIZATION(node.h) + +PEERDIR( + library/cpp/yson + library/cpp/yson/json +) + +OWNER( + ermolovd + g:yt +) + +SRCS( + node.cpp + node_io.cpp + node_builder.cpp + node_visitor.cpp + serialize.cpp +) + +END() + +RECURSE_FOR_TESTS(ut) |