summaryrefslogtreecommitdiffstats
path: root/library/cpp
diff options
context:
space:
mode:
authorarkady-e1ppa <[email protected]>2024-12-21 06:57:47 +0300
committerarkady-e1ppa <[email protected]>2024-12-21 07:15:03 +0300
commitc70428eb35690f3db0034227f8b03cb8d276590e (patch)
treed1804d1a0b3a6329eae4639d81dd21f210d50fc7 /library/cpp
parent6a6b06be9df7960772be6acdcdf5e9507b43de18 (diff)
YT-21233: Introduce Convert(To/From)TextYsonString in library/cpp/yt/yson_string to be later used for error attribute serialization
commit_hash:f7632c9622c71e023148c9454a9889e9cac75ecd
Diffstat (limited to 'library/cpp')
-rw-r--r--library/cpp/yt/yson_string/convert.cpp416
-rw-r--r--library/cpp/yt/yson_string/convert.h101
2 files changed, 515 insertions, 2 deletions
diff --git a/library/cpp/yt/yson_string/convert.cpp b/library/cpp/yt/yson_string/convert.cpp
index 68241adb789..1beb7cc4c79 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 eedb0939e00..a51821fa301 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