diff options
author | Devtools Arcadia <arcadia-devtools@yandex-team.ru> | 2022-02-07 18:08:42 +0300 |
---|---|---|
committer | Devtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net> | 2022-02-07 18:08:42 +0300 |
commit | 1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch) | |
tree | e26c9fed0de5d9873cce7e00bc214573dc2195b7 /library/cpp/yson_pull/detail/writer.h | |
download | ydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz |
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'library/cpp/yson_pull/detail/writer.h')
-rw-r--r-- | library/cpp/yson_pull/detail/writer.h | 566 |
1 files changed, 566 insertions, 0 deletions
diff --git a/library/cpp/yson_pull/detail/writer.h b/library/cpp/yson_pull/detail/writer.h new file mode 100644 index 0000000000..b24b994292 --- /dev/null +++ b/library/cpp/yson_pull/detail/writer.h @@ -0,0 +1,566 @@ +#pragma once + +#include "byte_writer.h" +#include "cescape.h" +#include "percent_scalar.h" +#include "stream_counter.h" +#include "symbols.h" +#include "varint.h" + +#include <library/cpp/yson_pull/consumer.h> +#include <library/cpp/yson_pull/event.h> +#include <library/cpp/yson_pull/output.h> +#include <library/cpp/yson_pull/stream_type.h> +#include <library/cpp/yson_pull/writer.h> + +#include <util/generic/vector.h> +#include <util/system/yassert.h> + +#include <cmath> + +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_counter<false>> stream_; + TVector<EEventType> 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<const ui8*>(ptr), len); + } + + template <typename T> + 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 <typename... Args> + ATTRIBUTE(noinline, cold) + void fail[[noreturn]](const char* msg, Args&&... args) { + auto formatted_message = format_string( + msg, + std::forward<Args>(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<i32>(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<i32>(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 <typename T, typename... Args> + NYsonPull::TWriter make_writer( + THolder<NYsonPull::NOutput::IStream> stream, + Args&&... args) { + auto impl = MakeHolder<T>(*stream, std::forward<Args>(args)...); + return NYsonPull::TWriter(std::move(stream), std::move(impl)); + } + } +} |