#pragma once #include "byte_writer.h" #include "cescape.h" #include "percent_scalar.h" #include "stream_counter.h" #include "symbols.h" #include "varint.h" #include #include #include #include #include #include #include #include namespace NYsonPull { namespace NDetail { class writer: public IConsumer { enum class state { maybe_key, maybe_value, value, value_noattr, before_begin, before_end, after_end, }; byte_writer> stream_; TVector stack_; bool need_item_separator_ = false; EStreamType mode_ = EStreamType::ListFragment; state state_ = state::before_begin; public: void OnBeginStream() override { update_state(EEventType::BeginStream); } void OnEndStream() override { update_state(EEventType::EndStream); stream_.flush_buffer(); } void OnBeginList() override { begin_node(); write(NSymbol::begin_list); update_state(EEventType::BeginList); begin_collection(collection_type::list); } void OnEndList() override { update_state(EEventType::EndList); end_collection(collection_type::list); write(NSymbol::end_list); end_node(); } void OnBeginMap() override { begin_node(); write(NSymbol::begin_map); update_state(EEventType::BeginMap); begin_collection(collection_type::map); } void OnEndMap() override { update_state(EEventType::EndMap); end_collection(collection_type::map); write(NSymbol::end_map); end_node(); } void OnBeginAttributes() override { begin_node(); write(NSymbol::begin_attributes); update_state(EEventType::BeginAttributes); begin_collection(collection_type::attributes); } void OnEndAttributes() override { update_state(EEventType::EndAttributes); end_collection(collection_type::attributes); write(NSymbol::end_attributes); // no end_node } void OnEntity() override { begin_node(); update_state(EEventType::Scalar); write(NSymbol::entity); end_node(); } protected: enum class collection_type { list, map, attributes, }; writer(NYsonPull::NOutput::IStream& stream, EStreamType mode) : stream_(stream) , mode_{mode} { } bool need_item_separator() const { return need_item_separator_; } void need_item_separator(bool value) { need_item_separator_ = value; } size_t depth() const { Y_ASSERT(!stack_.empty()); if (mode_ == EStreamType::Node) { return stack_.size() - 1; } else { return stack_.size() - 2; } } EStreamType mode() const { return mode_; } void write(ui8 c) { stream_.write(c); } void write(TStringBuf value) { write_raw(value.data(), value.size()); } void write_raw(const void* ptr, size_t len) { stream_.write(static_cast(ptr), len); } template void write_varint(T value) { NVarInt::write(stream_, value); } void write_escaped_string(TStringBuf value) { write(NSymbol::quote); NCEscape::encode(stream_, value); write(NSymbol::quote); } void push(EEventType type) { stack_.push_back(type); } void pop(EEventType type) { if (stack_.empty()) { fail("Unpaired events: empty event stack"); } if (stack_.back() != type) { fail("Unpaired events: expected ", type, ", got ", stack_.back()); } stack_.pop_back(); } void update_state(EEventType event) { switch (state_) { case state::before_begin: if (event != EEventType::BeginStream) { fail("Expected begin_stream, got ", event); } begin_stream(); return; case state::before_end: if (event != EEventType::EndStream) { fail("Expected end_stream, got ", event); } end_stream(); return; case state::after_end: fail("Attempted write past stream end"); case state::maybe_key: if (event == EEventType::Key) { state_ = state::value; return; } switch (event) { case EEventType::EndStream: end_stream(); return; case EEventType::EndMap: pop(EEventType::BeginMap); next_state(); return; case EEventType::EndAttributes: pop(EEventType::BeginAttributes); state_ = state::value_noattr; return; default: fail("Unexpected event ", event, " in maybe_key"); } break; case state::maybe_value: switch (event) { case EEventType::EndList: pop(EEventType::BeginList); next_state(); return; case EEventType::EndStream: end_stream(); return; default: break; } [[fallthrough]]; case state::value: if (event == EEventType::BeginAttributes) { push(EEventType::BeginAttributes); next_state(); return; } [[fallthrough]]; case state::value_noattr: switch (event) { case EEventType::Scalar: next_state(); return; case EEventType::BeginList: push(EEventType::BeginList); next_state(); return; case EEventType::BeginMap: push(EEventType::BeginMap); next_state(); return; default: fail("Unexpected event ", event, " (in value_*)"); } break; } } void next_state() { Y_ASSERT(!stack_.empty()); switch (stack_.back()) { case EEventType::BeginMap: case EEventType::BeginAttributes: state_ = state::maybe_key; break; case EEventType::BeginList: state_ = state::maybe_value; break; case EEventType::BeginStream: state_ = state::before_end; break; default: Y_UNREACHABLE(); } } void begin_stream() { push(EEventType::BeginStream); switch (mode_) { case EStreamType::ListFragment: push(EEventType::BeginList); state_ = state::maybe_value; break; case EStreamType::MapFragment: push(EEventType::BeginMap); state_ = state::maybe_key; break; case EStreamType::Node: state_ = state::value; break; } } void end_stream() { switch (mode_) { case EStreamType::ListFragment: pop(EEventType::BeginList); break; case EStreamType::MapFragment: pop(EEventType::BeginMap); break; case EStreamType::Node: break; } pop(EEventType::BeginStream); state_ = state::after_end; } virtual void begin_node() { if (need_item_separator_) { write(NSymbol::item_separator); } } virtual void end_node() { need_item_separator_ = true; } virtual void begin_key() { begin_node(); } virtual void end_key() { need_item_separator_ = false; write(NSymbol::key_value_separator); } virtual void begin_collection(collection_type type) { Y_UNUSED(type); need_item_separator_ = false; } virtual void end_collection(collection_type type) { need_item_separator_ = (type != collection_type::attributes); } template ATTRIBUTE(noinline, cold) void fail[[noreturn]](const char* msg, Args&&... args) { auto formatted_message = format_string( msg, std::forward(args)...); throw NException::TBadOutput( formatted_message, stream_.counter().info()); } }; class TBinaryWriterImpl final: public writer { public: TBinaryWriterImpl(NYsonPull::NOutput::IStream& stream, EStreamType mode) : writer(stream, mode) { } void OnScalarBoolean(bool value) override { update_state(EEventType::Scalar); begin_node(); write(value ? NSymbol::true_marker : NSymbol::false_marker); end_node(); } void OnScalarInt64(i64 value) override { update_state(EEventType::Scalar); begin_node(); write(NSymbol::int64_marker); write_varint(value); end_node(); } void OnScalarUInt64(ui64 value) override { update_state(EEventType::Scalar); begin_node(); write(NSymbol::uint64_marker); write_varint(value); end_node(); } void OnScalarFloat64(double value) override { update_state(EEventType::Scalar); begin_node(); write(NSymbol::double_marker); write_raw(&value, sizeof value); end_node(); } void OnScalarString(TStringBuf value) override { update_state(EEventType::Scalar); begin_node(); write(NSymbol::string_marker); write_varint(static_cast(value.size())); write_raw(value.data(), value.size()); end_node(); } void OnKey(TStringBuf name) override { update_state(EEventType::Key); begin_key(); write(NSymbol::string_marker); write_varint(static_cast(name.size())); write_raw(name.data(), name.size()); end_key(); } }; class TTextWriterImpl: public writer { public: TTextWriterImpl(NYsonPull::NOutput::IStream& stream, EStreamType mode) : writer(stream, mode) { } void OnScalarBoolean(bool value) override { update_state(EEventType::Scalar); begin_node(); write(value ? percent_scalar::true_literal : percent_scalar::false_literal); end_node(); } void OnScalarInt64(i64 value) override { update_state(EEventType::Scalar); char buf[32]; auto len = ::snprintf(buf, sizeof(buf), "%" PRIi64, value); begin_node(); write_raw(buf, len); end_node(); } void OnScalarUInt64(ui64 value) override { update_state(EEventType::Scalar); char buf[32]; auto len = ::snprintf(buf, sizeof(buf), "%" PRIu64, value); begin_node(); write_raw(buf, len); write('u'); end_node(); } void OnScalarFloat64(double value) override { update_state(EEventType::Scalar); begin_node(); if (std::isfinite(value)) { char buf[32]; auto len = ::snprintf(buf, sizeof(buf), "%#.17lg", value); write_raw(buf, len); } else if (std::isnan(value)) { write(percent_scalar::nan_literal); } else if (value > 0) { write(percent_scalar::positive_inf_literal); } else { write(percent_scalar::negative_inf_literal); } end_node(); } void OnScalarString(TStringBuf value) override { update_state(EEventType::Scalar); begin_node(); write_escaped_string(value); end_node(); } void OnKey(TStringBuf name) override { update_state(EEventType::Key); begin_key(); write_escaped_string(name); end_key(); } protected: void begin_node() override { if (need_item_separator()) { write(NSymbol::item_separator); write(' '); } } void end_node() override { if (mode() != EStreamType::Node && depth() == 0) { write(NSymbol::item_separator); write('\n'); need_item_separator(false); } else { writer::end_node(); } } void end_key() override { write(' '); writer::end_key(); write(' '); } }; class TPrettyWriterImpl final: public TTextWriterImpl { size_t indent_size_; public: TPrettyWriterImpl( NYsonPull::NOutput::IStream& stream, EStreamType mode, size_t indent_size) : TTextWriterImpl(stream, mode) , indent_size_{indent_size} { } protected: void begin_node() override { if (need_item_separator()) { write(NSymbol::item_separator); newline(); } } void begin_collection(collection_type type) override { TTextWriterImpl::begin_collection(type); newline(); } void end_collection(collection_type type) override { TTextWriterImpl::end_collection(type); newline(); } void newline() { write('\n'); indent(depth()); } void indent(size_t count) { for (size_t i = 0; i < count * indent_size_; ++i) { write(' '); } } }; template NYsonPull::TWriter make_writer( THolder stream, Args&&... args) { auto impl = MakeHolder(*stream, std::forward(args)...); return NYsonPull::TWriter(std::move(stream), std::move(impl)); } } }