#include "convert.h" #include "format.h" #include #include #include #include #include #include namespace NYT::NYson { //////////////////////////////////////////////////////////////////////////////// template <> TYsonString ConvertToYsonString(const i8& value) { return ConvertToYsonString(static_cast(value)); } template <> TYsonString ConvertToYsonString(const i32& value) { return ConvertToYsonString(static_cast(value)); } template <> TYsonString ConvertToYsonString(const i64& value) { std::array buffer; auto* ptr = buffer.data(); *ptr++ = NDetail::Int64Marker; ptr += WriteVarInt64(ptr, value); return TYsonString(TStringBuf(buffer.data(), ptr - buffer.data())); } template <> TYsonString ConvertToYsonString(const ui8& value) { return ConvertToYsonString(static_cast(value)); } template <> TYsonString ConvertToYsonString(const ui32& value) { return ConvertToYsonString(static_cast(value)); } template <> TYsonString ConvertToYsonString(const ui64& value) { std::array buffer; auto* ptr = buffer.data(); *ptr++ = NDetail::Uint64Marker; ptr += WriteVarUint64(ptr, value); return TYsonString(TStringBuf(buffer.data(), ptr - buffer.data())); } template <> TYsonString ConvertToYsonString(const TString& value) { return ConvertToYsonString(static_cast(value)); } struct TConvertStringToYsonStringTag { }; template <> TYsonString ConvertToYsonString(const TStringBuf& value) { auto buffer = TSharedMutableRef::Allocate( 1 + MaxVarInt64Size + value.length(), {.InitializeStorage = false}); auto* ptr = buffer.Begin(); *ptr++ = NDetail::StringMarker; ptr += WriteVarInt64(ptr, static_cast(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(const float& value) { return ConvertToYsonString(static_cast(value)); } template <> TYsonString ConvertToYsonString(const double& value) { std::array 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(const bool& value) { char ch = value ? NDetail::TrueMarker : NDetail::FalseMarker; return TYsonString(TStringBuf(&ch, 1)); } template <> TYsonString ConvertToYsonString(const TInstant& value) { return ConvertToYsonString(value.ToString()); } template <> TYsonString ConvertToYsonString(const TDuration& value) { return ConvertToYsonString(value.MilliSeconds()); } template <> TYsonString ConvertToYsonString(const TGuid& value) { std::array guidBuffer; auto guidLength = WriteGuidToBuffer(guidBuffer.data(), value) - guidBuffer.data(); std::array ysonBuffer; auto* ptr = ysonBuffer.data(); *ptr++ = NDetail::StringMarker; ptr += WriteVarInt64(ptr, static_cast(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(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(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(const TYsonStringBuf& str) \ { \ try { \ return CheckedIntegralCast(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(const TYsonStringBuf& str) { try { return ParseStringFromYsonString(str); } catch (const std::exception& ex) { throw TYsonLiteralParseException(ex, "Error parsing \"string\" value from YSON"); } } template <> float ConvertFromYsonString(const TYsonStringBuf& str) { try { return static_cast(ParseDoubleFromYsonString(str)); } catch (const std::exception& ex) { throw TYsonLiteralParseException(ex, "Error parsing \"float\" value from YSON"); } } template <> double ConvertFromYsonString(const TYsonStringBuf& str) { try { return ParseDoubleFromYsonString(str); } catch (const std::exception& ex) { throw TYsonLiteralParseException(ex, "Error parsing \"double\" value from YSON"); } } template <> bool ConvertFromYsonString(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(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(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(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