aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorarkady-e1ppa <arkady-e1ppa@yandex-team.com>2024-12-21 06:57:47 +0300
committerarkady-e1ppa <arkady-e1ppa@yandex-team.com>2024-12-21 07:15:03 +0300
commitc70428eb35690f3db0034227f8b03cb8d276590e (patch)
treed1804d1a0b3a6329eae4639d81dd21f210d50fc7
parent6a6b06be9df7960772be6acdcdf5e9507b43de18 (diff)
downloadydb-c70428eb35690f3db0034227f8b03cb8d276590e.tar.gz
YT-21233: Introduce Convert(To/From)TextYsonString in library/cpp/yt/yson_string to be later used for error attribute serialization
commit_hash:f7632c9622c71e023148c9454a9889e9cac75ecd
-rw-r--r--library/cpp/yt/yson_string/convert.cpp416
-rw-r--r--library/cpp/yt/yson_string/convert.h101
-rw-r--r--yt/yt/core/yson/writer.cpp26
-rw-r--r--yt/yt/core/ytree/unittests/text_yson_convert_ut.cpp273
-rw-r--r--yt/yt/core/ytree/unittests/ya.make1
5 files changed, 790 insertions, 27 deletions
diff --git a/library/cpp/yt/yson_string/convert.cpp b/library/cpp/yt/yson_string/convert.cpp
index 68241adb78..1beb7cc4c7 100644
--- a/library/cpp/yt/yson_string/convert.cpp
+++ b/library/cpp/yt/yson_string/convert.cpp
@@ -11,12 +11,43 @@
#include <array>
+#include <util/string/escape.h>
+
#include <util/stream/mem.h>
namespace NYT::NYson {
////////////////////////////////////////////////////////////////////////////////
+namespace NDetail {
+
+size_t FloatToStringWithNanInf(double value, char* buf, size_t size)
+{
+ if (std::isfinite(value)) {
+ return FloatToString(value, buf, size);
+ }
+
+ static const TStringBuf nanLiteral = "%nan";
+ static const TStringBuf infLiteral = "%inf";
+ static const TStringBuf negativeInfLiteral = "%-inf";
+
+ TStringBuf str;
+ if (std::isnan(value)) {
+ str = nanLiteral;
+ } else if (std::isinf(value) && value > 0) {
+ str = infLiteral;
+ } else {
+ str = negativeInfLiteral;
+ }
+ YT_VERIFY(str.size() + 1 <= size);
+ ::memcpy(buf, str.data(), str.size() + 1);
+ return str.size();
+}
+
+} // namespace NDetail
+
+////////////////////////////////////////////////////////////////////////////////
+
template <>
TYsonString ConvertToYsonString<i8>(const i8& value)
{
@@ -385,4 +416,389 @@ TGuid ConvertFromYsonString<TGuid>(const TYsonStringBuf& str)
////////////////////////////////////////////////////////////////////////////////
+template <>
+TYsonString ConvertToTextYsonString<i8>(const i8& value)
+{
+ return ConvertToTextYsonString(static_cast<i64>(value));
+}
+
+template <>
+TYsonString ConvertToTextYsonString<i32>(const i32& value)
+{
+ return ConvertToTextYsonString(static_cast<i64>(value));
+}
+
+template <>
+TYsonString ConvertToTextYsonString<i64>(const i64& value)
+{
+ return TYsonString{::ToString(value)};
+}
+
+template <>
+TYsonString ConvertToTextYsonString<ui8>(const ui8& value)
+{
+ return ConvertToTextYsonString(static_cast<ui64>(value));
+}
+
+template <>
+TYsonString ConvertToTextYsonString<ui32>(const ui32& value)
+{
+ return ConvertToTextYsonString(static_cast<ui64>(value));
+}
+
+template <>
+TYsonString ConvertToTextYsonString<ui64>(const ui64& value)
+{
+ return TYsonString{::ToString(value) + 'u'};
+}
+
+template <>
+TYsonString ConvertToTextYsonString<TString>(const TString& value)
+{
+ return ConvertToTextYsonString(TStringBuf(value));
+}
+
+template <>
+TYsonString ConvertToTextYsonString<std::string>(const std::string& value)
+{
+ return ConvertToTextYsonString(TStringBuf(value));
+}
+
+template <>
+TYsonString ConvertToTextYsonString<TStringBuf>(const TStringBuf& value)
+{
+ return TYsonString(NYT::Format("\"%v\"", ::EscapeC(value)));
+}
+
+template <>
+TYsonString ConvertToTextYsonString<std::string_view>(const std::string_view& value)
+{
+ return ConvertToTextYsonString(TStringBuf(value));
+}
+
+TYsonString ConvertToTextYsonString(const char* value)
+{
+ return ConvertToTextYsonString(TStringBuf(value));
+}
+
+template <>
+TYsonString ConvertToTextYsonString<float>(const float& value)
+{
+ return ConvertToTextYsonString(static_cast<double>(value));
+}
+
+template <>
+TYsonString ConvertToTextYsonString<double>(const double& value)
+{
+ char buf[256];
+ auto str = TStringBuf(buf, NDetail::FloatToStringWithNanInf(value, buf, sizeof(buf)));
+ auto ret = NYT::Format(
+ "%v%v",
+ str,
+ MakeFormatterWrapper([&] (TStringBuilderBase* builder) {
+ if (str.find('.') == TString::npos && str.find('e') == TString::npos && std::isfinite(value)) {
+ builder->AppendChar('.');
+ }
+ }));
+ return TYsonString(std::move(ret));
+}
+
+template <>
+TYsonString ConvertToTextYsonString<bool>(const bool& value)
+{
+ return value
+ ? TYsonString(TStringBuf("%true"))
+ : TYsonString(TStringBuf("%false"));
+}
+
+template <>
+TYsonString ConvertToTextYsonString<TInstant>(const TInstant& value)
+{
+ return ConvertToTextYsonString(value.ToString());
+}
+
+template <>
+TYsonString ConvertToTextYsonString<TDuration>(const TDuration& value)
+{
+ // ConvertTo does unchecked cast to i64 :(.
+ return ConvertToTextYsonString(static_cast<i64>(value.MilliSeconds()));
+}
+
+template <>
+TYsonString ConvertToTextYsonString<TGuid>(const TGuid& value)
+{
+ return ConvertToTextYsonString(NYT::ToString(value));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+namespace {
+
+template <class TSomeInt>
+TSomeInt ReadTextUint(TStringBuf strBuf)
+{
+ // Drop 'u'
+ return ::FromString<TSomeInt>(TStringBuf{strBuf.data(), strBuf.length() - 1});
+}
+
+template <class TSomeInt>
+TSomeInt ReadTextInt(TStringBuf strBuf)
+{
+ return ::FromString<TSomeInt>(TStringBuf{strBuf.data(), strBuf.length()});
+}
+
+bool IsNumeric(TStringBuf strBuf)
+{
+ bool isNumeric = true;
+ bool isNegative = false;
+ for (int i = 0; i < std::ssize(strBuf); ++i) {
+ char c = strBuf[i];
+
+ if (!('0' <= c && c <= '9')) {
+ if (i == 0 && c == '-') {
+ isNegative = true;
+ continue;
+ }
+ if (i == std::ssize(strBuf) - 1 && c == 'u' && !isNegative) {
+ continue;
+ }
+ isNumeric = false;
+ break;
+ }
+ }
+
+ return isNumeric;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+template <class TSomeInt>
+TSomeInt ParseSomeIntFromTextYsonString(const TYsonStringBuf& str)
+{
+ YT_ASSERT(str.GetType() == EYsonType::Node);
+ auto strBuf = str.AsStringBuf();
+
+ if (std::ssize(strBuf) == 0 || !IsNumeric(strBuf)) {
+ throw TYsonLiteralParseException(NYT::Format(
+ "Unexpected %v\n"
+ "Value is not numeric",
+ strBuf));
+ }
+
+ if (strBuf.back() == 'u') {
+ // Drop 'u'
+ return ReadTextUint<TSomeInt>(strBuf);
+ } else {
+ return ReadTextInt<TSomeInt>(strBuf);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+TString DoParseStringFromTextYson(TStringBuf strBuf)
+{
+ // Remove quotation marks.
+ return ::UnescapeC(TStringBuf{strBuf.data() + 1, strBuf.length() - 2});
+}
+
+TString ParseStringFromTextYsonString(const TYsonStringBuf& str)
+{
+ YT_ASSERT(str.GetType() == EYsonType::Node);
+ auto strBuf = str.AsStringBuf();
+ if (std::ssize(strBuf) < 2 || strBuf.front() != '\"' || strBuf.back() != '\"') {
+ throw TYsonLiteralParseException(Format(
+ "Unexpected %v\n"
+ "Text yson string must begin and end with \\\"",
+ strBuf));
+ }
+ return DoParseStringFromTextYson(strBuf);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+double ParseDoubleFromTextYsonString(const TYsonStringBuf& str)
+{
+ YT_ASSERT(str.GetType() == EYsonType::Node);
+ auto strBuf = str.AsStringBuf();
+
+ if (std::ssize(strBuf) < 2) {
+ throw TYsonLiteralParseException(Format(
+ "Incorrect remaining string length: expected at least 2, got %v",
+ std::ssize(strBuf)));
+ }
+
+ // Check special values first.
+ // %nan
+ // %inf, %+inf, %-inf
+ if (strBuf[0] == '%') {
+ switch (strBuf[1]) {
+ case '+':
+ case 'i':
+ return std::numeric_limits<double>::infinity();
+
+ case '-':
+ return -std::numeric_limits<double>::infinity();
+
+ case 'n':
+ return std::numeric_limits<double>::quiet_NaN();
+
+ default:
+ throw TYsonLiteralParseException(Format(
+ "Incorrect %%-literal %v",
+ strBuf));
+ }
+ }
+
+ return ::FromString<double>(strBuf);
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+
+#define PARSE_INT(type, underlyingType) \
+ template <> \
+ type ConvertFromTextYsonString<type>(const TYsonStringBuf& str) \
+ { \
+ try { \
+ return CheckedIntegralCast<type>(ParseSomeIntFromTextYsonString<underlyingType>(str)); \
+ } catch (const std::exception& ex) { \
+ throw TYsonLiteralParseException(ex, "Error parsing \"" #type "\" value from YSON"); \
+ } \
+ }
+
+PARSE_INT(i8, i64)
+PARSE_INT(i16, i64)
+PARSE_INT(i32, i64)
+PARSE_INT(i64, i64)
+PARSE_INT(ui8, ui64)
+PARSE_INT(ui16, ui64)
+PARSE_INT(ui32, ui64)
+PARSE_INT(ui64, ui64)
+
+#undef PARSE
+
+template <>
+TString ConvertFromTextYsonString<TString>(const TYsonStringBuf& str)
+{
+ try {
+ return ParseStringFromTextYsonString(str);
+ } catch (const std::exception& ex) {
+ throw TYsonLiteralParseException(ex, "Error parsing \"string\" value from YSON");
+ }
+}
+
+template <>
+std::string ConvertFromTextYsonString<std::string>(const TYsonStringBuf& str)
+{
+ return std::string{ConvertFromTextYsonString<TString>(str)};
+}
+
+template <>
+float ConvertFromTextYsonString<float>(const TYsonStringBuf& str)
+{
+ try {
+ return static_cast<float>(ParseDoubleFromTextYsonString(str));
+ } catch (const std::exception& ex) {
+ throw TYsonLiteralParseException(ex, "Error parsing \"float\" value from YSON");
+ }
+}
+
+template <>
+double ConvertFromTextYsonString<double>(const TYsonStringBuf& str)
+{
+ try {
+ return ParseDoubleFromTextYsonString(str);
+ } catch (const std::exception& ex) {
+ throw TYsonLiteralParseException(ex, "Error parsing \"double\" value from YSON");
+ }
+}
+
+template <>
+bool ConvertFromTextYsonString<bool>(const TYsonStringBuf& str)
+{
+ try {
+ YT_ASSERT(str.GetType() == EYsonType::Node);
+ auto strBuf = str.AsStringBuf();
+
+ if (std::ssize(strBuf) == 0) {
+ throw TYsonLiteralParseException("Empty string");
+ }
+
+ char ch = strBuf.front();
+
+ if (ch == '%') {
+ if (strBuf != "%true" && strBuf != "%false") {
+ throw TYsonLiteralParseException(Format(
+ "Expected %%true or %%false but found %v",
+ strBuf));
+ }
+ return strBuf == "%true";
+ }
+
+ if (ch == '\"') {
+ return ParseBool(DoParseStringFromTextYson(strBuf));
+ }
+
+ // NB(arkady-e1ppa): This check is linear in size(strBuf)
+ // And thus is tried as the last resort.
+ if (IsNumeric(strBuf)) {
+ auto checkValue = [&] (const auto& functor) {
+ auto value = functor(strBuf);
+ if (value != 0 && value != 1) {
+ throw TYsonLiteralParseException(Format(
+ "Expected 0 or 1 but found %v",
+ value));
+ }
+ return static_cast<bool>(value);
+ };
+
+ if (strBuf.back() == 'u') {
+ return checkValue(&ReadTextUint<ui64>);
+ } else {
+ return checkValue(&ReadTextInt<i64>);
+ }
+ }
+
+ throw TYsonLiteralParseException(Format(
+ "Unexpected %v\n"
+ "No known conversion to \"boolean\" value",
+ strBuf));
+ } catch (const std::exception& ex) {
+ throw TYsonLiteralParseException(ex, "Error parsing \"boolean\" value from YSON");
+ }
+}
+
+template <>
+TInstant ConvertFromTextYsonString<TInstant>(const TYsonStringBuf& str)
+{
+ try {
+ return TInstant::ParseIso8601(ParseStringFromTextYsonString(str));
+ } catch (const std::exception& ex) {
+ throw TYsonLiteralParseException(ex, "Error parsing \"instant\" value from YSON");
+ }
+}
+
+template <>
+TDuration ConvertFromTextYsonString<TDuration>(const TYsonStringBuf& str)
+{
+ try {
+ return TDuration::MilliSeconds(ParseSomeIntFromTextYsonString<i64>(str));
+ } catch (const std::exception& ex) {
+ throw TYsonLiteralParseException(ex, "Error parsing \"duration\" value from YSON");
+ }
+}
+
+template <>
+TGuid ConvertFromTextYsonString<TGuid>(const TYsonStringBuf& str)
+{
+ try {
+ return TGuid::FromString(ParseStringFromTextYsonString(str));
+ } catch (const std::exception& ex) {
+ throw TYsonLiteralParseException(ex, "Error parsing \"guid\" value from YSON");
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
} // namespace NYT::NYson
diff --git a/library/cpp/yt/yson_string/convert.h b/library/cpp/yt/yson_string/convert.h
index eedb0939e0..a51821fa30 100644
--- a/library/cpp/yt/yson_string/convert.h
+++ b/library/cpp/yt/yson_string/convert.h
@@ -13,6 +13,15 @@
namespace NYT::NYson {
////////////////////////////////////////////////////////////////////////////////
+
+namespace NDetail {
+
+size_t FloatToStringWithNanInf(double value, char* buf, size_t size);
+
+} // namespace NDetail
+
+////////////////////////////////////////////////////////////////////////////////
+
// Generic forward declarations.
template <class T>
@@ -24,6 +33,13 @@ TYsonString ConvertToYsonString(const T& value, EYsonFormat format);
template <class T>
T ConvertFromYsonString(const TYsonStringBuf& str);
+// TODO(arkady-e1ppa): Move those to library/cpp/yt/error
+// and swap to std::string(_view) to drop dep on library/cpp/yson_string.
+template <class T>
+TYsonString ConvertToTextYsonString(const T& value) = delete;
+template <class T>
+T ConvertFromTextYsonString(const TYsonStringBuf& str) = delete;
+
////////////////////////////////////////////////////////////////////////////////
// Basic specializations for ConvertToYsonString.
@@ -44,8 +60,6 @@ TYsonString ConvertToYsonString<ui64>(const ui64& value);
template <>
TYsonString ConvertToYsonString<TString>(const TString& value);
template <>
-TYsonString ConvertToYsonString<std::string>(const std::string& value);
-template <>
TYsonString ConvertToYsonString<TStringBuf>(const TStringBuf& value);
TYsonString ConvertToYsonString(const char* value);
@@ -93,6 +107,8 @@ ui64 ConvertFromYsonString<ui64>(const TYsonStringBuf& str);
template <>
TString ConvertFromYsonString<TString>(const TYsonStringBuf& str);
+template <>
+std::string ConvertFromYsonString<std::string>(const TYsonStringBuf& str);
template <>
float ConvertFromYsonString<float>(const TYsonStringBuf& str);
@@ -113,4 +129,85 @@ TGuid ConvertFromYsonString<TGuid>(const TYsonStringBuf& str);
////////////////////////////////////////////////////////////////////////////////
+template <>
+TYsonString ConvertToTextYsonString<i8>(const i8& value);
+template <>
+TYsonString ConvertToTextYsonString<i32>(const i32& value);
+template <>
+TYsonString ConvertToTextYsonString<i64>(const i64& value);
+
+template <>
+TYsonString ConvertToTextYsonString<ui8>(const ui8& value);
+template <>
+TYsonString ConvertToTextYsonString<ui32>(const ui32& value);
+template <>
+TYsonString ConvertToTextYsonString<ui64>(const ui64& value);
+
+template <>
+TYsonString ConvertToTextYsonString<TString>(const TString& value);
+template <>
+TYsonString ConvertToTextYsonString<std::string>(const std::string& value);
+template <>
+TYsonString ConvertToTextYsonString<TStringBuf>(const TStringBuf& value);
+template <>
+TYsonString ConvertToTextYsonString<std::string_view>(const std::string_view& value);
+TYsonString ConvertToTextYsonString(const char* value);
+
+template <>
+TYsonString ConvertToTextYsonString<float>(const float& value);
+template <>
+TYsonString ConvertToTextYsonString<double>(const double& value);
+
+template <>
+TYsonString ConvertToTextYsonString<bool>(const bool& value);
+
+template <>
+TYsonString ConvertToTextYsonString<TInstant>(const TInstant& value);
+
+template <>
+TYsonString ConvertToTextYsonString<TDuration>(const TDuration& value);
+
+template <>
+TYsonString ConvertToTextYsonString<TGuid>(const TGuid& value);
+
+////////////////////////////////////////////////////////////////////////////////
+
+template <>
+i8 ConvertFromTextYsonString<i8>(const TYsonStringBuf& str);
+template <>
+i32 ConvertFromTextYsonString<i32>(const TYsonStringBuf& str);
+template <>
+i64 ConvertFromTextYsonString<i64>(const TYsonStringBuf& str);
+
+template <>
+ui8 ConvertFromTextYsonString<ui8>(const TYsonStringBuf& str);
+template <>
+ui32 ConvertFromTextYsonString<ui32>(const TYsonStringBuf& str);
+template <>
+ui64 ConvertFromTextYsonString<ui64>(const TYsonStringBuf& str);
+
+template <>
+TString ConvertFromTextYsonString<TString>(const TYsonStringBuf& str);
+template <>
+std::string ConvertFromTextYsonString<std::string>(const TYsonStringBuf& str);
+
+template <>
+float ConvertFromTextYsonString<float>(const TYsonStringBuf& str);
+template <>
+double ConvertFromTextYsonString<double>(const TYsonStringBuf& str);
+
+template <>
+bool ConvertFromTextYsonString<bool>(const TYsonStringBuf& str);
+
+template <>
+TInstant ConvertFromTextYsonString<TInstant>(const TYsonStringBuf& str);
+
+template <>
+TDuration ConvertFromTextYsonString<TDuration>(const TYsonStringBuf& str);
+
+template <>
+TGuid ConvertFromTextYsonString<TGuid>(const TYsonStringBuf& str);
+
+////////////////////////////////////////////////////////////////////////////////
+
} // namespace NYT::NYson
diff --git a/yt/yt/core/yson/writer.cpp b/yt/yt/core/yson/writer.cpp
index d31abf0b46..32e3f1636c 100644
--- a/yt/yt/core/yson/writer.cpp
+++ b/yt/yt/core/yson/writer.cpp
@@ -132,30 +132,6 @@ void WriteUtf8String(const char* str, size_t len, IOutputStream& output)
}
}
-size_t FloatToStringWithNanInf(double value, char* buf, size_t size)
-{
- if (std::isfinite(value)) {
- return FloatToString(value, buf, size);
- }
-
- static const TStringBuf nanLiteral = "%nan";
- static const TStringBuf infLiteral = "%inf";
- static const TStringBuf negativeInfLiteral = "%-inf";
-
- TStringBuf str;
- if (std::isnan(value)) {
- str = nanLiteral;
- } else if (std::isinf(value) && value > 0) {
- str = infLiteral;
- } else {
- str = negativeInfLiteral;
- }
- YT_VERIFY(str.size() + 1 <= size);
- ::memcpy(buf, str.data(), str.size() + 1);
- return str.size();
-}
-
-
} // namespace
////////////////////////////////////////////////////////////////////////////////
@@ -277,7 +253,7 @@ void TYsonWriter::OnDoubleScalar(double value)
Stream_->Write(&value, sizeof(double));
} else {
char buf[256];
- auto str = TStringBuf(buf, FloatToStringWithNanInf(value, buf, sizeof(buf)));
+ auto str = TStringBuf(buf, NDetail::FloatToStringWithNanInf(value, buf, sizeof(buf)));
Stream_->Write(str);
if (str.find('.') == TString::npos && str.find('e') == TString::npos && std::isfinite(value)) {
Stream_->Write(".");
diff --git a/yt/yt/core/ytree/unittests/text_yson_convert_ut.cpp b/yt/yt/core/ytree/unittests/text_yson_convert_ut.cpp
new file mode 100644
index 0000000000..75913bed0b
--- /dev/null
+++ b/yt/yt/core/ytree/unittests/text_yson_convert_ut.cpp
@@ -0,0 +1,273 @@
+#include <yt/yt/core/test_framework/framework.h>
+
+#include <yt/yt/core/ytree/convert.h>
+
+#include <library/cpp/yt/misc/source_location.h>
+
+#include <library/cpp/yt/yson_string/convert.h>
+
+namespace NYT::NYTree {
+namespace {
+
+using namespace NYson;
+
+////////////////////////////////////////////////////////////////////////////////
+
+template <class T>
+void CheckEqualConversionToTextYson(const T& value, const TSourceLocation& loc = YT_CURRENT_SOURCE_LOCATION)
+{
+ EXPECT_EQ(ConvertToTextYsonString(value).AsStringBuf(), ConvertToYsonString(value, EYsonFormat::Text).AsStringBuf())
+ << NYT::Format("At %v", loc);
+}
+
+template <class T, class U>
+void CheckEqualConversionToFromTextYson(const U& value, const TSourceLocation& loc = YT_CURRENT_SOURCE_LOCATION)
+{
+ auto yson = ConvertToTextYsonString(value);
+ EXPECT_EQ(ConvertFromTextYsonString<T>(yson), ConvertTo<T>(yson))
+ << NYT::Format("At %v", loc);
+}
+
+template <class T>
+void CheckEqualConversionFromTextYson(TStringBuf value, const TSourceLocation& loc = YT_CURRENT_SOURCE_LOCATION)
+{
+ NYson::TYsonString yson(value);
+ EXPECT_EQ(ConvertTo<T>(yson), ConvertTo<T>(yson))
+ << NYT::Format("At %v", loc);
+}
+
+TEST(TTextYsonConvertTest, ConvertToTextIntegrals)
+{
+ CheckEqualConversionToTextYson<i8>(+14);
+ CheckEqualConversionToTextYson<i8>(0);
+ CheckEqualConversionToTextYson<i8>(-15);
+ CheckEqualConversionToTextYson<i32>(+100);
+ CheckEqualConversionToTextYson<i32>(0);
+ CheckEqualConversionToTextYson<i32>(-123);
+ CheckEqualConversionToTextYson<i64>(+100);
+ CheckEqualConversionToTextYson<i64>(0);
+ CheckEqualConversionToTextYson<i64>(-123);
+
+ CheckEqualConversionToTextYson<ui8>(+100);
+ CheckEqualConversionToTextYson<ui8>(0);
+ CheckEqualConversionToTextYson<ui32>(+100);
+ CheckEqualConversionToTextYson<ui32>(0);
+ CheckEqualConversionToTextYson<ui64>(+100);
+ CheckEqualConversionToTextYson<ui64>(0);
+}
+
+TEST(TTextYsonConvertTest, ConvertToTextIntegralsLimits)
+{
+ CheckEqualConversionToTextYson<i64>(std::numeric_limits<i64>::max());
+ CheckEqualConversionToTextYson<i64>(std::numeric_limits<i64>::min());
+
+ CheckEqualConversionToTextYson<ui64>(std::numeric_limits<ui64>::max());
+ CheckEqualConversionToTextYson<ui64>(std::numeric_limits<ui64>::min());
+}
+
+TEST(TTextYsonConvertTest, ConvertToTextFloats)
+{
+ CheckEqualConversionToTextYson<float>(0.0);
+ CheckEqualConversionToTextYson<float>(-0.0);
+ CheckEqualConversionToTextYson<float>(-7.7777);
+ CheckEqualConversionToTextYson<float>(+9.243);
+
+ CheckEqualConversionToTextYson<double>(0.0);
+ CheckEqualConversionToTextYson<double>(-0.0);
+ CheckEqualConversionToTextYson<double>(-7.7777);
+ CheckEqualConversionToTextYson<double>(+9.243);
+}
+
+TEST(TTextYsonConvertTest, ConvertToTextFloatsSpecialValues)
+{
+ CheckEqualConversionToTextYson<double>(std::numeric_limits<double>::min());
+ CheckEqualConversionToTextYson<double>(std::numeric_limits<double>::max());
+ CheckEqualConversionToTextYson<double>(std::numeric_limits<double>::infinity());
+ CheckEqualConversionToTextYson<double>(-std::numeric_limits<double>::infinity());
+ CheckEqualConversionToTextYson<double>(std::numeric_limits<double>::quiet_NaN());
+}
+
+TEST(TTextYsonConvertTest, ConvertToTextOtherPrimitiveTypes)
+{
+ CheckEqualConversionToTextYson<bool>(true);
+ CheckEqualConversionToTextYson<bool>(false);
+
+ CheckEqualConversionToTextYson<TInstant>(TInstant::Now());
+ CheckEqualConversionToTextYson<TInstant>(TInstant::Zero());
+ CheckEqualConversionToTextYson<TInstant>(TInstant::FromValue(42));
+
+ CheckEqualConversionToTextYson<TDuration>(TDuration::Zero());
+ CheckEqualConversionToTextYson<TDuration>(TDuration::Seconds(2));
+ CheckEqualConversionToTextYson<TDuration>(TDuration::MilliSeconds(123));
+ CheckEqualConversionToTextYson<TDuration>(TDuration::MicroSeconds(12));
+
+ CheckEqualConversionToTextYson<std::string>("Hello, world!");
+ CheckEqualConversionToTextYson<std::string>("This is a so-called \"quotation marks\" test");
+ CheckEqualConversionToTextYson<std::string>("This tests \r other \b hidden symbols \n");
+ CheckEqualConversionToTextYson<std::string>("And this one tests special numbers numbers \x012");
+
+ CheckEqualConversionToTextYson<TGuid>(TGuid::Create());
+}
+
+TEST(TTextYsonConvertTest, ConvertFromTextIntegrals)
+{
+ CheckEqualConversionToFromTextYson<i8>(+15);
+ CheckEqualConversionToFromTextYson<i8>(0);
+ CheckEqualConversionToFromTextYson<i8>(-15);
+ CheckEqualConversionToFromTextYson<i32>(+100);
+ CheckEqualConversionToFromTextYson<i32>(0);
+ CheckEqualConversionToFromTextYson<i32>(-123);
+ CheckEqualConversionToFromTextYson<i64>(+100);
+ CheckEqualConversionToFromTextYson<i64>(0);
+ CheckEqualConversionToFromTextYson<i64>(-123);
+
+ CheckEqualConversionToFromTextYson<ui8>(+100);
+ CheckEqualConversionToFromTextYson<ui8>(0);
+ CheckEqualConversionToFromTextYson<ui32>(+100);
+ CheckEqualConversionToFromTextYson<ui32>(0);
+ CheckEqualConversionToFromTextYson<ui64>(+100);
+ CheckEqualConversionToFromTextYson<ui64>(0);
+}
+
+TEST(TTextYsonConvertTest, ConvertFromTextIntegralsLimits)
+{
+ CheckEqualConversionToFromTextYson<i64>(std::numeric_limits<i64>::max());
+ CheckEqualConversionToFromTextYson<i64>(std::numeric_limits<i64>::min());
+
+ CheckEqualConversionToFromTextYson<ui64>(std::numeric_limits<ui64>::max());
+ CheckEqualConversionToFromTextYson<ui64>(std::numeric_limits<ui64>::min());
+}
+
+TEST(TTextYsonConvertTest, ConvertFromTextFloats)
+{
+ CheckEqualConversionToFromTextYson<double>(0.0);
+ CheckEqualConversionToFromTextYson<double>(-0.0);
+ CheckEqualConversionToFromTextYson<double>(-7.7777);
+ CheckEqualConversionToFromTextYson<double>(+9.243);
+}
+
+TEST(TTextYsonConvertTest, ConvertFromTextFloatsSpecialValues)
+{
+ CheckEqualConversionToFromTextYson<double>(std::numeric_limits<double>::min());
+ CheckEqualConversionToFromTextYson<double>(std::numeric_limits<double>::max());
+ CheckEqualConversionToFromTextYson<double>(std::numeric_limits<double>::infinity());
+ CheckEqualConversionToFromTextYson<double>(-std::numeric_limits<double>::infinity());
+
+ // nans do not compare.
+ // CheckEqualConversionFromTextYson<double>(std::numeric_limits<double>::quiet_NaN());
+}
+
+TEST(TTextYsonConvertTest, ConvertFromTextOtherPrimitiveTypes)
+{
+ CheckEqualConversionToTextYson<bool>(true);
+ CheckEqualConversionToTextYson<bool>(false);
+ CheckEqualConversionToTextYson<bool>("true");
+ CheckEqualConversionToTextYson<bool>("false");
+ CheckEqualConversionToTextYson<bool>("0");
+ CheckEqualConversionToTextYson<bool>("1");
+
+ CheckEqualConversionToTextYson<TInstant>(TInstant::Now());
+ CheckEqualConversionToTextYson<TInstant>(TInstant::Zero());
+ CheckEqualConversionToTextYson<TInstant>(TInstant::FromValue(42));
+
+ CheckEqualConversionToTextYson<TDuration>(TDuration::Zero());
+ CheckEqualConversionToTextYson<TDuration>(TDuration::Seconds(2));
+ CheckEqualConversionToTextYson<TDuration>(TDuration::MilliSeconds(123));
+ CheckEqualConversionToTextYson<TDuration>(TDuration::MicroSeconds(12));
+
+ CheckEqualConversionToTextYson<std::string>("Hello, world!");
+ CheckEqualConversionToTextYson<std::string>("This is a so-called \"quotation marks\" test");
+ CheckEqualConversionToTextYson<std::string>("This tests \r other \b hidden symbols \n");
+ CheckEqualConversionToTextYson<std::string>("And this one tests special numbers numbers \x012");
+
+ CheckEqualConversionToTextYson<TGuid>(TGuid::Create());
+}
+
+TEST(TTextYsonConvertTest, ConvertFromTextIntegralsTypeMissmatch)
+{
+ CheckEqualConversionToFromTextYson<i8>(static_cast<ui64>(+100));
+ CheckEqualConversionToFromTextYson<i8>(static_cast<ui64>(0));
+ CheckEqualConversionToFromTextYson<i32>(static_cast<ui64>(+100));
+ CheckEqualConversionToFromTextYson<i32>(static_cast<ui64>(0));
+ CheckEqualConversionToFromTextYson<i64>(static_cast<ui64>(+100));
+ CheckEqualConversionToFromTextYson<i64>(static_cast<ui64>(0));
+}
+
+TEST(TTextYsonConvertTest, ConvertFromTextTypeMissmatch)
+{
+ CheckEqualConversionFromTextYson<bool>("%true");
+ CheckEqualConversionFromTextYson<bool>("%false");
+ CheckEqualConversionFromTextYson<bool>("1");
+ CheckEqualConversionFromTextYson<bool>("0");
+ CheckEqualConversionFromTextYson<bool>("-0");
+ CheckEqualConversionFromTextYson<bool>("1u");
+ CheckEqualConversionFromTextYson<bool>("0u");
+
+ CheckEqualConversionFromTextYson<bool>(ConvertToTextYsonString("true").AsStringBuf());
+ CheckEqualConversionFromTextYson<bool>(ConvertToTextYsonString("false").AsStringBuf());
+ CheckEqualConversionFromTextYson<bool>(ConvertToTextYsonString("1").AsStringBuf());
+ CheckEqualConversionFromTextYson<bool>(ConvertToTextYsonString("0").AsStringBuf());
+}
+
+TEST(TTextYsonConvertTest, ConvertFromTextYsonStringThrowBasicCases)
+{
+ auto fromPayload = [] (const auto& value) {
+ return NYson::TYsonString(TString(value));
+ };
+
+ // Overflow.
+ EXPECT_ANY_THROW(ConvertFromTextYsonString<i8>(fromPayload("123123123213")));
+ EXPECT_ANY_THROW(ConvertTo<i8>(fromPayload("123123123213")));
+
+ // Negative.
+ EXPECT_ANY_THROW(ConvertFromTextYsonString<ui64>(fromPayload("-123")));
+ EXPECT_ANY_THROW(ConvertTo<ui64>(fromPayload("-123")));
+
+ // Non-numeric.
+ EXPECT_ANY_THROW(ConvertFromTextYsonString<i64>(fromPayload("haha")));
+ EXPECT_ANY_THROW(ConvertFromTextYsonString<i64>(fromPayload("123qq")));
+ EXPECT_ANY_THROW(ConvertFromTextYsonString<i64>(fromPayload("-123u")));
+ EXPECT_ANY_THROW(ConvertTo<i64>(fromPayload("haha")));
+ EXPECT_ANY_THROW(ConvertTo<i64>(fromPayload("123qq")));
+ EXPECT_ANY_THROW(ConvertTo<i64>(fromPayload("-123u")));
+
+ // Big positive to bool
+ EXPECT_ANY_THROW(ConvertFromTextYsonString<bool>(fromPayload("42")));
+ EXPECT_ANY_THROW(ConvertTo<bool>(fromPayload("42")));
+
+ // Garbage to bool
+ EXPECT_ANY_THROW(ConvertFromTextYsonString<bool>(fromPayload("%falsse")));
+ EXPECT_ANY_THROW(ConvertTo<bool>(fromPayload("%falsse")));
+
+ // Wrong string to bool
+ EXPECT_ANY_THROW(ConvertFromTextYsonString<bool>(fromPayload("\"True\"")));
+ EXPECT_ANY_THROW(ConvertFromTextYsonString<bool>(fromPayload("\"False\"")));
+ EXPECT_ANY_THROW(ConvertFromTextYsonString<bool>(fromPayload("\"1u\"")));
+ EXPECT_ANY_THROW(ConvertFromTextYsonString<bool>(fromPayload("\"0u\"")));
+ EXPECT_ANY_THROW(ConvertTo<bool>(fromPayload("\"True\"")));
+ EXPECT_ANY_THROW(ConvertTo<bool>(fromPayload("\"False\"")));
+ EXPECT_ANY_THROW(ConvertTo<bool>(fromPayload("\"1u\"")));
+ EXPECT_ANY_THROW(ConvertTo<bool>(fromPayload("\"0u\"")));
+
+ // Wrong string to string
+ EXPECT_ANY_THROW(ConvertFromTextYsonString<std::string>(fromPayload("")));
+ EXPECT_ANY_THROW(ConvertFromTextYsonString<std::string>(fromPayload("\"")));
+ EXPECT_ANY_THROW(ConvertFromTextYsonString<std::string>(fromPayload("haha\"")));
+ EXPECT_ANY_THROW(ConvertFromTextYsonString<std::string>(fromPayload("\'oops\'")));
+ EXPECT_ANY_THROW(ConvertTo<std::string>(fromPayload("")));
+ EXPECT_ANY_THROW(ConvertTo<std::string>(fromPayload("\"")));
+ EXPECT_ANY_THROW(ConvertTo<std::string>(fromPayload("haha\"")));
+ EXPECT_ANY_THROW(ConvertTo<std::string>(fromPayload("\'oops\'")));
+
+ // Wrong literal to double
+ EXPECT_ANY_THROW(ConvertFromTextYsonString<double>(fromPayload("%%")));
+ EXPECT_ANY_THROW(ConvertFromTextYsonString<std::string>(fromPayload("%42inf")));
+ EXPECT_ANY_THROW(ConvertFromTextYsonString<std::string>(fromPayload("%NaaN")));
+ EXPECT_ANY_THROW(ConvertTo<double>(fromPayload("%%")));
+ EXPECT_ANY_THROW(ConvertTo<std::string>(fromPayload("%42inf")));
+ EXPECT_ANY_THROW(ConvertTo<std::string>(fromPayload("%NaaN")));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+} // namespace
+} // namespace NYT::NYTree
diff --git a/yt/yt/core/ytree/unittests/ya.make b/yt/yt/core/ytree/unittests/ya.make
index 7196cea98c..1bc54b95aa 100644
--- a/yt/yt/core/ytree/unittests/ya.make
+++ b/yt/yt/core/ytree/unittests/ya.make
@@ -10,6 +10,7 @@ SRCS(
resolver_ut.cpp
serialize_ut.cpp
service_combiner_ut.cpp
+ text_yson_convert_ut.cpp
tree_builder_ut.cpp
lazy_ypath_service_ut.cpp
yson_schema_ut.cpp