diff options
author | arkady-e1ppa <arkady-e1ppa@yandex-team.com> | 2024-12-26 10:52:40 +0300 |
---|---|---|
committer | arkady-e1ppa <arkady-e1ppa@yandex-team.com> | 2024-12-26 11:12:17 +0300 |
commit | 0328aa1f62630f14d06076175e2f3750969ac813 (patch) | |
tree | a2652fb95c5a603e15f1692b43b10e4bf036f084 | |
parent | b4473180e344fac1deb5f4e85fc681efe2c708de (diff) | |
download | ydb-0328aa1f62630f14d06076175e2f3750969ac813.tar.gz |
YT-21233: Drop dependency on yson in library/cpp/yt/error by switch to std::string everywhere
done
commit_hash:8a83afa39917ba66a5161388a7cd74a4488d9908
-rw-r--r-- | library/cpp/yt/error/error.cpp | 38 | ||||
-rw-r--r-- | library/cpp/yt/error/error.h | 2 | ||||
-rw-r--r-- | library/cpp/yt/error/error_attribute-inl.h | 33 | ||||
-rw-r--r-- | library/cpp/yt/error/error_attribute.h | 15 | ||||
-rw-r--r-- | library/cpp/yt/error/error_attributes-inl.h | 18 | ||||
-rw-r--r-- | library/cpp/yt/error/error_attributes.cpp | 10 | ||||
-rw-r--r-- | library/cpp/yt/error/error_attributes.h | 4 | ||||
-rw-r--r-- | library/cpp/yt/error/origin_attributes.cpp | 15 | ||||
-rw-r--r-- | library/cpp/yt/error/text_yson.cpp | 436 | ||||
-rw-r--r-- | library/cpp/yt/error/text_yson.h | 104 | ||||
-rw-r--r-- | library/cpp/yt/error/ya.make | 2 | ||||
-rw-r--r-- | library/cpp/yt/yson_string/convert.cpp | 414 | ||||
-rw-r--r-- | library/cpp/yt/yson_string/convert.h | 96 | ||||
-rw-r--r-- | yt/yt/core/misc/error-inl.h | 4 | ||||
-rw-r--r-- | yt/yt/core/misc/error.cpp | 18 | ||||
-rw-r--r-- | yt/yt/core/yson/writer.cpp | 4 | ||||
-rw-r--r-- | yt/yt/core/ytree/convert-inl.h | 4 | ||||
-rw-r--r-- | yt/yt/core/ytree/unittests/text_yson_convert_ut.cpp | 65 |
18 files changed, 650 insertions, 632 deletions
diff --git a/library/cpp/yt/error/error.cpp b/library/cpp/yt/error/error.cpp index 59eea6ce8c..866c76eec8 100644 --- a/library/cpp/yt/error/error.cpp +++ b/library/cpp/yt/error/error.cpp @@ -18,8 +18,6 @@ namespace NYT { -using namespace NYson; - //////////////////////////////////////////////////////////////////////////////// void FormatValue(TStringBuilderBase* builder, TErrorCode code, TStringBuf spec) @@ -494,10 +492,8 @@ TError TError::Truncate( }; auto truncateAttributes = [stringLimit, &attributeWhitelist] (const TErrorAttributes& attributes, TErrorAttributes* mutableAttributes) { - for (const auto& key : attributes.ListKeys()) { - const auto& value = attributes.FindValue(key); - - if (std::ssize(value.AsStringBuf()) > stringLimit && !attributeWhitelist.contains(key)) { + for (const auto& [key, value] : attributes.ListPairs()) { + if (std::ssize(value) > stringLimit && !attributeWhitelist.contains(key)) { mutableAttributes->SetValue( key, NYT::ToErrorAttributeValue("...<attribute truncated>...")); @@ -559,8 +555,8 @@ TError TError::Truncate( }; auto truncateAttributes = [stringLimit, &attributeWhitelist] (TErrorAttributes* attributes) { - for (const auto& key : attributes->ListKeys()) { - if (std::ssize(attributes->FindValue(key).AsStringBuf()) > stringLimit && !attributeWhitelist.contains(key)) { + for (const auto& [key, value] : attributes->ListPairs()) { + if (std::ssize(value) > stringLimit && !attributeWhitelist.contains(key)) { attributes->SetValue( key, NYT::ToErrorAttributeValue("...<attribute truncated>...")); @@ -718,15 +714,15 @@ void AppendIndent(TStringBuilderBase* builer, int indent) builer->AppendChar(' ', indent); } -void AppendAttribute(TStringBuilderBase* builder, const TString& key, const TString& value, int indent) +void AppendAttribute(TStringBuilderBase* builder, const std::string& key, const std::string& value, int indent) { AppendIndent(builder, indent + 4); - if (!value.Contains('\n')) { + if (value.find('\n') == std::string::npos) { builder->AppendFormat("%-15s %s", key, value); } else { builder->AppendString(key); - TString indentedValue = "\n" + value; - SubstGlobal(indentedValue, "\n", "\n" + TString{static_cast<size_t>(indent + 8), ' '}); + std::string indentedValue = "\n" + value; + SubstGlobal(indentedValue, "\n", "\n" + std::string(static_cast<size_t>(indent + 8), ' ')); // Now first line in indentedValue is empty and every other line is indented by 8 spaces. builder->AppendString(indentedValue); } @@ -735,16 +731,16 @@ void AppendAttribute(TStringBuilderBase* builder, const TString& key, const TStr void AppendError(TStringBuilderBase* builder, const TError& error, int indent) { - auto isStringTextYson = [] (const NYson::TYsonString& str) { + auto isStringTextYson = [] (TStringBuf str) { return str && - std::ssize(str.AsStringBuf()) != 0 && - str.AsStringBuf().front() == '\"'; + std::ssize(str) != 0 && + str.front() == '\"'; }; - auto isBoolTextYson = [] (const NYson::TYsonString& str) { + auto isBoolTextYson = [] (TStringBuf str) { return - str.AsStringBuf() == "%false" || - str.AsStringBuf() == "%true"; + str == "%false" || + str == "%true"; }; if (error.IsOK()) { @@ -787,11 +783,11 @@ void AppendError(TStringBuilderBase* builder, const TError& error, int indent) for (const auto& [key, value] : error.Attributes().ListPairs()) { if (isStringTextYson(value)) { - AppendAttribute(builder, key, ConvertFromTextYsonString<TString>(value), indent); + AppendAttribute(builder, key, NDetail::ConvertFromTextYsonString<std::string>(value), indent); } else if (isBoolTextYson(value)) { - AppendAttribute(builder, key, TString{FormatBool(ConvertFromTextYsonString<bool>(value))}, indent); + AppendAttribute(builder, key, std::string(FormatBool(NDetail::ConvertFromTextYsonString<bool>(value))), indent); } else { - AppendAttribute(builder, key, value.ToString(), indent); + AppendAttribute(builder, key, value, indent); } } diff --git a/library/cpp/yt/error/error.h b/library/cpp/yt/error/error.h index eed1cfb75d..3def2c4b50 100644 --- a/library/cpp/yt/error/error.h +++ b/library/cpp/yt/error/error.h @@ -6,8 +6,6 @@ #include <library/cpp/yt/error/mergeable_dictionary.h> -#include <library/cpp/yt/yson/public.h> - #include <library/cpp/yt/yson_string/convert.h> #include <library/cpp/yt/yson_string/string.h> diff --git a/library/cpp/yt/error/error_attribute-inl.h b/library/cpp/yt/error/error_attribute-inl.h index df7696b85b..412b497128 100644 --- a/library/cpp/yt/error/error_attribute-inl.h +++ b/library/cpp/yt/error/error_attribute-inl.h @@ -4,8 +4,7 @@ #include "error_attribute.h" #endif -#include <library/cpp/yt/yson_string/convert.h> -#include <library/cpp/yt/yson_string/format.h> +#include "text_yson.h" namespace NYT { @@ -14,40 +13,22 @@ namespace NYT { namespace NAttributeValueConversionImpl { template <CPrimitiveConvertible T> -NYson::TYsonString TagInvoke(TTagInvokeTag<ToErrorAttributeValue>, const T& value) +std::string TagInvoke(TTagInvokeTag<ToErrorAttributeValue>, const T& value) { if constexpr (std::constructible_from<TStringBuf, const T&>) { - return NYson::ConvertToTextYsonString(TStringBuf(value)); + return NDetail::ConvertToTextYsonString(TStringBuf(value)); } else { - return NYson::ConvertToTextYsonString(value); + return NDetail::ConvertToTextYsonString(value); } } //////////////////////////////////////////////////////////////////////////////// -inline bool IsBinaryYson(const NYson::TYsonString& yson) -{ - using namespace NYson::NDetail; - - auto view = yson.AsStringBuf(); - return - std::ssize(view) != 0 && - (view.front() == EntitySymbol || - view.front() == StringMarker || - view.front() == Int64Marker || - view.front() == DoubleMarker || - view.front() == FalseMarker || - view.front() == TrueMarker || - view.front() == Uint64Marker); -} - -//////////////////////////////////////////////////////////////////////////////// - template <CPrimitiveConvertible T> -T TagInvoke(TFrom<T>, const NYson::TYsonString& value) +T TagInvoke(TFrom<T>, TStringBuf value) { - YT_VERIFY(!IsBinaryYson(value)); - return NYson::ConvertFromTextYsonString<T>(value); + YT_VERIFY(!NDetail::IsBinaryYson(value)); + return NDetail::ConvertFromTextYsonString<T>(value); } } // namespace NAttributeValueConversionImpl diff --git a/library/cpp/yt/error/error_attribute.h b/library/cpp/yt/error/error_attribute.h index 8b7a79004a..24ebf30ee7 100644 --- a/library/cpp/yt/error/error_attribute.h +++ b/library/cpp/yt/error/error_attribute.h @@ -5,8 +5,7 @@ #include <library/cpp/yt/misc/guid.h> #include <library/cpp/yt/misc/tag_invoke_cpo.h> -// TODO(arkady-e1ppa): Eliminate. -#include <library/cpp/yt/yson_string/string.h> +#include <string> namespace NYT { @@ -55,11 +54,11 @@ inline constexpr NAttributeValueConversionImpl::TFrom<U> FromErrorAttributeValue template <class T> concept CConvertibleToAttributeValue = requires (const T& value) { - { NYT::ToErrorAttributeValue(value) } -> std::same_as<NYson::TYsonString>; + { NYT::ToErrorAttributeValue(value) } -> std::same_as<std::string>; }; template <class T> -concept CConvertibleFromAttributeValue = requires (const NYson::TYsonString& value) { +concept CConvertibleFromAttributeValue = requires (TStringBuf value) { { NYT::FromErrorAttributeValue<T>(value) } -> std::same_as<T>; }; @@ -67,12 +66,8 @@ concept CConvertibleFromAttributeValue = requires (const NYson::TYsonString& val struct TErrorAttribute { - // TODO(arkady-e1ppa): Switch to std::string is quite possible - // however it requires patching IAttributeDictionary or - // switching it to the std::string first for interop reasons. - // Do that later. - using TKey = TString; - using TValue = NYson::TYsonString; + using TKey = std::string; + using TValue = std::string; template <CConvertibleToAttributeValue T> TErrorAttribute(const TKey& key, const T& value) diff --git a/library/cpp/yt/error/error_attributes-inl.h b/library/cpp/yt/error/error_attributes-inl.h index e6b39f531e..399c0792d4 100644 --- a/library/cpp/yt/error/error_attributes-inl.h +++ b/library/cpp/yt/error/error_attributes-inl.h @@ -12,9 +12,9 @@ template <class T> requires CConvertibleFromAttributeValue<T> T TErrorAttributes::Get(TStringBuf key) const { - auto yson = GetValue(key); + auto value = GetValue(key); try { - return NYT::FromErrorAttributeValue<T>(yson); + return NYT::FromErrorAttributeValue<T>(value); } catch (const std::exception& ex) { ThrowCannotParseAttributeException(key, ex); } @@ -29,7 +29,7 @@ typename TOptionalTraits<T>::TOptional TErrorAttributes::Find(TStringBuf key) co return typename TOptionalTraits<T>::TOptional(); } try { - return NYT::FromErrorAttributeValue<T>(value); + return NYT::FromErrorAttributeValue<T>(*value); } catch (const std::exception& ex) { ThrowCannotParseAttributeException(key, ex); } @@ -55,10 +55,10 @@ template <class T> requires CConvertibleFromAttributeValue<T> T TErrorAttributes::GetAndRemove(const TKey& key, const T& defaultValue) { - auto result = Find<T>(key); - if (result) { + auto value = Find<T>(key); + if (value) { Remove(key); - return *result; + return *value; } else { return defaultValue; } @@ -68,11 +68,11 @@ template <class T> requires CConvertibleFromAttributeValue<T> typename TOptionalTraits<T>::TOptional TErrorAttributes::FindAndRemove(const TKey& key) { - auto result = Find<T>(key); - if (result) { + auto value = Find<T>(key); + if (value) { Remove(key); } - return result; + return value; } template <CMergeableDictionary TDictionary> diff --git a/library/cpp/yt/error/error_attributes.cpp b/library/cpp/yt/error/error_attributes.cpp index 06db3b211e..fcfe80c414 100644 --- a/library/cpp/yt/error/error_attributes.cpp +++ b/library/cpp/yt/error/error_attributes.cpp @@ -9,7 +9,7 @@ namespace NYT { std::vector<TErrorAttributes::TKey> TErrorAttributes::ListKeys() const { - std::vector<TString> keys; + std::vector<TKey> keys; keys.reserve(Map_.size()); for (const auto& [key, value] : Map_) { keys.push_back(key); @@ -27,10 +27,12 @@ std::vector<TErrorAttributes::TKeyValuePair> TErrorAttributes::ListPairs() const return pairs; } -TErrorAttributes::TValue TErrorAttributes::FindValue(TStringBuf key) const +std::optional<TErrorAttribute::TValue> TErrorAttributes::FindValue(TStringBuf key) const { auto it = Map_.find(key); - return it == Map_.end() ? TValue{} : it->second; + return it == Map_.end() + ? std::nullopt + : std::optional(it->second); } void TErrorAttributes::SetValue(const TKey& key, const TValue& value) @@ -54,7 +56,7 @@ TErrorAttributes::TValue TErrorAttributes::GetValue(TStringBuf key) const if (!result) { ThrowNoSuchAttributeException(key); } - return result; + return *result; } void TErrorAttributes::Clear() diff --git a/library/cpp/yt/error/error_attributes.h b/library/cpp/yt/error/error_attributes.h index da71af63cc..8ab6388ef3 100644 --- a/library/cpp/yt/error/error_attributes.h +++ b/library/cpp/yt/error/error_attributes.h @@ -74,7 +74,7 @@ public: void MergeFrom(const TDictionary& dict); private: - THashMap<TKey, TValue> Map_; + THashMap<TKey, TValue, THash<TStringBuf>, TEqualTo<TStringBuf>> Map_; friend class TErrorOr<void>; TErrorAttributes() = default; @@ -86,7 +86,7 @@ private: TErrorAttributes& operator= (TErrorAttributes&& other) = default; //! Returns the value of the attribute (null indicates that the attribute is not found). - TValue FindValue(TStringBuf key) const; + std::optional<TValue> FindValue(TStringBuf key) const; //! Returns the value of the attribute (throws an exception if the attribute is not found). TValue GetValue(TStringBuf key) const; diff --git a/library/cpp/yt/error/origin_attributes.cpp b/library/cpp/yt/error/origin_attributes.cpp index 6f86e31ae8..2abb060ce2 100644 --- a/library/cpp/yt/error/origin_attributes.cpp +++ b/library/cpp/yt/error/origin_attributes.cpp @@ -128,22 +128,21 @@ TOriginAttributes ExtractFromDictionaryDefault(TErrorAttributes* attributes) return result; } - // TODO(arkady-e1ppa): Try using std::string here. - static const TString HostKey("host"); - result.HostHolder = TSharedRef::FromString(attributes->GetAndRemove(HostKey, TString())); + static const std::string HostKey("host"); + result.HostHolder = TSharedRef::FromString(attributes->GetAndRemove(HostKey, std::string())); result.Host = result.HostHolder.empty() ? TStringBuf() : TStringBuf(result.HostHolder.Begin(), result.HostHolder.End()); - static const TString DatetimeKey("datetime"); + static const std::string DatetimeKey("datetime"); result.Datetime = attributes->GetAndRemove(DatetimeKey, TInstant()); - static const TString PidKey("pid"); + static const std::string PidKey("pid"); result.Pid = attributes->GetAndRemove(PidKey, TProcessId{}); - static const TString TidKey("tid"); + static const std::string TidKey("tid"); result.Tid = attributes->GetAndRemove(TidKey, NThreading::InvalidThreadId); - static const TString ThreadNameKey("thread"); - result.ThreadName = attributes->GetAndRemove<TString>(ThreadNameKey, TString()); + static const std::string ThreadNameKey("thread"); + result.ThreadName = TString(attributes->GetAndRemove(ThreadNameKey, std::string())); return result; } diff --git a/library/cpp/yt/error/text_yson.cpp b/library/cpp/yt/error/text_yson.cpp new file mode 100644 index 0000000000..42ad52bbcc --- /dev/null +++ b/library/cpp/yt/error/text_yson.cpp @@ -0,0 +1,436 @@ +#include "text_yson.h" + +#include "error.h" + +#include <library/cpp/yt/assert/assert.h> + +#include <library/cpp/yt/string/format.h> + +#include <library/cpp/yt/coding/varint.h> + +#include <library/cpp/yt/misc/cast.h> + +#include <array> + +#include <util/string/escape.h> + +#include <util/stream/mem.h> + +namespace NYT::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(); +} + +//////////////////////////////////////////////////////////////////////////////// + +// NB(arkady-e1ppa): Copied from library/cpp/yt/yson_string/format.h +// to avoid direct dependency on it. + +//! Indicates an entity. +constexpr char EntitySymbol = '#'; +//! Marks the beginning of a binary string literal. +constexpr char StringMarker = '\x01'; +//! Marks the beginning of a binary i64 literal. +constexpr char Int64Marker = '\x02'; +//! Marks the beginning of a binary double literal. +constexpr char DoubleMarker = '\x03'; +//! Marks |false| boolean value. +constexpr char FalseMarker = '\x04'; +//! Marks |true| boolean value. +constexpr char TrueMarker = '\x05'; +//! Marks the beginning of a binary ui64 literal. +constexpr char Uint64Marker = '\x06'; + +//////////////////////////////////////////////////////////////////////////////// + +bool IsBinaryYson(TStringBuf str) +{ + return + std::ssize(str) != 0 && + (str.front() == EntitySymbol || + str.front() == StringMarker || + str.front() == Int64Marker || + str.front() == DoubleMarker || + str.front() == FalseMarker || + str.front() == TrueMarker || + str.front() == Uint64Marker); +} + +//////////////////////////////////////////////////////////////////////////////// + +template <> +std::string ConvertToTextYsonString<i8>(const i8& value) +{ + return ConvertToTextYsonString(static_cast<i64>(value)); +} + +template <> +std::string ConvertToTextYsonString<i32>(const i32& value) +{ + return ConvertToTextYsonString(static_cast<i64>(value)); +} + +template <> +std::string ConvertToTextYsonString<i64>(const i64& value) +{ + return std::string{::ToString(value)}; +} + +template <> +std::string ConvertToTextYsonString<ui8>(const ui8& value) +{ + return ConvertToTextYsonString(static_cast<ui64>(value)); +} + +template <> +std::string ConvertToTextYsonString<ui32>(const ui32& value) +{ + return ConvertToTextYsonString(static_cast<ui64>(value)); +} + +template <> +std::string ConvertToTextYsonString<ui64>(const ui64& value) +{ + return std::string{::ToString(value) + 'u'}; +} + +template <> +std::string ConvertToTextYsonString<TStringBuf>(const TStringBuf& value) +{ + return std::string(NYT::Format("\"%v\"", ::EscapeC(value))); +} + +template <> +std::string ConvertToTextYsonString<float>(const float& value) +{ + return ConvertToTextYsonString(static_cast<double>(value)); +} + +template <> +std::string 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 std::string(std::move(ret)); +} + +template <> +std::string ConvertToTextYsonString<bool>(const bool& value) +{ + return value + ? std::string(TStringBuf("%true")) + : std::string(TStringBuf("%false")); +} + +template <> +std::string ConvertToTextYsonString<TInstant>(const TInstant& value) +{ + return ConvertToTextYsonString(TStringBuf(value.ToString())); +} + +template <> +std::string ConvertToTextYsonString<TDuration>(const TDuration& value) +{ + // ConvertTo does unchecked cast to i64 :(. + return ConvertToTextYsonString(static_cast<i64>(value.MilliSeconds())); +} + +template <> +std::string ConvertToTextYsonString<TGuid>(const TGuid& value) +{ + return ConvertToTextYsonString(TStringBuf(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(TStringBuf strBuf) +{ + if (std::ssize(strBuf) == 0 || !IsNumeric(strBuf)) { + THROW_ERROR_EXCEPTION( + "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(TStringBuf strBuf) +{ + if (std::ssize(strBuf) < 2 || strBuf.front() != '\"' || strBuf.back() != '\"') { + THROW_ERROR_EXCEPTION( + "Unexpected %v\n" + "Text yson string must begin and end with \\\"", + strBuf); + } + return DoParseStringFromTextYson(strBuf); +} + +//////////////////////////////////////////////////////////////////////////////// + +double ParseDoubleFromTextYsonString(TStringBuf strBuf) +{ + if (std::ssize(strBuf) < 2) { + THROW_ERROR_EXCEPTION( + "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_ERROR_EXCEPTION( + "Incorrect %%-literal %v", + strBuf); + } + } + + return ::FromString<double>(strBuf); +} + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// + +#define PARSE_INT(type, underlyingType) \ + template <> \ + type ConvertFromTextYsonString<type>(TStringBuf str) \ + { \ + try { \ + return CheckedIntegralCast<type>(ParseSomeIntFromTextYsonString<underlyingType>(str)); \ + } catch (const std::exception& ex) { \ + THROW_ERROR_EXCEPTION("Error parsing \"" #type "\" value from YSON") << ex; \ + } \ + } + +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>(TStringBuf str) +{ + try { + return ParseStringFromTextYsonString(str); + } catch (const std::exception& ex) { + THROW_ERROR_EXCEPTION("Error parsing \"string\" value from YSON") << ex; + } +} + +template <> +std::string ConvertFromTextYsonString<std::string>(TStringBuf str) +{ + return std::string(ConvertFromTextYsonString<TString>(str)); +} + +template <> +float ConvertFromTextYsonString<float>(TStringBuf str) +{ + try { + return static_cast<float>(ParseDoubleFromTextYsonString(str)); + } catch (const std::exception& ex) { + THROW_ERROR_EXCEPTION("Error parsing \"float\" value from YSON") << ex; + } +} + +template <> +double ConvertFromTextYsonString<double>(TStringBuf str) +{ + try { + return ParseDoubleFromTextYsonString(str); + } catch (const std::exception& ex) { + THROW_ERROR_EXCEPTION("Error parsing \"double\" value from YSON") << ex; + } +} + +template <> +bool ConvertFromTextYsonString<bool>(TStringBuf strBuf) +{ + try { + if (std::ssize(strBuf) == 0) { + THROW_ERROR_EXCEPTION("Empty string"); + } + + char ch = strBuf.front(); + + if (ch == '%') { + if (strBuf != "%true" && strBuf != "%false") { + THROW_ERROR_EXCEPTION( + "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_ERROR_EXCEPTION( + "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_ERROR_EXCEPTION( + "Unexpected %v\n" + "No known conversion to \"boolean\" value", + strBuf); + } catch (const std::exception& ex) { + THROW_ERROR_EXCEPTION("Error parsing \"boolean\" value from YSON") << ex; + } +} + +template <> +TInstant ConvertFromTextYsonString<TInstant>(TStringBuf str) +{ + try { + return TInstant::ParseIso8601(ParseStringFromTextYsonString(str)); + } catch (const std::exception& ex) { + THROW_ERROR_EXCEPTION("Error parsing \"instant\" value from YSON") << ex; + } +} + +template <> +TDuration ConvertFromTextYsonString<TDuration>(TStringBuf str) +{ + try { + return TDuration::MilliSeconds(ParseSomeIntFromTextYsonString<i64>(str)); + } catch (const std::exception& ex) { + THROW_ERROR_EXCEPTION("Error parsing \"duration\" value from YSON") << ex; + } +} + +template <> +TGuid ConvertFromTextYsonString<TGuid>(TStringBuf str) +{ + try { + return TGuid::FromString(ParseStringFromTextYsonString(str)); + } catch (const std::exception& ex) { + THROW_ERROR_EXCEPTION("Error parsing \"guid\" value from YSON") << ex; + } +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT::NDetail diff --git a/library/cpp/yt/error/text_yson.h b/library/cpp/yt/error/text_yson.h new file mode 100644 index 0000000000..efd3d562bf --- /dev/null +++ b/library/cpp/yt/error/text_yson.h @@ -0,0 +1,104 @@ +#pragma once + +#include <library/cpp/yt/misc/guid.h> + +#include <util/datetime/base.h> + +#include <util/generic/strbuf.h> + +#include <string> + +namespace NYT::NDetail { + +//////////////////////////////////////////////////////////////////////////////// + +size_t FloatToStringWithNanInf(double value, char* buf, size_t size); + +//////////////////////////////////////////////////////////////////////////////// + +bool IsBinaryYson(TStringBuf str); + +//////////////////////////////////////////////////////////////////////////////// + +template <class T> +std::string ConvertToTextYsonString(const T& value) = delete; +template <class T> +T ConvertFromTextYsonString(TStringBuf str) = delete; + +//////////////////////////////////////////////////////////////////////////////// + +template <> +std::string ConvertToTextYsonString<i8>(const i8& value); +template <> +std::string ConvertToTextYsonString<i32>(const i32& value); +template <> +std::string ConvertToTextYsonString<i64>(const i64& value); + +template <> +std::string ConvertToTextYsonString<ui8>(const ui8& value); +template <> +std::string ConvertToTextYsonString<ui32>(const ui32& value); +template <> +std::string ConvertToTextYsonString<ui64>(const ui64& value); + +template <> +std::string ConvertToTextYsonString<TStringBuf>(const TStringBuf& value); + +template <> +std::string ConvertToTextYsonString<float>(const float& value); +template <> +std::string ConvertToTextYsonString<double>(const double& value); + +template <> +std::string ConvertToTextYsonString<bool>(const bool& value); + +template <> +std::string ConvertToTextYsonString<TInstant>(const TInstant& value); + +template <> +std::string ConvertToTextYsonString<TDuration>(const TDuration& value); + +template <> +std::string ConvertToTextYsonString<TGuid>(const TGuid& value); + +//////////////////////////////////////////////////////////////////////////////// + +template <> +i8 ConvertFromTextYsonString<i8>(TStringBuf str); +template <> +i32 ConvertFromTextYsonString<i32>(TStringBuf str); +template <> +i64 ConvertFromTextYsonString<i64>(TStringBuf str); + +template <> +ui8 ConvertFromTextYsonString<ui8>(TStringBuf str); +template <> +ui32 ConvertFromTextYsonString<ui32>(TStringBuf str); +template <> +ui64 ConvertFromTextYsonString<ui64>(TStringBuf str); + +template <> +TString ConvertFromTextYsonString<TString>(TStringBuf str); +template <> +std::string ConvertFromTextYsonString<std::string>(TStringBuf str); + +template <> +float ConvertFromTextYsonString<float>(TStringBuf str); +template <> +double ConvertFromTextYsonString<double>(TStringBuf str); + +template <> +bool ConvertFromTextYsonString<bool>(TStringBuf str); + +template <> +TInstant ConvertFromTextYsonString<TInstant>(TStringBuf str); + +template <> +TDuration ConvertFromTextYsonString<TDuration>(TStringBuf str); + +template <> +TGuid ConvertFromTextYsonString<TGuid>(TStringBuf str); + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT::NDetail diff --git a/library/cpp/yt/error/ya.make b/library/cpp/yt/error/ya.make index ca4370ef9d..b6dcc07343 100644 --- a/library/cpp/yt/error/ya.make +++ b/library/cpp/yt/error/ya.make @@ -9,7 +9,6 @@ PEERDIR( library/cpp/yt/misc library/cpp/yt/threading library/cpp/yt/string - library/cpp/yt/yson_string # TODO(arkady-e1ppa): eliminate library/cpp/yt/logging # TODO(arkady-e1ppa): Consider logging error_code crashes to stderr and drop this dep. util @@ -20,6 +19,7 @@ SRCS( error_attributes.cpp error_code.cpp origin_attributes.cpp + text_yson.cpp ) END() diff --git a/library/cpp/yt/yson_string/convert.cpp b/library/cpp/yt/yson_string/convert.cpp index 1beb7cc4c7..ca34428290 100644 --- a/library/cpp/yt/yson_string/convert.cpp +++ b/library/cpp/yt/yson_string/convert.cpp @@ -19,35 +19,6 @@ 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) { @@ -416,389 +387,4 @@ 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 a51821fa30..3a46eb15a3 100644 --- a/library/cpp/yt/yson_string/convert.h +++ b/library/cpp/yt/yson_string/convert.h @@ -14,14 +14,6 @@ namespace NYT::NYson { //////////////////////////////////////////////////////////////////////////////// -namespace NDetail { - -size_t FloatToStringWithNanInf(double value, char* buf, size_t size); - -} // namespace NDetail - -//////////////////////////////////////////////////////////////////////////////// - // Generic forward declarations. template <class T> @@ -33,13 +25,6 @@ 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. @@ -129,85 +114,4 @@ 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/misc/error-inl.h b/yt/yt/core/misc/error-inl.h index f215fcd907..9359cd72f7 100644 --- a/yt/yt/core/misc/error-inl.h +++ b/yt/yt/core/misc/error-inl.h @@ -10,9 +10,9 @@ namespace NYT::NAttributeValueConversionImpl { template <class T> requires (!CPrimitiveConvertible<T>) -NYson::TYsonString TagInvoke(TTagInvokeTag<ToErrorAttributeValue>, const T& value) +std::string TagInvoke(TTagInvokeTag<ToErrorAttributeValue>, const T& value) { - return NYson::ConvertToYsonString(value, NYson::EYsonFormat::Text); + return std::string(NYson::ConvertToYsonString(value, NYson::EYsonFormat::Text).ToString()); } //////////////////////////////////////////////////////////////////////////////// diff --git a/yt/yt/core/misc/error.cpp b/yt/yt/core/misc/error.cpp index 18394636d5..012f301a6b 100644 --- a/yt/yt/core/misc/error.cpp +++ b/yt/yt/core/misc/error.cpp @@ -359,7 +359,7 @@ void Serialize( } for (const auto& [key, value] : error.Attributes().ListPairs()) { fluent - .Item(key).Value(value); + .Item(key).Value(NYson::TYsonString(value)); } }) .DoIf(!error.InnerErrors().empty(), [&] (auto fluent) { @@ -398,8 +398,10 @@ void Deserialize(TError& error, const NYTree::INodePtr& node) auto children = mapNode->GetChildOrThrow(AttributesKey)->AsMap()->GetChildren(); for (const auto& [key, value] : children) { - // TODO(babenko): migrate to std::string - error <<= TErrorAttribute(TString(key), ConvertToYsonString(value)); + // NB(arkady-e1ppa): Serialization may add some attributes in normal yson + // format (in legacy versions) thus we have to recovert them into the + // text ones in order to make sure that everything is in the text format. + error <<= TErrorAttribute(key, ConvertToYsonString(value)); } error.UpdateOriginAttributes(); @@ -443,7 +445,7 @@ void ToProto(NYT::NProto::TError* protoError, const TError& error) for (const auto& [key, value] : pairs) { auto* protoAttribute = protoAttributes->add_attributes(); protoAttribute->set_key(key); - protoAttribute->set_value(value.ToString()); + protoAttribute->set_value(value); } } @@ -503,6 +505,8 @@ void FromProto(TError* error, const NYT::NProto::TError& protoError) error->SetMessage(FromProto<TString>(protoError.message())); if (protoError.has_attributes()) { for (const auto& protoAttribute : protoError.attributes().attributes()) { + // NB(arkady-e1ppa): Again for compatibility reasons we have to recovert stuff + // here as well. auto key = FromProto<TString>(protoAttribute.key()); auto value = FromProto<TString>(protoAttribute.value()); (*error) <<= TErrorAttribute(key, TYsonString(value)); @@ -607,8 +611,10 @@ void TErrorSerializer::Save(TStreamSaveContext& context, const TError& error) return lhs.first < rhs.first; }); for (const auto& [key, value] : attributePairs) { - Save(context, key); - Save(context, value); + // NB(arkady-e1ppa): For the sake of compatibility we keep the old + // serialization format. + Save(context, TString(key)); + Save(context, NYson::TYsonString(value)); } } else { Save(context, false); diff --git a/yt/yt/core/yson/writer.cpp b/yt/yt/core/yson/writer.cpp index 32e3f1636c..e4e3441ad2 100644 --- a/yt/yt/core/yson/writer.cpp +++ b/yt/yt/core/yson/writer.cpp @@ -4,6 +4,8 @@ #include <library/cpp/yt/coding/varint.h> +#include <library/cpp/yt/error/text_yson.h> + #include <util/charset/utf8.h> #include <util/stream/buffer.h> @@ -253,7 +255,7 @@ void TYsonWriter::OnDoubleScalar(double value) Stream_->Write(&value, sizeof(double)); } else { char buf[256]; - auto str = TStringBuf(buf, NDetail::FloatToStringWithNanInf(value, buf, sizeof(buf))); + auto str = TStringBuf(buf, NYT::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/convert-inl.h b/yt/yt/core/ytree/convert-inl.h index 60bf597941..6d1294f646 100644 --- a/yt/yt/core/ytree/convert-inl.h +++ b/yt/yt/core/ytree/convert-inl.h @@ -332,9 +332,9 @@ namespace NYT::NAttributeValueConversionImpl { template <class T> requires (!CPrimitiveConvertible<T>) -T TagInvoke(TFrom<T>, const NYson::TYsonString& value) +T TagInvoke(TFrom<T>, TStringBuf value) { - return NYTree::ConvertTo<T>(value); + return NYTree::ConvertTo<T>(NYson::TYsonString(value)); } //////////////////////////////////////////////////////////////////////////////// diff --git a/yt/yt/core/ytree/unittests/text_yson_convert_ut.cpp b/yt/yt/core/ytree/unittests/text_yson_convert_ut.cpp index 75913bed0b..598d1fe613 100644 --- a/yt/yt/core/ytree/unittests/text_yson_convert_ut.cpp +++ b/yt/yt/core/ytree/unittests/text_yson_convert_ut.cpp @@ -4,27 +4,36 @@ #include <library/cpp/yt/misc/source_location.h> -#include <library/cpp/yt/yson_string/convert.h> +#include <library/cpp/yt/error/text_yson.h> namespace NYT::NYTree { namespace { using namespace NYson; +using namespace NYT::NDetail; //////////////////////////////////////////////////////////////////////////////// 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()) + auto smartConvert = [] (const auto& value) { + if constexpr (std::constructible_from<TStringBuf, const std::remove_cvref_t<decltype(value)>>) { + return ConvertToTextYsonString(TStringBuf(value)); + } else { + return ConvertToTextYsonString(value); + } + }; + EXPECT_EQ(smartConvert(value), 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)) + auto str = ConvertToTextYsonString(value); + auto yson = TYsonString(str); + EXPECT_EQ(ConvertFromTextYsonString<T>(str), ConvertTo<T>(yson)) << NYT::Format("At %v", loc); } @@ -32,7 +41,7 @@ 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)) + EXPECT_EQ(ConvertFromTextYsonString<T>(value), ConvertTo<T>(yson)) << NYT::Format("At %v", loc); } @@ -203,66 +212,66 @@ TEST(TTextYsonConvertTest, ConvertFromTextTypeMissmatch) 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()); + CheckEqualConversionFromTextYson<bool>(ConvertToTextYsonString(TStringBuf("true"))); + CheckEqualConversionFromTextYson<bool>(ConvertToTextYsonString(TStringBuf("false"))); + CheckEqualConversionFromTextYson<bool>(ConvertToTextYsonString(TStringBuf("1"))); + CheckEqualConversionFromTextYson<bool>(ConvertToTextYsonString(TStringBuf("0"))); } TEST(TTextYsonConvertTest, ConvertFromTextYsonStringThrowBasicCases) { auto fromPayload = [] (const auto& value) { - return NYson::TYsonString(TString(value)); + return TYsonString(TString(value)); }; // Overflow. - EXPECT_ANY_THROW(ConvertFromTextYsonString<i8>(fromPayload("123123123213"))); + EXPECT_ANY_THROW(ConvertFromTextYsonString<i8>("123123123213")); EXPECT_ANY_THROW(ConvertTo<i8>(fromPayload("123123123213"))); // Negative. - EXPECT_ANY_THROW(ConvertFromTextYsonString<ui64>(fromPayload("-123"))); + EXPECT_ANY_THROW(ConvertFromTextYsonString<ui64>("-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(ConvertFromTextYsonString<i64>("haha")); + EXPECT_ANY_THROW(ConvertFromTextYsonString<i64>("123qq")); + EXPECT_ANY_THROW(ConvertFromTextYsonString<i64>("-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(ConvertFromTextYsonString<bool>("42")); EXPECT_ANY_THROW(ConvertTo<bool>(fromPayload("42"))); // Garbage to bool - EXPECT_ANY_THROW(ConvertFromTextYsonString<bool>(fromPayload("%falsse"))); + EXPECT_ANY_THROW(ConvertFromTextYsonString<bool>("%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(ConvertFromTextYsonString<bool>("\"True\"")); + EXPECT_ANY_THROW(ConvertFromTextYsonString<bool>("\"False\"")); + EXPECT_ANY_THROW(ConvertFromTextYsonString<bool>("\"1u\"")); + EXPECT_ANY_THROW(ConvertFromTextYsonString<bool>("\"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(ConvertFromTextYsonString<std::string>("")); + EXPECT_ANY_THROW(ConvertFromTextYsonString<std::string>("\"")); + EXPECT_ANY_THROW(ConvertFromTextYsonString<std::string>("haha\"")); + EXPECT_ANY_THROW(ConvertFromTextYsonString<std::string>("\'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(ConvertFromTextYsonString<double>("%%")); + EXPECT_ANY_THROW(ConvertFromTextYsonString<std::string>("%42inf")); + EXPECT_ANY_THROW(ConvertFromTextYsonString<std::string>("%NaaN")); EXPECT_ANY_THROW(ConvertTo<double>(fromPayload("%%"))); EXPECT_ANY_THROW(ConvertTo<std::string>(fromPayload("%42inf"))); EXPECT_ANY_THROW(ConvertTo<std::string>(fromPayload("%NaaN"))); |