#include "convert.h" #include "format.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/stream/mem.h> namespace NYT::NYson { //////////////////////////////////////////////////////////////////////////////// template <> TYsonString ConvertToYsonString<i8>(const i8& value) { return ConvertToYsonString(static_cast<i64>(value)); } template <> TYsonString ConvertToYsonString<i32>(const i32& value) { return ConvertToYsonString(static_cast<i64>(value)); } template <> TYsonString ConvertToYsonString<i64>(const i64& value) { std::array<char, 1 + MaxVarInt64Size> buffer; auto* ptr = buffer.data(); *ptr++ = NDetail::Int64Marker; ptr += WriteVarInt64(ptr, value); return TYsonString(TStringBuf(buffer.data(), ptr - buffer.data())); } template <> TYsonString ConvertToYsonString<ui8>(const ui8& value) { return ConvertToYsonString(static_cast<ui64>(value)); } template <> TYsonString ConvertToYsonString<ui32>(const ui32& value) { return ConvertToYsonString(static_cast<ui64>(value)); } template <> TYsonString ConvertToYsonString<ui64>(const ui64& value) { std::array<char, 1 + MaxVarInt64Size> buffer; auto* ptr = buffer.data(); *ptr++ = NDetail::Uint64Marker; ptr += WriteVarUint64(ptr, value); return TYsonString(TStringBuf(buffer.data(), ptr - buffer.data())); } template <> TYsonString ConvertToYsonString<TString>(const TString& value) { return ConvertToYsonString(static_cast<TStringBuf>(value)); } struct TConvertStringToYsonStringTag { }; template <> TYsonString ConvertToYsonString<TStringBuf>(const TStringBuf& value) { auto buffer = TSharedMutableRef::Allocate<TConvertStringToYsonStringTag>( 1 + MaxVarInt64Size + value.length(), {.InitializeStorage = false}); auto* ptr = buffer.Begin(); *ptr++ = NDetail::StringMarker; ptr += WriteVarInt64(ptr, static_cast<i64>(value.length())); ::memcpy(ptr, value.data(), value.length()); ptr += value.length(); return TYsonString(buffer.Slice(buffer.Begin(), ptr)); } TYsonString ConvertToYsonString(const char* value) { return ConvertToYsonString(TStringBuf(value)); } template <> TYsonString ConvertToYsonString<float>(const float& value) { return ConvertToYsonString(static_cast<double>(value)); } template <> TYsonString ConvertToYsonString<double>(const double& value) { std::array<char, 1 + sizeof(double)> buffer; auto* ptr = buffer.data(); *ptr++ = NDetail::DoubleMarker; ::memcpy(ptr, &value, sizeof(value)); ptr += sizeof(value); return TYsonString(TStringBuf(buffer.data(), ptr - buffer.data())); } template <> TYsonString ConvertToYsonString<bool>(const bool& value) { char ch = value ? NDetail::TrueMarker : NDetail::FalseMarker; return TYsonString(TStringBuf(&ch, 1)); } template <> TYsonString ConvertToYsonString<TInstant>(const TInstant& value) { return ConvertToYsonString(value.ToString()); } template <> TYsonString ConvertToYsonString<TDuration>(const TDuration& value) { return ConvertToYsonString(value.MilliSeconds()); } template <> TYsonString ConvertToYsonString<TGuid>(const TGuid& value) { std::array<char, MaxGuidStringSize> guidBuffer; auto guidLength = WriteGuidToBuffer(guidBuffer.data(), value) - guidBuffer.data(); std::array<char, 1 + MaxVarInt64Size + MaxGuidStringSize> ysonBuffer; auto* ptr = ysonBuffer.data(); *ptr++ = NDetail::StringMarker; ptr += WriteVarInt64(ptr, static_cast<i64>(guidLength)); ::memcpy(ptr, guidBuffer.data(), guidLength); ptr += guidLength; return TYsonString(TStringBuf(ysonBuffer.data(), ptr - ysonBuffer.data())); } //////////////////////////////////////////////////////////////////////////////// namespace { TString FormatUnexpectedMarker(char ch) { switch (ch) { case NDetail::BeginListSymbol: return "list"; case NDetail::BeginMapSymbol: return "map"; case NDetail::BeginAttributesSymbol: return "attributes"; case NDetail::EntitySymbol: return "\"entity\" literal"; case NDetail::StringMarker: return "\"string\" literal"; case NDetail::Int64Marker: return "\"int64\" literal"; case NDetail::DoubleMarker: return "\"double\" literal"; case NDetail::FalseMarker: case NDetail::TrueMarker: return "\"boolean\" literal"; case NDetail::Uint64Marker: return "\"uint64\" literal"; default: return Format("unexpected symbol %qv", ch); } } i64 ParseInt64FromYsonString(const TYsonStringBuf& str) { YT_ASSERT(str.GetType() == EYsonType::Node); auto strBuf = str.AsStringBuf(); TMemoryInput input(strBuf.data(), strBuf.length()); char ch; if (!input.ReadChar(ch)) { throw TYsonLiteralParseException("Missing type marker"); } if (ch != NDetail::Int64Marker) { throw TYsonLiteralParseException(Format("Unexpected %v", FormatUnexpectedMarker(ch))); } i64 result; try { ReadVarInt64(&input, &result); } catch (const std::exception& ex) { throw TYsonLiteralParseException(ex, "Failed to decode \"int64\" value"); } return result; } ui64 ParseUint64FromYsonString(const TYsonStringBuf& str) { YT_ASSERT(str.GetType() == EYsonType::Node); auto strBuf = str.AsStringBuf(); TMemoryInput input(strBuf.data(), strBuf.length()); char ch; if (!input.ReadChar(ch)) { throw TYsonLiteralParseException("Missing type marker"); } if (ch != NDetail::Uint64Marker) { throw TYsonLiteralParseException(Format("Unexpected %v", FormatUnexpectedMarker(ch))); } ui64 result; try { ReadVarUint64(&input, &result); } catch (const std::exception& ex) { throw TYsonLiteralParseException(ex, "Failed to decode \"uint64\" value"); } return result; } TString ParseStringFromYsonString(const TYsonStringBuf& str) { YT_ASSERT(str.GetType() == EYsonType::Node); auto strBuf = str.AsStringBuf(); TMemoryInput input(strBuf.data(), strBuf.length()); char ch; if (!input.ReadChar(ch)) { throw TYsonLiteralParseException("Missing type marker"); } if (ch != NDetail::StringMarker) { throw TYsonLiteralParseException(Format("Unexpected %v", FormatUnexpectedMarker(ch))); } i64 length; try { ReadVarInt64(&input, &length); } catch (const std::exception& ex) { throw TYsonLiteralParseException(ex, "Failed to decode string length"); } if (length < 0) { throw TYsonLiteralParseException(Format("Negative string length ", length)); } if (static_cast<i64>(input.Avail()) != length) { throw TYsonLiteralParseException(Format("Incorrect remaining string length: expected %v, got %v", length, input.Avail())); } TString result; result.ReserveAndResize(length); YT_VERIFY(static_cast<i64>(input.Read(result.Detach(), length)) == length); return result; } double ParseDoubleFromYsonString(const TYsonStringBuf& str) { YT_ASSERT(str.GetType() == EYsonType::Node); auto strBuf = str.AsStringBuf(); TMemoryInput input(strBuf.data(), strBuf.length()); char ch; if (!input.ReadChar(ch)) { throw TYsonLiteralParseException("Missing type marker"); } if (ch != NDetail::DoubleMarker) { throw TYsonLiteralParseException(Format("Unexpected %v", FormatUnexpectedMarker(ch))); } if (input.Avail() != sizeof(double)) { throw TYsonLiteralParseException(Format("Incorrect remaining string length: expected %v, got %v", sizeof(double), input.Avail())); } double result; YT_VERIFY(input.Read(&result, sizeof(result))); return result; } } // namespace #define PARSE(type, underlyingType) \ template <> \ type ConvertFromYsonString<type>(const TYsonStringBuf& str) \ { \ try { \ return CheckedIntegralCast<type>(Parse ## underlyingType ## FromYsonString(str)); \ } catch (const std::exception& ex) { \ throw TYsonLiteralParseException(ex, "Error parsing \"" #type "\" value from YSON"); \ } \ } PARSE(i8, Int64 ) PARSE(i16, Int64 ) PARSE(i32, Int64 ) PARSE(i64, Int64 ) PARSE(ui8, Uint64) PARSE(ui16, Uint64) PARSE(ui32, Uint64) PARSE(ui64, Uint64) #undef PARSE template <> TString ConvertFromYsonString<TString>(const TYsonStringBuf& str) { try { return ParseStringFromYsonString(str); } catch (const std::exception& ex) { throw TYsonLiteralParseException(ex, "Error parsing \"string\" value from YSON"); } } template <> float ConvertFromYsonString<float>(const TYsonStringBuf& str) { try { return static_cast<float>(ParseDoubleFromYsonString(str)); } catch (const std::exception& ex) { throw TYsonLiteralParseException(ex, "Error parsing \"float\" value from YSON"); } } template <> double ConvertFromYsonString<double>(const TYsonStringBuf& str) { try { return ParseDoubleFromYsonString(str); } catch (const std::exception& ex) { throw TYsonLiteralParseException(ex, "Error parsing \"double\" value from YSON"); } } template <> bool ConvertFromYsonString<bool>(const TYsonStringBuf& str) { try { YT_ASSERT(str.GetType() == EYsonType::Node); auto strBuf = str.AsStringBuf(); TMemoryInput input(strBuf.data(), strBuf.length()); char ch; if (!input.ReadChar(ch)) { throw TYsonLiteralParseException("Missing type marker"); } if (ch != NDetail::TrueMarker && ch != NDetail::FalseMarker) { throw TYsonLiteralParseException(Format("Unexpected %v", FormatUnexpectedMarker(ch))); } return ch == NDetail::TrueMarker; } catch (const std::exception& ex) { throw TYsonLiteralParseException(ex, "Error parsing \"boolean\" value from YSON"); } } template <> TInstant ConvertFromYsonString<TInstant>(const TYsonStringBuf& str) { try { return TInstant::ParseIso8601(ParseStringFromYsonString(str)); } catch (const std::exception& ex) { throw TYsonLiteralParseException(ex, "Error parsing \"instant\" value from YSON"); } } template <> TDuration ConvertFromYsonString<TDuration>(const TYsonStringBuf& str) { try { return TDuration::MilliSeconds(ParseUint64FromYsonString(str)); } catch (const std::exception& ex) { throw TYsonLiteralParseException(ex, "Error parsing \"duration\" value from YSON"); } } template <> TGuid ConvertFromYsonString<TGuid>(const TYsonStringBuf& str) { try { return TGuid::FromString(ParseStringFromYsonString(str)); } catch (const std::exception& ex) { throw TYsonLiteralParseException(ex, "Error parsing \"guid\" value from YSON"); } } //////////////////////////////////////////////////////////////////////////////// } // namespace NYT::NYson