diff options
author | dgolear <dgolear@yandex-team.com> | 2024-10-10 11:05:17 +0300 |
---|---|---|
committer | dgolear <dgolear@yandex-team.com> | 2024-10-10 11:34:56 +0300 |
commit | 9e123c4656b0ef4973531b837f30e9e542207590 (patch) | |
tree | 2b0ace1c8b13b4da76c50b03d3cde321395484fe | |
parent | 2d78fdbaebf316aaddd8674538b6d2fcd9e095d7 (diff) | |
download | ydb-9e123c4656b0ef4973531b837f30e9e542207590.tar.gz |
YT: Support plain enum deserialization
(HIDDEN_URL
commit_hash:d9358ac48da1ab4a4ef9ccdbf7eb77a100cf3897
-rw-r--r-- | library/cpp/yt/misc/cast-inl.h | 30 | ||||
-rw-r--r-- | library/cpp/yt/misc/cast.h | 10 | ||||
-rw-r--r-- | library/cpp/yt/misc/enum.h | 3 | ||||
-rw-r--r-- | yt/yt/client/table_client/helpers-inl.h | 42 | ||||
-rw-r--r-- | yt/yt/client/table_client/helpers.h | 14 | ||||
-rw-r--r-- | yt/yt/core/yson/pull_parser_deserialize-inl.h | 14 | ||||
-rw-r--r-- | yt/yt/core/yson/pull_parser_deserialize.h | 3 | ||||
-rw-r--r-- | yt/yt/core/ytree/serialize-inl.h | 25 | ||||
-rw-r--r-- | yt/yt/core/ytree/serialize.h | 6 | ||||
-rw-r--r-- | yt/yt/core/ytree/unittests/serialize_ut.cpp | 13 |
10 files changed, 148 insertions, 12 deletions
diff --git a/library/cpp/yt/misc/cast-inl.h b/library/cpp/yt/misc/cast-inl.h index a694394f88..00e6e240b8 100644 --- a/library/cpp/yt/misc/cast-inl.h +++ b/library/cpp/yt/misc/cast-inl.h @@ -18,35 +18,35 @@ namespace NYT { namespace NDetail { template <class T, class S> -bool IsInIntegralRange(S value) +constexpr bool IsInIntegralRange(S value) requires std::is_signed_v<T> && std::is_signed_v<S> { return value >= std::numeric_limits<T>::lowest() && value <= std::numeric_limits<T>::max(); } template <class T, class S> -bool IsInIntegralRange(S value) +constexpr bool IsInIntegralRange(S value) requires std::is_signed_v<T> && std::is_unsigned_v<S> { return value <= static_cast<typename std::make_unsigned<T>::type>(std::numeric_limits<T>::max()); } template <class T, class S> -bool IsInIntegralRange(S value) +constexpr bool IsInIntegralRange(S value) requires std::is_unsigned_v<T> && std::is_signed_v<S> { return value >= 0 && static_cast<typename std::make_unsigned<S>::type>(value) <= std::numeric_limits<T>::max(); } template <class T, class S> -bool IsInIntegralRange(S value) +constexpr bool IsInIntegralRange(S value) requires std::is_unsigned_v<T> && std::is_unsigned_v<S> { return value <= std::numeric_limits<T>::max(); } template <class T, class S> -bool IsInIntegralRange(S value) +constexpr bool IsInIntegralRange(S value) requires std::is_enum_v<S> { return IsInIntegralRange<T>(static_cast<std::underlying_type_t<S>>(value)); @@ -79,10 +79,24 @@ inline TString FormatInvalidCastValue(char8_t value) //////////////////////////////////////////////////////////////////////////////// + +template <class T, class S> +constexpr bool CanFitSubtype() +{ + return NDetail::IsInIntegralRange<T>(std::numeric_limits<S>::min()) && + NDetail::IsInIntegralRange<T>(std::numeric_limits<S>::max()); +} + +template <class T, class S> +constexpr bool IsInIntegralRange(S value) +{ + return NDetail::IsInIntegralRange<T>(value); +} + template <class T, class S> -bool TryIntegralCast(S value, T* result) +constexpr bool TryIntegralCast(S value, T* result) { - if (!NYT::NDetail::IsInIntegralRange<T>(value)) { + if (!NDetail::IsInIntegralRange<T>(value)) { return false; } *result = static_cast<T>(value); @@ -105,7 +119,7 @@ T CheckedIntegralCast(S value) } template <class T, class S> -bool TryEnumCast(S value, T* result) +constexpr bool TryEnumCast(S value, T* result) { std::underlying_type_t<T> underlying; if (!TryIntegralCast<std::underlying_type_t<T>>(value, &underlying)) { diff --git a/library/cpp/yt/misc/cast.h b/library/cpp/yt/misc/cast.h index c7565c9e6d..e8654cc122 100644 --- a/library/cpp/yt/misc/cast.h +++ b/library/cpp/yt/misc/cast.h @@ -7,7 +7,13 @@ namespace NYT { //////////////////////////////////////////////////////////////////////////////// template <class T, class S> -bool TryIntegralCast(S value, T* result); +constexpr bool CanFitSubtype(); + +template <class T, class S> +constexpr bool IsInIntegralRange(S value); + +template <class T, class S> +constexpr bool TryIntegralCast(S value, T* result); template <class T, class S> T CheckedIntegralCast(S value); @@ -15,7 +21,7 @@ T CheckedIntegralCast(S value); //////////////////////////////////////////////////////////////////////////////// template <class T, class S> -bool TryEnumCast(S value, T* result); +constexpr bool TryEnumCast(S value, T* result); template <class T, class S> T CheckedEnumCast(S value); diff --git a/library/cpp/yt/misc/enum.h b/library/cpp/yt/misc/enum.h index 954b63cbc0..4d40ab8ec4 100644 --- a/library/cpp/yt/misc/enum.h +++ b/library/cpp/yt/misc/enum.h @@ -26,6 +26,9 @@ namespace NYT { template <class T> void GetEnumTraitsImpl(T); +template <class T, class S> +constexpr bool CanFitSubtype(); + template <class T> using TEnumTraitsImpl = decltype(GetEnumTraitsImpl(T())); diff --git a/yt/yt/client/table_client/helpers-inl.h b/yt/yt/client/table_client/helpers-inl.h index a4e39d7a6c..86cc5a793c 100644 --- a/yt/yt/client/table_client/helpers-inl.h +++ b/yt/yt/client/table_client/helpers-inl.h @@ -14,6 +14,7 @@ #include <yt/yt/core/concurrency/scheduler.h> #include <library/cpp/yt/misc/strong_typedef.h> +#include <library/cpp/yt/misc/cast.h> #include <array> @@ -142,8 +143,10 @@ void ToUnversionedValue( if constexpr (TEnumTraits<T>::IsStringSerializableEnum) { ToUnversionedValue(unversionedValue, NYT::FormatEnum(value), rowBuffer, id, flags); } else if constexpr (TEnumTraits<T>::IsBitEnum) { + static_assert(CanFitSubtype<ui64, std::underlying_type_t<T>>()); ToUnversionedValue(unversionedValue, static_cast<ui64>(value), rowBuffer, id, flags); } else { + static_assert(CanFitSubtype<i64, std::underlying_type_t<T>>()); ToUnversionedValue(unversionedValue, static_cast<i64>(value), rowBuffer, id, flags); } } @@ -154,12 +157,16 @@ void FromUnversionedValue( T* value, TUnversionedValue unversionedValue) { + static_assert(TEnumTraits<T>::IsStringSerializableEnum || + TEnumTraits<T>::IsBitEnum && CanFitSubtype<ui64, std::underlying_type_t<T>>() || + !TEnumTraits<T>::IsBitEnum && CanFitSubtype<i64, std::underlying_type_t<T>>()); + switch (unversionedValue.Type) { case EValueType::Int64: - *value = static_cast<T>(unversionedValue.Data.Int64); + *value = static_cast<T>(CheckedIntegralCast<std::underlying_type_t<T>>(unversionedValue.Data.Int64)); break; case EValueType::Uint64: - *value = static_cast<T>(unversionedValue.Data.Uint64); + *value = static_cast<T>(CheckedIntegralCast<std::underlying_type_t<T>>(unversionedValue.Data.Uint64)); break; case EValueType::String: *value = NYT::ParseEnum<T>(unversionedValue.AsStringBuf()); @@ -170,6 +177,37 @@ void FromUnversionedValue( } } +template <class T> + requires (!TEnumTraits<T>::IsEnum) && std::is_enum_v<T> +void ToUnversionedValue( + TUnversionedValue* unversionedValue, + T value, + const TRowBufferPtr& rowBuffer, + int id, + EValueFlags flags) +{ + static_assert(CanFitSubtype<i64, std::underlying_type_t<T>>()); + ToUnversionedValue(unversionedValue, static_cast<i64>(value), rowBuffer, id, flags); +} + +template <class T> + requires (!TEnumTraits<T>::IsEnum) && std::is_enum_v<T> +void FromUnversionedValue( + T* value, + TUnversionedValue unversionedValue) +{ + static_assert(CanFitSubtype<i64, std::underlying_type_t<T>>()); + + switch (unversionedValue.Type) { + case EValueType::Int64: + *value = static_cast<T>(CheckedIntegralCast<std::underlying_type_t<T>>(unversionedValue.Data.Int64)); + break; + default: + THROW_ERROR_EXCEPTION("Cannot parse enum value from %Qlv", + unversionedValue.Type); + } +} + //////////////////////////////////////////////////////////////////////////////// void ProtobufToUnversionedValueImpl( diff --git a/yt/yt/client/table_client/helpers.h b/yt/yt/client/table_client/helpers.h index b86124557e..d1c5d17325 100644 --- a/yt/yt/client/table_client/helpers.h +++ b/yt/yt/client/table_client/helpers.h @@ -148,6 +148,20 @@ void FromUnversionedValue( TUnversionedValue unversionedValue); template <class T> + requires (!TEnumTraits<T>::IsEnum) && std::is_enum_v<T> +void ToUnversionedValue( + TUnversionedValue* unversionedValue, + T value, + const TRowBufferPtr& rowBuffer, + int id = 0, + EValueFlags flags = EValueFlags::None); +template <class T> + requires (!TEnumTraits<T>::IsEnum) && std::is_enum_v<T> +void FromUnversionedValue( + T* value, + TUnversionedValue unversionedValue); + +template <class T> TUnversionedValue ToUnversionedValue( T&& value, const TRowBufferPtr& rowBuffer, diff --git a/yt/yt/core/yson/pull_parser_deserialize-inl.h b/yt/yt/core/yson/pull_parser_deserialize-inl.h index b0f6b9d8ca..78e343d917 100644 --- a/yt/yt/core/yson/pull_parser_deserialize-inl.h +++ b/yt/yt/core/yson/pull_parser_deserialize-inl.h @@ -8,6 +8,8 @@ #include <yt/yt/core/yson/token_writer.h> +#include <library/cpp/yt/misc/cast.h> + #include <vector> namespace NYT::NYson { @@ -239,6 +241,18 @@ void Deserialize(T& value, TYsonPullParserCursor* cursor) } } +template <class T> +requires (!TEnumTraits<T>::IsEnum) && std::is_enum_v<T> +void Deserialize(T& value, TYsonPullParserCursor* cursor) +{ + static_assert(CanFitSubtype<i64, std::underlying_type_t<T>>()); + + MaybeSkipAttributes(cursor); + EnsureYsonToken("enum", *cursor, EYsonItemType::Int64Value); + value = static_cast<T>(CheckedIntegralCast<std::underlying_type_t<T>>((*cursor)->UncheckedAsInt64())); + cursor->Next(); +} + // TCompactVector template <class T, size_t N> void Deserialize(TCompactVector<T, N>& value, TYsonPullParserCursor* cursor, std::enable_if_t<ArePullParserDeserializable<T>(), void*>) diff --git a/yt/yt/core/yson/pull_parser_deserialize.h b/yt/yt/core/yson/pull_parser_deserialize.h index 5f6215180b..669b976394 100644 --- a/yt/yt/core/yson/pull_parser_deserialize.h +++ b/yt/yt/core/yson/pull_parser_deserialize.h @@ -113,6 +113,9 @@ void Deserialize( template <class T> requires TEnumTraits<T>::IsEnum void Deserialize(T& value, TYsonPullParserCursor* cursor); +template <class T> + requires (!TEnumTraits<T>::IsEnum) && std::is_enum_v<T> +void Deserialize(T& value, TYsonPullParserCursor* cursor); // TCompactVector. template <class T, size_t N> diff --git a/yt/yt/core/ytree/serialize-inl.h b/yt/yt/core/ytree/serialize-inl.h index fe1ce789e6..01fc90c73a 100644 --- a/yt/yt/core/ytree/serialize-inl.h +++ b/yt/yt/core/ytree/serialize-inl.h @@ -344,6 +344,14 @@ void Serialize(T value, NYson::IYsonConsumer* consumer) } } +template <class T> + requires (!TEnumTraits<T>::IsEnum) && std::is_enum_v<T> +void Serialize(T value, NYson::IYsonConsumer* consumer) +{ + static_assert(CanFitSubtype<i64, std::underlying_type_t<T>>()); + consumer->OnInt64Scalar(static_cast<i64>(value)); +} + // std::optional template <class T> void Serialize(const std::optional<T>& value, NYson::IYsonConsumer* consumer) @@ -528,6 +536,23 @@ void Deserialize(T& value, INodePtr node) } } +template <class T> + requires (!TEnumTraits<T>::IsEnum) && std::is_enum_v<T> +void Deserialize(T& value, INodePtr node) +{ + switch (node->GetType()) { + case ENodeType::Int64: { + // TODO: CheckedEnumCast via __PRETTY_FUNCTION__? + i64 serialized = node->AsInt64()->GetValue(); + value = static_cast<T>(CheckedIntegralCast<std::underlying_type_t<T>>(serialized)); + break; + } + default: + THROW_ERROR_EXCEPTION("Cannot deserialize enum from %Qlv node", + node->GetType()); + } +} + // std::optional template <class T> void Deserialize(std::optional<T>& value, INodePtr node) diff --git a/yt/yt/core/ytree/serialize.h b/yt/yt/core/ytree/serialize.h index 3aa784ac8d..2d5185f18d 100644 --- a/yt/yt/core/ytree/serialize.h +++ b/yt/yt/core/ytree/serialize.h @@ -109,6 +109,9 @@ void Serialize(IInputStream& input, NYson::IYsonConsumer* consumer); template <class T> requires TEnumTraits<T>::IsEnum void Serialize(T value, NYson::IYsonConsumer* consumer); +template <class T> + requires (!TEnumTraits<T>::IsEnum) && std::is_enum_v<T> +void Serialize(T value, NYson::IYsonConsumer* consumer); // std::optional template <class T> @@ -218,6 +221,9 @@ void Deserialize(TGuid& value, INodePtr node); template <class T> requires TEnumTraits<T>::IsEnum void Deserialize(T& value, INodePtr node); +template <class T> + requires (!TEnumTraits<T>::IsEnum) && std::is_enum_v<T> +void Deserialize(T& value, INodePtr node); // std::optional template <class T> diff --git a/yt/yt/core/ytree/unittests/serialize_ut.cpp b/yt/yt/core/ytree/unittests/serialize_ut.cpp index ee36997f70..63bf71b0d2 100644 --- a/yt/yt/core/ytree/unittests/serialize_ut.cpp +++ b/yt/yt/core/ytree/unittests/serialize_ut.cpp @@ -51,6 +51,12 @@ DEFINE_BIT_ENUM(ETestBitEnum, ((Green) (0x0004)) ); +enum class EPlainTestEnum +{ + First, + Second, +}; + template <typename T> T PullParserConvert(TYsonStringBuf s) { @@ -427,6 +433,13 @@ TEST(TSerializationTest, SerializableArcadiaEnum) } } +TEST(TSerializationTest, PlainEnum) +{ + TestSerializationDeserialization(EPlainTestEnum::First); + + TestSerializationDeserialization(static_cast<EPlainTestEnum>(42)); +} + TEST(TYTreeSerializationTest, Protobuf) { NProto::TTestMessage message; |