aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/type_info
diff options
context:
space:
mode:
authormax42 <max42@yandex-team.com>2023-06-30 03:37:03 +0300
committermax42 <max42@yandex-team.com>2023-06-30 03:37:03 +0300
commitfac2bd72b4b31ec3238292caf8fb2a8aaa6d6c4a (patch)
treeb8cbc1deb00309c7f1a7ab6df520a76cf0b5c6d7 /library/cpp/type_info
parent7bf166b1a7ed0af927f230022b245af618e998c1 (diff)
downloadydb-fac2bd72b4b31ec3238292caf8fb2a8aaa6d6c4a.tar.gz
YT-19324: move YT provider to ydb/library/yql
This commit is formed by the following script: https://paste.yandex-team.ru/6f92e4b8-efc5-4d34-948b-15ee2accd7e7/text. This commit has zero effect on all projects that depend on YQL. The summary of changes: - `yql/providers/yt -> ydb/library/yql/providers/yt `- the whole implementation of YT provider is moved into YDB code base for further export as a part of YT YQL plugin shared library; - `yql/providers/stat/{expr_nodes,uploader} -> ydb/library/yql/providers/stat/{expr_nodes,uploader}` - a small interface without implementation and the description of stat expr nodes; - `yql/core/extract_predicate/ut -> ydb/library/yql/core/extract_predicate/ut`; - `yql/core/{ut,ut_common} -> ydb/library/yql/core/{ut,ut_common}`; - `yql/core` is gone; - `yql/library/url_preprocessing -> ydb/library/yql/core/url_preprocessing`. **NB**: all new targets inside `ydb/` are under `IF (NOT CMAKE_EXPORT)` clause which disables them from open-source cmake generation and ya make build. They will be enabled in the subsequent commits.
Diffstat (limited to 'library/cpp/type_info')
-rw-r--r--library/cpp/type_info/Readme.md9
-rw-r--r--library/cpp/type_info/builder.cpp458
-rw-r--r--library/cpp/type_info/builder.h346
-rw-r--r--library/cpp/type_info/error.cpp1
-rw-r--r--library/cpp/type_info/error.h33
-rw-r--r--library/cpp/type_info/fwd.h123
-rw-r--r--library/cpp/type_info/test-data/bad-types.txt229
-rw-r--r--library/cpp/type_info/test-data/good-types.txt478
-rw-r--r--library/cpp/type_info/type.cpp1662
-rw-r--r--library/cpp/type_info/type.h2421
-rw-r--r--library/cpp/type_info/type_complexity.cpp78
-rw-r--r--library/cpp/type_info/type_complexity.h18
-rw-r--r--library/cpp/type_info/type_constructors.h110
-rw-r--r--library/cpp/type_info/type_equivalence.cpp286
-rw-r--r--library/cpp/type_info/type_equivalence.h36
-rw-r--r--library/cpp/type_info/type_factory.cpp495
-rw-r--r--library/cpp/type_info/type_factory.h906
-rw-r--r--library/cpp/type_info/type_info.cpp1
-rw-r--r--library/cpp/type_info/type_info.h10
-rw-r--r--library/cpp/type_info/type_io.cpp1186
-rw-r--r--library/cpp/type_info/type_io.h115
-rw-r--r--library/cpp/type_info/type_list.cpp1
-rw-r--r--library/cpp/type_info/type_list.h183
-rw-r--r--library/cpp/type_info/ut/builder.cpp125
-rw-r--r--library/cpp/type_info/ut/test_data.cpp91
-rw-r--r--library/cpp/type_info/ut/type_basics.cpp381
-rw-r--r--library/cpp/type_info/ut/type_complexity_ut.cpp33
-rw-r--r--library/cpp/type_info/ut/type_constraints.cpp53
-rw-r--r--library/cpp/type_info/ut/type_deserialize.cpp528
-rw-r--r--library/cpp/type_info/ut/type_equivalence.cpp394
-rw-r--r--library/cpp/type_info/ut/type_factory.cpp121
-rw-r--r--library/cpp/type_info/ut/type_factory_raw.cpp365
-rw-r--r--library/cpp/type_info/ut/type_io.cpp535
-rw-r--r--library/cpp/type_info/ut/type_list.cpp64
-rw-r--r--library/cpp/type_info/ut/type_serialize.cpp251
-rw-r--r--library/cpp/type_info/ut/type_show.cpp199
-rw-r--r--library/cpp/type_info/ut/type_strip_tags.cpp31
-rw-r--r--library/cpp/type_info/ut/utils.h73
-rw-r--r--library/cpp/type_info/ut/ya.make32
-rw-r--r--library/cpp/type_info/ya.make26
40 files changed, 12487 insertions, 0 deletions
diff --git a/library/cpp/type_info/Readme.md b/library/cpp/type_info/Readme.md
new file mode 100644
index 0000000000..e4501c8c11
--- /dev/null
+++ b/library/cpp/type_info/Readme.md
@@ -0,0 +1,9 @@
+# TI — unified in-memory representation for Common Yandex Typesystem
+
+Common Yandex Typesystem is a type system used across data storage and processing technologies developed by Yandex. A list of systems that support it includes YT, YDB, RTMR, YQL and others.
+
+The Type Info library provides classes that represent types from Common Yandex Typesystem. It allows constructing in-memory representations of types, inspecting said representations, combining them to derive new types, as well as serializing and deserializing them.
+
+For those familiar with Protobuf, Type Info implements the concept of message descriptors for Yandex type system.
+
+Here you can find open issues: https://st.yandex-team.ru/YT/order:updated:false/filter?tags=type_info&resolution=empty() \ No newline at end of file
diff --git a/library/cpp/type_info/builder.cpp b/library/cpp/type_info/builder.cpp
new file mode 100644
index 0000000000..008e0af754
--- /dev/null
+++ b/library/cpp/type_info/builder.cpp
@@ -0,0 +1,458 @@
+#include "builder.h"
+
+#include "type_factory.h"
+
+namespace NTi {
+ TStructBuilderRaw::TStructBuilderRaw(IPoolTypeFactory& factory) noexcept
+ : Factory_(&factory)
+ {
+ }
+
+ TStructBuilderRaw::TStructBuilderRaw(IPoolTypeFactory& factory, TStructTypePtr prototype) noexcept
+ : TStructBuilderRaw(factory, prototype.Get())
+ {
+ }
+
+ TStructBuilderRaw::TStructBuilderRaw(IPoolTypeFactory& factory, const TStructType* prototype) noexcept
+ : TStructBuilderRaw(factory)
+ {
+ Members_.reserve(prototype->GetMembers().size());
+
+ if (prototype->GetFactory() == Factory_) {
+ // members are in the same factory -- can reuse them.
+ for (auto member : prototype->GetMembers()) {
+ Members_.push_back(member);
+ }
+ } else {
+ // members are in a different factory -- should copy them.
+ for (auto member : prototype->GetMembers()) {
+ AddMember(member.GetName(), member.GetTypeRaw());
+ }
+ }
+ }
+
+ TStructBuilderRaw& TStructBuilderRaw::SetName(TMaybe<TStringBuf> name) & noexcept {
+ Name_ = Factory_->AllocateStringMaybe(name);
+ return *this;
+ }
+
+ TStructBuilderRaw TStructBuilderRaw::SetName(TMaybe<TStringBuf> name) && noexcept {
+ return std::move(SetName(name));
+ }
+
+ bool TStructBuilderRaw::HasName() const noexcept {
+ return Name_.Defined();
+ }
+
+ TMaybe<TStringBuf> TStructBuilderRaw::GetName() const noexcept {
+ return Name_;
+ }
+
+ TStructBuilderRaw& TStructBuilderRaw::Reserve(size_t size) & noexcept {
+ Members_.reserve(size);
+ return *this;
+ }
+
+ TStructBuilderRaw TStructBuilderRaw::Reserve(size_t size) && noexcept {
+ return std::move(Reserve(size));
+ }
+
+ TStructBuilderRaw& TStructBuilderRaw::AddMember(TStringBuf name, TTypePtr type) & noexcept {
+ return AddMember(name, type.Get());
+ }
+
+ TStructBuilderRaw TStructBuilderRaw::AddMember(TStringBuf name, TTypePtr type) && noexcept {
+ return std::move(AddMember(name, type));
+ }
+
+ TStructBuilderRaw& TStructBuilderRaw::AddMember(TStringBuf name, const TType* type) & noexcept {
+ Members_.emplace_back(Factory_->AllocateString(name), Factory_->Own(type));
+ return *this;
+ }
+
+ TStructBuilderRaw TStructBuilderRaw::AddMember(TStringBuf name, const TType* type) && noexcept {
+ return std::move(AddMember(name, type));
+ }
+
+ TStructBuilderRaw& TStructBuilderRaw::AddMemberName(TStringBuf name) & noexcept {
+ PendingMemberName_ = Factory_->AllocateString(name);
+ return *this;
+ }
+
+ TStructBuilderRaw TStructBuilderRaw::AddMemberName(TStringBuf name) && noexcept {
+ return std::move(AddMemberName(name));
+ }
+
+ TStructBuilderRaw& TStructBuilderRaw::DiscardMemberName() & noexcept {
+ PendingMemberName_.Clear();
+ return *this;
+ }
+
+ TStructBuilderRaw TStructBuilderRaw::DiscardMemberName() && noexcept {
+ return std::move(DiscardMemberName());
+ }
+
+ bool TStructBuilderRaw::HasMemberName() const noexcept {
+ return PendingMemberName_.Defined();
+ }
+
+ TMaybe<TStringBuf> TStructBuilderRaw::GetMemberName() const noexcept {
+ return PendingMemberName_;
+ }
+
+ TStructBuilderRaw& TStructBuilderRaw::AddMemberType(TTypePtr type) & noexcept {
+ return AddMemberType(type.Get());
+ }
+
+ TStructBuilderRaw TStructBuilderRaw::AddMemberType(TTypePtr type) && noexcept {
+ return std::move(AddMemberType(type));
+ }
+
+ TStructBuilderRaw& TStructBuilderRaw::AddMemberType(const TType* type) & noexcept {
+ PendingMemberType_ = Factory_->Own(type);
+ return *this;
+ }
+
+ TStructBuilderRaw TStructBuilderRaw::AddMemberType(const TType* type) && noexcept {
+ return std::move(AddMemberType(type));
+ }
+
+ TStructBuilderRaw& TStructBuilderRaw::DiscardMemberType() & noexcept {
+ PendingMemberType_.Clear();
+ return *this;
+ }
+
+ TStructBuilderRaw TStructBuilderRaw::DiscardMemberType() && noexcept {
+ return std::move(DiscardMemberType());
+ }
+
+ bool TStructBuilderRaw::HasMemberType() const noexcept {
+ return PendingMemberType_.Defined();
+ }
+
+ TMaybe<const TType*> TStructBuilderRaw::GetMemberType() const noexcept {
+ return PendingMemberType_;
+ }
+
+ bool TStructBuilderRaw::CanAddMember() const noexcept {
+ return HasMemberName() && HasMemberType();
+ }
+
+ TStructBuilderRaw& TStructBuilderRaw::AddMember() & noexcept {
+ Y_VERIFY(CanAddMember());
+ Members_.emplace_back(*PendingMemberName_, *PendingMemberType_);
+ DiscardMember();
+ return *this;
+ }
+
+ TStructBuilderRaw TStructBuilderRaw::AddMember() && noexcept {
+ return std::move(AddMember());
+ }
+
+ TStructBuilderRaw& TStructBuilderRaw::DiscardMember() & noexcept {
+ DiscardMemberName();
+ DiscardMemberType();
+ return *this;
+ }
+
+ TStructBuilderRaw TStructBuilderRaw::DiscardMember() && noexcept {
+ return std::move(DiscardMember());
+ }
+
+ TStructType::TMembers TStructBuilderRaw::GetMembers() const noexcept {
+ return Members_;
+ }
+
+ TStructBuilderRaw& TStructBuilderRaw::Reset() & noexcept {
+ Name_ = {};
+ Members_.clear();
+ return *this;
+ }
+
+ TStructBuilderRaw TStructBuilderRaw::Reset() && noexcept {
+ return std::move(Reset());
+ }
+
+ TStructTypePtr TStructBuilderRaw::Build() {
+ return BuildRaw()->AsPtr();
+ }
+
+ const TStructType* TStructBuilderRaw::BuildRaw() {
+ return DoBuildRaw(Name_);
+ }
+
+ TVariantTypePtr TStructBuilderRaw::BuildVariant() {
+ return BuildVariantRaw()->AsPtr();
+ }
+
+ const TVariantType* TStructBuilderRaw::BuildVariantRaw() {
+ return Factory_->New<TVariantType>(Nothing(), Name_, DoBuildRaw(Nothing()));
+ }
+
+ const TStructType* TStructBuilderRaw::DoBuildRaw(TMaybe<TStringBuf> name) {
+ auto members = Factory_->NewArray<TStructType::TMember>(Members_.size(), [this](TStructType::TMember* member, size_t i) {
+ new (member) TStructType::TMember(Members_[i]);
+ });
+
+ auto sortedMembersArray = Factory_->AllocateArrayFor<size_t>(Members_.size());
+ auto sortedMembers = TArrayRef(sortedMembersArray, members.size());
+ TStructType::MakeSortedMembers(members, sortedMembers);
+
+ return Factory_->New<TStructType>(Nothing(), name, members, sortedMembers);
+ }
+
+ TTupleBuilderRaw::TTupleBuilderRaw(IPoolTypeFactory& factory) noexcept
+ : Factory_(&factory)
+ {
+ }
+
+ TTupleBuilderRaw::TTupleBuilderRaw(IPoolTypeFactory& factory, TTupleTypePtr prototype) noexcept
+ : TTupleBuilderRaw(factory, prototype.Get())
+ {
+ }
+
+ TTupleBuilderRaw::TTupleBuilderRaw(IPoolTypeFactory& factory, const TTupleType* prototype) noexcept
+ : TTupleBuilderRaw(factory)
+ {
+ Elements_.reserve(prototype->GetElements().size());
+
+ if (prototype->GetFactory() == Factory_) {
+ // elements are in the same factory -- can reuse them
+ for (auto element : prototype->GetElements()) {
+ Elements_.push_back(element);
+ }
+ } else {
+ // items are in a different factory -- should copy them
+ for (auto element : prototype->GetElements()) {
+ AddElement(element.GetTypeRaw());
+ }
+ }
+ }
+
+ TTupleBuilderRaw& TTupleBuilderRaw::SetName(TMaybe<TStringBuf> name) & noexcept {
+ Name_ = Factory_->AllocateStringMaybe(name);
+ return *this;
+ }
+
+ TTupleBuilderRaw TTupleBuilderRaw::SetName(TMaybe<TStringBuf> name) && noexcept {
+ return std::move(SetName(name));
+ }
+
+ bool TTupleBuilderRaw::HasName() const noexcept {
+ return Name_.Defined();
+ }
+
+ TMaybe<TStringBuf> TTupleBuilderRaw::GetName() const noexcept {
+ return Name_;
+ }
+
+ TTupleBuilderRaw& TTupleBuilderRaw::Reserve(size_t size) & noexcept {
+ Elements_.reserve(size);
+ return *this;
+ }
+
+ TTupleBuilderRaw TTupleBuilderRaw::Reserve(size_t size) && noexcept {
+ return std::move(Reserve(size));
+ }
+
+ TTupleBuilderRaw& TTupleBuilderRaw::AddElement(TTypePtr type) & noexcept {
+ return AddElement(type.Get());
+ }
+
+ TTupleBuilderRaw TTupleBuilderRaw::AddElement(TTypePtr type) && noexcept {
+ return std::move(AddElement(type));
+ }
+
+ TTupleBuilderRaw& TTupleBuilderRaw::AddElement(const TType* type) & noexcept {
+ Elements_.emplace_back(Factory_->Own(type));
+ return *this;
+ }
+
+ TTupleBuilderRaw TTupleBuilderRaw::AddElement(const TType* type) && noexcept {
+ return std::move(AddElement(type));
+ }
+
+ TTupleBuilderRaw& TTupleBuilderRaw::AddElementType(TTypePtr type) & noexcept {
+ return AddElementType(type.Get());
+ }
+
+ TTupleBuilderRaw TTupleBuilderRaw::AddElementType(TTypePtr type) && noexcept {
+ return std::move(AddElementType(type));
+ }
+
+ TTupleBuilderRaw& TTupleBuilderRaw::AddElementType(const TType* type) & noexcept {
+ PendingElementType_ = Factory_->Own(type);
+ return *this;
+ }
+
+ TTupleBuilderRaw TTupleBuilderRaw::AddElementType(const TType* type) && noexcept {
+ return std::move(AddElementType(type));
+ }
+
+ TTupleBuilderRaw& TTupleBuilderRaw::DiscardElementType() & noexcept {
+ PendingElementType_.Clear();
+ return *this;
+ }
+
+ TTupleBuilderRaw TTupleBuilderRaw::DiscardElementType() && noexcept {
+ return std::move(DiscardElementType());
+ }
+
+ bool TTupleBuilderRaw::HasElementType() const noexcept {
+ return PendingElementType_.Defined();
+ }
+
+ TMaybe<const TType*> TTupleBuilderRaw::GetElementType() const noexcept {
+ return PendingElementType_;
+ }
+
+ bool TTupleBuilderRaw::CanAddElement() const noexcept {
+ return HasElementType();
+ }
+
+ TTupleBuilderRaw& TTupleBuilderRaw::AddElement() & noexcept {
+ Y_VERIFY(CanAddElement());
+ Elements_.emplace_back(*PendingElementType_);
+ DiscardElement();
+ return *this;
+ }
+
+ TTupleBuilderRaw TTupleBuilderRaw::AddElement() && noexcept {
+ return std::move(AddElement());
+ }
+
+ TTupleBuilderRaw& TTupleBuilderRaw::DiscardElement() & noexcept {
+ DiscardElementType();
+ return *this;
+ }
+
+ TTupleBuilderRaw TTupleBuilderRaw::DiscardElement() && noexcept {
+ return std::move(DiscardElement());
+ }
+
+ TTupleBuilderRaw& TTupleBuilderRaw::Reset() & noexcept {
+ Name_ = {};
+ Elements_.clear();
+ return *this;
+ }
+
+ TTupleType::TElements TTupleBuilderRaw::GetElements() const noexcept {
+ return Elements_;
+ }
+
+ TTupleBuilderRaw TTupleBuilderRaw::Reset() && noexcept {
+ return std::move(Reset());
+ }
+
+ TTupleTypePtr TTupleBuilderRaw::Build() {
+ return BuildRaw()->AsPtr();
+ }
+
+ const TTupleType* TTupleBuilderRaw::BuildRaw() {
+ return DoBuildRaw(Name_);
+ }
+
+ TVariantTypePtr TTupleBuilderRaw::BuildVariant() {
+ return BuildVariantRaw()->AsPtr();
+ }
+
+ const TVariantType* TTupleBuilderRaw::BuildVariantRaw() {
+ return Factory_->New<TVariantType>(Nothing(), Name_, DoBuildRaw(Nothing()));
+ }
+
+ const TTupleType* TTupleBuilderRaw::DoBuildRaw(TMaybe<TStringBuf> name) {
+ auto items = Factory_->NewArray<TTupleType::TElement>(Elements_.size(), [this](TTupleType::TElement* element, size_t i) {
+ new (element) TTupleType::TElement(Elements_[i]);
+ });
+
+ return Factory_->New<TTupleType>(Nothing(), name, items);
+ }
+
+ TTaggedBuilderRaw::TTaggedBuilderRaw(IPoolTypeFactory& factory) noexcept
+ : Factory_(&factory)
+ {
+ }
+
+ TTaggedBuilderRaw& TTaggedBuilderRaw::SetTag(TStringBuf tag) & noexcept {
+ Tag_ = Factory_->AllocateString(tag);
+ return *this;
+ }
+
+ TTaggedBuilderRaw TTaggedBuilderRaw::SetTag(TStringBuf tag) && noexcept {
+ return std::move(SetTag(tag));
+ }
+
+ TTaggedBuilderRaw& TTaggedBuilderRaw::DiscardTag() & noexcept {
+ Tag_.Clear();
+ return *this;
+ }
+
+ TTaggedBuilderRaw TTaggedBuilderRaw::DiscardTag() && noexcept {
+ return std::move(DiscardTag());
+ }
+
+ bool TTaggedBuilderRaw::HasTag() const noexcept {
+ return Tag_.Defined();
+ }
+
+ TMaybe<TStringBuf> TTaggedBuilderRaw::GetTag() const noexcept {
+ return Tag_;
+ }
+
+ TTaggedBuilderRaw& TTaggedBuilderRaw::SetItem(TTypePtr type) & noexcept {
+ return SetItem(type.Get());
+ }
+
+ TTaggedBuilderRaw TTaggedBuilderRaw::SetItem(TTypePtr type) && noexcept {
+ return std::move(SetItem(std::move(type)));
+ }
+
+ TTaggedBuilderRaw& TTaggedBuilderRaw::SetItem(const TType* type) & noexcept {
+ Item_ = Factory_->Own(type);
+ return *this;
+ }
+
+ TTaggedBuilderRaw TTaggedBuilderRaw::SetItem(const TType* type) && noexcept {
+ return std::move(SetItem(type));
+ }
+
+ TTaggedBuilderRaw& TTaggedBuilderRaw::DiscardItem() & noexcept {
+ Item_.Clear();
+ return *this;
+ }
+
+ TTaggedBuilderRaw TTaggedBuilderRaw::DiscardItem() && noexcept {
+ return std::move(DiscardItem());
+ }
+
+ bool TTaggedBuilderRaw::HasItem() const noexcept {
+ return Item_.Defined();
+ }
+
+ TMaybe<const TType*> TTaggedBuilderRaw::GetItem() const noexcept {
+ return Item_;
+ }
+
+ bool TTaggedBuilderRaw::CanBuild() const noexcept {
+ return HasTag() && HasItem();
+ }
+
+ TTaggedBuilderRaw& TTaggedBuilderRaw::Reset() & noexcept {
+ DiscardTag();
+ DiscardItem();
+ return *this;
+ }
+
+ TTaggedBuilderRaw TTaggedBuilderRaw::Reset() && noexcept {
+ return std::move(Reset());
+ }
+
+ TTaggedTypePtr TTaggedBuilderRaw::Build() {
+ return BuildRaw()->AsPtr();
+ }
+
+ const TTaggedType* TTaggedBuilderRaw::BuildRaw() {
+ Y_VERIFY(CanBuild());
+ return Factory_->New<TTaggedType>(Nothing(), *Item_, *Tag_);
+ }
+}
diff --git a/library/cpp/type_info/builder.h b/library/cpp/type_info/builder.h
new file mode 100644
index 0000000000..faae45ab51
--- /dev/null
+++ b/library/cpp/type_info/builder.h
@@ -0,0 +1,346 @@
+#pragma once
+
+//! @file builder.h
+//!
+//! Builders help with creating complex types piece-by-piece.
+
+#include <util/generic/vector.h>
+
+#include "type.h"
+
+#include "fwd.h"
+
+namespace NTi {
+ /// An interface for building structs using the raw interface (via memory-pool-based factory).
+ ///
+ /// This interface allows allocating structs piece-by-piece. You can feed it struct items, one-by-one, and they'll
+ /// be copied into the memory pool before the struct is created. This way, you don't have to allocate heap memory
+ /// to temporarily store item names or types.
+ ///
+ /// Note: this builder doesn't own the underlying factory.
+ class TStructBuilderRaw {
+ public:
+ /// Create a new builder with the given factory.
+ TStructBuilderRaw(IPoolTypeFactory& factory) noexcept;
+
+ /// Create a new builder with the given factory and add fields from the given struct.
+ /// Note that struct name is not copied to the builder.
+ TStructBuilderRaw(IPoolTypeFactory& factory, TStructTypePtr prototype) noexcept;
+ TStructBuilderRaw(IPoolTypeFactory& factory, const TStructType* prototype) noexcept;
+
+ /// Set a new struct name.
+ ///
+ /// Note that the name is copied to the factory right away. If you call this function twice, second call will
+ /// overwrite name from the first call, but will not remove it from the memory pool.
+ TStructBuilderRaw& SetName(TMaybe<TStringBuf> name) & noexcept;
+ TStructBuilderRaw SetName(TMaybe<TStringBuf> name) && noexcept;
+
+ /// Check if there's a struct name set.
+ bool HasName() const noexcept;
+
+ /// Get a struct name.
+ ///
+ /// Name was copied to the factory's memory pool and will live as long as the factory lives.
+ TMaybe<TStringBuf> GetName() const noexcept;
+
+ /// Reserve some place in the underlying vector that collects struct items.
+ TStructBuilderRaw& Reserve(size_t size) & noexcept;
+ TStructBuilderRaw Reserve(size_t size) && noexcept;
+
+ /// Append a new struct member.
+ TStructBuilderRaw& AddMember(TStringBuf name, TTypePtr type) & noexcept;
+ TStructBuilderRaw AddMember(TStringBuf name, TTypePtr type) && noexcept;
+ TStructBuilderRaw& AddMember(TStringBuf name, const TType* type) & noexcept;
+ TStructBuilderRaw AddMember(TStringBuf name, const TType* type) && noexcept;
+
+ /// Partial member creation interface.
+ ///
+ /// This interface allows building individual struct items piece-by-piece. You can pass member's name
+ /// and copy it to the pool without passing its type. For example:
+ ///
+ /// ```
+ /// auto builder = TStructBuilderRaw(factory);
+ /// builder.AddMemberName("name"); // add name for a new member.
+ /// builder.AddMemberType(type); // add type for a new member.
+ /// builder.AddMember(); // use added name and type to construct and append an member.
+ /// ```
+ ///
+ /// This interface is useful when you can't store member name for a long time. For example, if you building
+ /// a parser, at some point have an member name, but you don't have an member type yet. Then you can add member name
+ /// when you have it, and add member type later.
+ ///
+ /// @{
+ //-
+ /// Set name for pending member.
+ ///
+ /// Note that the name is copied to the factory right away. If you call this function twice, second call will
+ /// overwrite name from the first call, but will not remove it from the memory pool.
+ TStructBuilderRaw& AddMemberName(TStringBuf name) & noexcept;
+ TStructBuilderRaw AddMemberName(TStringBuf name) && noexcept;
+
+ /// Unset name for pending member.
+ ///
+ /// Note that member name is copied to the factory right away. If you discard it via this method,
+ /// it will not be removed from the memory pool.
+ TStructBuilderRaw& DiscardMemberName() & noexcept;
+ TStructBuilderRaw DiscardMemberName() && noexcept;
+
+ /// Check if there's a name set for pending member.
+ bool HasMemberName() const noexcept;
+
+ /// Get name for pending member.
+ ///
+ /// Name was copied to the factory's memory pool and will live as long as the factory lives.
+ TMaybe<TStringBuf> GetMemberName() const noexcept;
+
+ /// Set type for pending member.
+ ///
+ /// Note that the type is copied to the factory right away. If you call this function twice, second call will
+ /// overwrite type from the first call, but will not remove it from the memory pool.
+ TStructBuilderRaw& AddMemberType(TTypePtr type) & noexcept;
+ TStructBuilderRaw AddMemberType(TTypePtr type) && noexcept;
+ TStructBuilderRaw& AddMemberType(const TType* type) & noexcept;
+ TStructBuilderRaw AddMemberType(const TType* type) && noexcept;
+
+ /// Unset type for pending member.
+ ///
+ /// Note that member type is copied to the factory right away. If you discard it via this method,
+ /// it will not be removed from the memory pool.
+ TStructBuilderRaw& DiscardMemberType() & noexcept;
+ TStructBuilderRaw DiscardMemberType() && noexcept;
+
+ /// Check if there's a type set for pending member.
+ bool HasMemberType() const noexcept;
+
+ /// Get type for pending member.
+ TMaybe<const TType*> GetMemberType() const noexcept;
+
+ /// Check if both name and type are set for pending member.
+ bool CanAddMember() const noexcept;
+
+ /// Use data added via `AddMemberName` and `AddMemberType` to construct a new struct member
+ /// and append it to this builder. This function panics if there's no name or no type set for pending member.
+ TStructBuilderRaw& AddMember() & noexcept;
+ TStructBuilderRaw AddMember() && noexcept;
+
+ /// Discard all data added via `AddMemberName` and `AddMemberType` functions.
+ ///
+ /// Note that member name and type are copied to the factory right away. If you discard them via this method,
+ /// they will not be removed from the memory pool.
+ TStructBuilderRaw& DiscardMember() & noexcept;
+ TStructBuilderRaw DiscardMember() && noexcept;
+
+ /// @}
+
+ /// Get immutable list of items that've been added to this builder.
+ /// The returned object is invalidated when this builder dies or when `AddMember` is called.
+ TStructType::TMembers GetMembers() const noexcept;
+
+ /// Reset struct name and items.
+ TStructBuilderRaw& Reset() & noexcept;
+ TStructBuilderRaw Reset() && noexcept;
+
+ /// Create a new struct type using name and items from this builder.
+ TStructTypePtr Build();
+
+ /// Like `Build`, but returns a raw pointer.
+ const TStructType* BuildRaw();
+
+ /// Create a new variant over struct using items from this builder.
+ TVariantTypePtr BuildVariant();
+
+ /// Like `BuildVariant`, but returns a raw pointer.
+ const TVariantType* BuildVariantRaw();
+
+ private:
+ const TStructType* DoBuildRaw(TMaybe<TStringBuf> name);
+
+ private:
+ IPoolTypeFactory* Factory_;
+ TMaybe<TStringBuf> Name_;
+ TVector<TStructType::TMember> Members_;
+ TMaybe<TStringBuf> PendingMemberName_;
+ TMaybe<const TType*> PendingMemberType_;
+ };
+
+ /// An interface for building tuples using the raw interface (via memory-pool-based factory).
+ ///
+ /// Note: this builder doesn't own the underlying factory.
+ class TTupleBuilderRaw {
+ public:
+ /// Create a new builder with the given factory.
+ TTupleBuilderRaw(IPoolTypeFactory& factory) noexcept;
+
+ /// Create a new builder with the given factory and add fields from the given tuple.
+ /// Note that tuple name is not copied to the builder.
+ TTupleBuilderRaw(IPoolTypeFactory& factory, TTupleTypePtr prototype) noexcept;
+ TTupleBuilderRaw(IPoolTypeFactory& factory, const TTupleType* prototype) noexcept;
+
+ /// Set a new tuple name.
+ /// Note that the name is copied to the factory right away. If you call this function twice, second call will
+ /// overwrite name from the first call, but will not remove it from the memory pool.
+ TTupleBuilderRaw& SetName(TMaybe<TStringBuf> name) & noexcept;
+ TTupleBuilderRaw SetName(TMaybe<TStringBuf> name) && noexcept;
+
+ /// Check if there's a tuple name set.
+ bool HasName() const noexcept;
+
+ /// Get a tuple name.
+ ///
+ /// Name was copied to the factory's memory pool and will live as long as the factory lives.
+ TMaybe<TStringBuf> GetName() const noexcept;
+
+ /// Reserve some place in the underlying vector that collects tuple items.
+ TTupleBuilderRaw& Reserve(size_t size) & noexcept;
+ TTupleBuilderRaw Reserve(size_t size) && noexcept;
+
+ /// Append a new tuple item.
+ TTupleBuilderRaw& AddElement(TTypePtr type) & noexcept;
+ TTupleBuilderRaw AddElement(TTypePtr type) && noexcept;
+ TTupleBuilderRaw& AddElement(const TType* type) & noexcept;
+ TTupleBuilderRaw AddElement(const TType* type) && noexcept;
+
+ /// Partial item creation interface.
+ ///
+ /// This interface allows building individual tuple items piece-by-piece. It mirrors the same type of interface
+ /// in the struct builder.
+ ///
+ /// @{
+ //-
+ /// Set type for pending item.
+ ///
+ /// Note that the type is copied to the factory right away. If you call this function twice, second call will
+ /// overwrite type from the first call, but will not remove it from the memory pool.
+ TTupleBuilderRaw& AddElementType(TTypePtr type) & noexcept;
+ TTupleBuilderRaw AddElementType(TTypePtr type) && noexcept;
+ TTupleBuilderRaw& AddElementType(const TType* type) & noexcept;
+ TTupleBuilderRaw AddElementType(const TType* type) && noexcept;
+
+ /// Unset type for pending item.
+ ///
+ /// Note that item type is copied to the factory right away. If you discard it via this method,
+ /// it will not be removed from the memory pool.
+ TTupleBuilderRaw& DiscardElementType() & noexcept;
+ TTupleBuilderRaw DiscardElementType() && noexcept;
+
+ /// Check if there's a type set for pending item.
+ bool HasElementType() const noexcept;
+
+ /// Get type for pending item.
+ TMaybe<const TType*> GetElementType() const noexcept;
+
+ /// Check if type is set for pending item.
+ bool CanAddElement() const noexcept;
+
+ /// Use data added via `AddElementType` to construct a new tuple item and append it to this builder.
+ /// This function panics if there's no type set for pending item.
+ TTupleBuilderRaw& AddElement() & noexcept;
+ TTupleBuilderRaw AddElement() && noexcept;
+
+ /// Discard all data added via `AddElementType` function.
+ ///
+ /// Note that item type is copied to the factory right away. If you discard it via this method,
+ /// it will not be removed from the memory pool.
+ TTupleBuilderRaw& DiscardElement() & noexcept;
+ TTupleBuilderRaw DiscardElement() && noexcept;
+
+ /// @}
+
+ /// Get immutable list of items that've been added to this builder.
+ /// The returned object is invalidated when this builder dies or when `AddElement` is called.
+ TTupleType::TElements GetElements() const noexcept;
+
+ /// Reset tuple name and items.
+ TTupleBuilderRaw& Reset() & noexcept;
+ TTupleBuilderRaw Reset() && noexcept;
+
+ /// Create a new tuple type using name and items from this builder.
+ TTupleTypePtr Build();
+
+ /// Like `Build`, but returns a raw pointer.
+ const TTupleType* BuildRaw();
+
+ /// Create a new variant over tuple using items from this builder.
+ TVariantTypePtr BuildVariant();
+
+ /// Like `BuildVariant`, but returns a raw pointer.
+ const TVariantType* BuildVariantRaw();
+
+ private:
+ const TTupleType* DoBuildRaw(TMaybe<TStringBuf> name);
+
+ private:
+ IPoolTypeFactory* Factory_;
+ TMaybe<TStringBuf> Name_;
+ TVector<TTupleType::TElement> Elements_;
+ TMaybe<const TType*> PendingElementType_;
+ };
+
+ /// An interface for building tagged types using the raw interface (via memory-pool-based factory).
+ ///
+ /// Note: this builder doesn't own the underlying factory.
+ class TTaggedBuilderRaw {
+ public:
+ /// Create a new builder with the given factory.
+ TTaggedBuilderRaw(IPoolTypeFactory& factory) noexcept;
+
+ /// Set a new tag.
+ ///
+ /// Note that the tag is copied to the factory right away. If you call this function twice, second call will
+ /// overwrite tag from the first call, but will not remove it from the memory pool.
+ TTaggedBuilderRaw& SetTag(TStringBuf tag) & noexcept;
+ TTaggedBuilderRaw SetTag(TStringBuf tag) && noexcept;
+
+ /// Unset tag.
+ ///
+ /// Note that tag is copied to the factory right away. If you discard it via this method,
+ /// it will not be removed from the memory pool.
+ TTaggedBuilderRaw& DiscardTag() & noexcept;
+ TTaggedBuilderRaw DiscardTag() && noexcept;
+
+ /// Check if a tag is set.
+ bool HasTag() const noexcept;
+
+ /// Get a tag.
+ ///
+ /// The tag was copied to the factory's memory pool and will live as long as the factory lives.
+ TMaybe<TStringBuf> GetTag() const noexcept;
+
+ /// Set type that's being tagged.
+ TTaggedBuilderRaw& SetItem(TTypePtr type) & noexcept;
+ TTaggedBuilderRaw SetItem(TTypePtr type) && noexcept;
+ TTaggedBuilderRaw& SetItem(const TType* type) & noexcept;
+ TTaggedBuilderRaw SetItem(const TType* type) && noexcept;
+
+ /// Unset item type.
+ ///
+ /// Note that item type is copied to the factory right away. If you discard it via this method,
+ /// it will not be removed from the memory pool.
+ TTaggedBuilderRaw& DiscardItem() & noexcept;
+ TTaggedBuilderRaw DiscardItem() && noexcept;
+
+ /// Check if there's an item type set.
+ bool HasItem() const noexcept;
+
+ /// Get item type.
+ TMaybe<const TType*> GetItem() const noexcept;
+
+ /// Check if there's both name and item type set.
+ bool CanBuild() const noexcept;
+
+ /// Discard both tag and item.
+ TTaggedBuilderRaw& Reset() & noexcept;
+ TTaggedBuilderRaw Reset() && noexcept;
+
+ /// Create a new tagged type using name and item from this builder.
+ TTaggedTypePtr Build();
+
+ /// Like `Build`, but returns a raw pointer.
+ const TTaggedType* BuildRaw();
+
+ private:
+ IPoolTypeFactory* Factory_;
+ TMaybe<TStringBuf> Tag_;
+ TMaybe<const TType*> Item_;
+ };
+}
diff --git a/library/cpp/type_info/error.cpp b/library/cpp/type_info/error.cpp
new file mode 100644
index 0000000000..fe9ecf1d9f
--- /dev/null
+++ b/library/cpp/type_info/error.cpp
@@ -0,0 +1 @@
+#include "error.h"
diff --git a/library/cpp/type_info/error.h b/library/cpp/type_info/error.h
new file mode 100644
index 0000000000..26fd8853f9
--- /dev/null
+++ b/library/cpp/type_info/error.h
@@ -0,0 +1,33 @@
+#pragma once
+
+//! @file error.h
+//!
+//! All error classes that one can encounter when working with type info library.
+
+#include <util/generic/yexception.h>
+
+namespace NTi {
+ /// Base class for all exceptions that arise when working with Type Info library.
+ class TException: public yexception {
+ };
+
+ /// Type Info API used in an unintended way.
+ class TApiException: public TException {
+ };
+
+ /// Attempting to create an illegal type.
+ ///
+ /// For example, this exception is raised when attempting to create a struct with non-unique item names.
+ class TIllegalTypeException: public TException {
+ };
+
+ /// Type deserializer got an invalid input.
+ ///
+ /// See `TType::Serialize()` and `TType::Deserialize()` for more info on type serialization/deserialization.
+ class TDeserializationException: public TException {
+ };
+
+ /// No such item in type.
+ class TItemNotFound: public TException {
+ };
+}
diff --git a/library/cpp/type_info/fwd.h b/library/cpp/type_info/fwd.h
new file mode 100644
index 0000000000..644932f677
--- /dev/null
+++ b/library/cpp/type_info/fwd.h
@@ -0,0 +1,123 @@
+#pragma once
+
+#include <util/generic/fwd.h>
+
+namespace NTi {
+ class ITypeFactoryInternal;
+
+ class TNamedTypeBuilderRaw;
+ class TStructBuilderRaw;
+ class TTupleBuilderRaw;
+ class TTaggedBuilderRaw;
+
+ class ITypeFactory;
+ using ITypeFactoryPtr = TIntrusivePtr<ITypeFactory>;
+
+ class IPoolTypeFactory;
+ using IPoolTypeFactoryPtr = TIntrusivePtr<IPoolTypeFactory>;
+
+ class TType;
+ using TTypePtr = TIntrusiveConstPtr<TType>;
+
+ class TVoidType;
+ using TVoidTypePtr = TIntrusiveConstPtr<TVoidType>;
+
+ class TNullType;
+ using TNullTypePtr = TIntrusiveConstPtr<TNullType>;
+
+ class TPrimitiveType;
+ using TPrimitiveTypePtr = TIntrusiveConstPtr<TPrimitiveType>;
+
+ class TBoolType;
+ using TBoolTypePtr = TIntrusiveConstPtr<TBoolType>;
+
+ class TInt8Type;
+ using TInt8TypePtr = TIntrusiveConstPtr<TInt8Type>;
+
+ class TInt16Type;
+ using TInt16TypePtr = TIntrusiveConstPtr<TInt16Type>;
+
+ class TInt32Type;
+ using TInt32TypePtr = TIntrusiveConstPtr<TInt32Type>;
+
+ class TInt64Type;
+ using TInt64TypePtr = TIntrusiveConstPtr<TInt64Type>;
+
+ class TUint8Type;
+ using TUint8TypePtr = TIntrusiveConstPtr<TUint8Type>;
+
+ class TUint16Type;
+ using TUint16TypePtr = TIntrusiveConstPtr<TUint16Type>;
+
+ class TUint32Type;
+ using TUint32TypePtr = TIntrusiveConstPtr<TUint32Type>;
+
+ class TUint64Type;
+ using TUint64TypePtr = TIntrusiveConstPtr<TUint64Type>;
+
+ class TFloatType;
+ using TFloatTypePtr = TIntrusiveConstPtr<TFloatType>;
+
+ class TDoubleType;
+ using TDoubleTypePtr = TIntrusiveConstPtr<TDoubleType>;
+
+ class TStringType;
+ using TStringTypePtr = TIntrusiveConstPtr<TStringType>;
+
+ class TUtf8Type;
+ using TUtf8TypePtr = TIntrusiveConstPtr<TUtf8Type>;
+
+ class TDateType;
+ using TDateTypePtr = TIntrusiveConstPtr<TDateType>;
+
+ class TDatetimeType;
+ using TDatetimeTypePtr = TIntrusiveConstPtr<TDatetimeType>;
+
+ class TTimestampType;
+ using TTimestampTypePtr = TIntrusiveConstPtr<TTimestampType>;
+
+ class TTzDateType;
+ using TTzDateTypePtr = TIntrusiveConstPtr<TTzDateType>;
+
+ class TTzDatetimeType;
+ using TTzDatetimeTypePtr = TIntrusiveConstPtr<TTzDatetimeType>;
+
+ class TTzTimestampType;
+ using TTzTimestampTypePtr = TIntrusiveConstPtr<TTzTimestampType>;
+
+ class TIntervalType;
+ using TIntervalTypePtr = TIntrusiveConstPtr<TIntervalType>;
+
+ class TDecimalType;
+ using TDecimalTypePtr = TIntrusiveConstPtr<TDecimalType>;
+
+ class TJsonType;
+ using TJsonTypePtr = TIntrusiveConstPtr<TJsonType>;
+
+ class TYsonType;
+ using TYsonTypePtr = TIntrusiveConstPtr<TYsonType>;
+
+ class TUuidType;
+ using TUuidTypePtr = TIntrusiveConstPtr<TUuidType>;
+
+ class TOptionalType;
+ using TOptionalTypePtr = TIntrusiveConstPtr<TOptionalType>;
+
+ class TListType;
+ using TListTypePtr = TIntrusiveConstPtr<TListType>;
+
+ class TDictType;
+ using TDictTypePtr = TIntrusiveConstPtr<TDictType>;
+
+ class TStructType;
+ using TStructTypePtr = TIntrusiveConstPtr<TStructType>;
+
+ class TTupleType;
+ using TTupleTypePtr = TIntrusiveConstPtr<TTupleType>;
+
+ class TVariantType;
+ using TVariantTypePtr = TIntrusiveConstPtr<TVariantType>;
+
+ class TTaggedType;
+ using TTaggedTypePtr = TIntrusiveConstPtr<TTaggedType>;
+}
diff --git a/library/cpp/type_info/test-data/bad-types.txt b/library/cpp/type_info/test-data/bad-types.txt
new file mode 100644
index 0000000000..c8f3120f63
--- /dev/null
+++ b/library/cpp/type_info/test-data/bad-types.txt
@@ -0,0 +1,229 @@
+#
+# The format of this file is described in README.txt
+#
+# Each test case contains 3 fields:
+# - Yson representation (that cannot be parsed to type).
+# - Expected error description.
+# - Path in yson that we expect to see in error.
+#
+# Suggested approach to writing test:
+# 1. Try to deserialize type from yson (1st field).
+# 2. Ensure that error is raised.
+# 3. (Optionally) ensure that error description matches (contains) 2nd field.
+# 4. (Optionally) ensure that error description contains path from 3rd field
+
+5 :: type must be either a string or a map :: ;;
+
+"" :: unknown type "" :: ;;
+
+"int" :: unknown type "int" :: ;;
+
+# Type names must be in lowercase.
+"Int32" :: unknown type "Int32" :: ;;
+
+{typename=int32} :: missing required key "type_name" :: ;;
+
+{type_name=5} :: "type_name" must contain a string :: ;;
+
+#
+# Decimal
+#
+
+{
+ type_name=decimal;
+ precision=3;
+} :: missing required key "scale" :: ;;
+
+{
+ type_name=decimal;
+ scale=3;
+} :: missing required key "precision" :: ;;
+
+#
+# Optional
+#
+
+{
+ type_name=optional;
+} :: missing required key "item" :: ;;
+
+{
+ item=int32;
+} :: missing required key "type_name" :: ;;
+
+#
+# List
+#
+
+{
+ type_name=list;
+} :: missing required key "item" :: ;;
+
+#
+# Struct
+#
+
+{
+ type_name=struct;
+} :: missing required key "members" :: ;;
+
+{
+ type_name=struct;
+ members=5;
+} :: "members" must contain a list :: ;;
+
+{
+ type_name=struct;
+ members=[
+ {name=foo; type=int32;};
+ 5;
+ ];
+} :: "members" must contain a list of maps :: ;;
+
+{
+ type_name=struct;
+ members=[
+ {type=int32;};
+ ];
+} :: missing required key "name" :: ;;
+
+{
+ type_name=struct;
+ members=[
+ {name=foo;};
+ ];
+} :: missing required key "type" :: ;;
+
+{
+ type_name=struct;
+ members=[
+ {name=foo; type=int32};
+ {name=bar; type=5};
+ ];
+} :: type must be either a string or a map :: /members/1/type ;;
+
+{
+ type_name=struct;
+ members=[
+ {name=4; type=int32};
+ ];
+} :: "name" must contain a string :: ;;
+
+{
+ type_name=struct;
+ elements=[
+ {name=4; type=int32};
+ ];
+} :: missing required key "members" :: ;;
+
+#
+# Tuple
+#
+
+{
+ type_name=tuple;
+} :: missing required key "elements" :: ;;
+
+{
+ type_name=tuple;
+ elements=5;
+} :: "elements" must contain a list :: ;;
+
+{
+ type_name=tuple;
+ elements=[
+ {type=int32;};
+ 5;
+ ];
+} :: "elements" must contain a list of maps :: ;;
+
+{
+ type_name=tuple;
+ elements=[
+ {};
+ ];
+} :: missing required key "type" :: /elements/0 ;;
+
+{
+ type_name=tuple;
+ elements=[
+ {type=5};
+ ];
+} :: type must be either a string or a map :: /elements/1/type ;;
+
+{
+ type_name=tuple;
+ members=[
+ {name=foo; type=int32};
+ ];
+} :: missing required key "elements" :: ;;
+
+#
+# Variant
+#
+
+# We don't specify exception message here because in C++ library that message is not good and we don't want to canonize it
+# Though fixing it is not that easy too.
+{
+ type_name=variant;
+ members=[
+ {name=foo; type=int32};
+ ];
+ elements=[
+ {type=int8};
+ ]
+} :: :: ;;
+
+{
+ type_name=variant;
+} :: missing both keys "members" and "elements" :: ;;
+
+#
+# Dict
+#
+{
+ type_name=dict;
+ key=string;
+} :: missing required key "value" :: ;;
+
+{
+ type_name=dict;
+ value=string;
+} :: missing required key "key" :: ;;
+
+{
+ type_name=dict;
+ value=string;
+} :: missing required key "key" :: ;;
+
+{
+ type_name=dict;
+ key=5;
+ value=string;
+} :: type must be either a string or a map :: /key ;;
+
+{
+ type_name=dict;
+ key=string;
+ value=5;
+} :: type must be either a string or a map :: /value ;;
+
+#
+# Tagged
+#
+
+{
+ type_name=tagged;
+ item=string;
+} :: missing required key "tag" :: ;;
+
+{
+ type_name=tagged;
+ tag=string;
+} :: missing required key "item" :: ;;
+
+{
+ type_name=tagged;
+ tag=5;
+ item=string;
+} :: "tag" must contain a string :: /tag ;;
diff --git a/library/cpp/type_info/test-data/good-types.txt b/library/cpp/type_info/test-data/good-types.txt
new file mode 100644
index 0000000000..cb082707b6
--- /dev/null
+++ b/library/cpp/type_info/test-data/good-types.txt
@@ -0,0 +1,478 @@
+#
+# The format of this file is described in README.txt
+#
+# Each test case contains 2 fields:
+# - Yson reporesentation of the type.
+# - String representation of the type.
+#
+# Suggested approach to writing test:
+# 1. Try to deserialize type from yson (1st field).
+# 2. Check that text representation of parsed type matches 2nd field.
+# 3. Serialize type to yson and deserialize it from it.
+# 4. Check that typef from 1. and 3. are equal.
+
+# Integer
+int8 :: Int8 ;;
+int16 :: Int16 ;;
+int32 :: Int32 ;;
+int64 :: Int64 ;;
+{type_name=int8} :: Int8 ;;
+{type_name=int16} :: Int16 ;;
+{type_name=int32} :: Int32 ;;
+{type_name=int64} :: Int64 ;;
+
+# Unsigned integer
+uint8 :: Uint8 ;;
+uint16 :: Uint16 ;;
+uint32 :: Uint32 ;;
+uint64 :: Uint64 ;;
+{type_name=uint8} :: Uint8 ;;
+{type_name=uint16} :: Uint16 ;;
+{type_name=uint32} :: Uint32 ;;
+{type_name=uint64} :: Uint64 ;;
+
+# Floating
+float :: Float ;;
+double :: Double ;;
+{type_name=float} :: Float ;;
+{type_name=double} :: Double ;;
+
+# Strings
+string :: String ;;
+utf8 :: Utf8 ;;
+{type_name=string} :: String ;;
+{type_name=utf8} :: Utf8 ;;
+
+# Time
+date :: Date ;;
+datetime :: Datetime ;;
+timestamp :: Timestamp ;;
+tz_date :: TzDate ;;
+tz_datetime :: TzDatetime ;;
+tz_timestamp :: TzTimestamp ;;
+interval :: Interval ;;
+{type_name=date} :: Date ;;
+{type_name=datetime} :: Datetime ;;
+{type_name=timestamp} :: Timestamp ;;
+{type_name=tz_date} :: TzDate ;;
+{type_name=tz_datetime} :: TzDatetime ;;
+{type_name=tz_timestamp} :: TzTimestamp ;;
+{type_name=interval} :: Interval ;;
+
+# Singular
+void :: Void ;;
+null :: Null ;;
+{type_name=void} :: Void ;;
+{type_name=null} :: Null ;;
+
+# UUID
+uuid :: Uuid ;;
+{type_name=uuid} :: Uuid ;;
+
+# Json / Yson
+yson :: Yson ;;
+json :: Json ;;
+{type_name=yson} :: Yson ;;
+{type_name=json} :: Json ;;
+
+{
+ type_name=string;
+ unknown_key=bar;
+} :: String ;;
+
+# Decimal
+{
+ type_name=decimal;
+ precision=3;
+ scale=2;
+} :: Decimal(3, 2) ;;
+
+{
+ type_name=decimal;
+ precision=3;
+ scale=2;
+ unknown_column=ha;
+} :: Decimal(3, 2) ;;
+
+#
+# Optional
+#
+{
+ type_name=optional;
+ item=string;
+} :: Optional<String> ;;
+
+{
+ type_name=optional;
+ item={
+ type_name=string;
+ }
+} :: Optional<String> ;;
+
+{
+ type_name=optional;
+ item={
+ type_name=optional;
+ item={
+ type_name=list;
+ item={
+ type_name=decimal;
+ precision=10;
+ scale=5;
+ }
+
+ }
+ }
+} :: Optional<Optional<List<Decimal(10, 5)>>> ;;
+
+{
+ type_name=optional;
+ item={
+ type_name=tagged;
+ item=int32;
+ tag="foo";
+ };
+} :: Optional<Tagged<Int32, 'foo'>> ;;
+
+{
+ type_name=optional;
+ item={
+ type_name=string;
+ };
+ unknown_column=ha;
+} :: Optional<String> ;;
+
+#
+# List
+#
+{
+ type_name=list;
+ item=string;
+} :: List<String> ;;
+
+{
+ type_name=list;
+ item={
+ type_name=string;
+ }
+} :: List<String> ;;
+
+{
+ type_name=list;
+ item={
+ type_name=list;
+ item={
+ type_name=optional;
+ item=string;
+ }
+ };
+} :: List<List<Optional<String>>> ;;
+
+{
+ type_name=list;
+ item={
+ type_name=tagged;
+ item=int32;
+ tag="foo";
+ };
+} :: List<Tagged<Int32, 'foo'>> ;;
+
+{
+ type_name=list;
+ item={
+ type_name=string;
+ };
+ unknown_column=ha;
+} :: List<String> ;;
+
+#
+# Dict
+#
+
+{
+ type_name=dict;
+ key=int32;
+ value=string;
+} :: Dict<Int32, String> ;;
+
+{
+ type_name=dict;
+ key={
+ type_name=optional;
+ item=string;
+ };
+ value={
+ type_name=list;
+ item={
+ type_name=int32;
+ }
+ };
+} :: Dict<Optional<String>, List<Int32>> ;;
+
+{
+ type_name=dict;
+ key={
+ type_name=tagged;
+ item=int32;
+ tag="foo";
+ };
+ value={
+ type_name=tagged;
+ item=string;
+ tag="bar";
+ };
+} :: Dict<Tagged<Int32, 'foo'>, Tagged<String, 'bar'>> ;;
+
+{
+ type_name=dict;
+ key=int32;
+ value=string;
+ unknown_column=ha;
+} :: Dict<Int32, String> ;;
+
+#
+# Struct
+#
+
+{
+ type_name=struct;
+ members=[];
+} :: Struct<> ;;
+
+{
+ type_name=struct;
+ members=[
+ {
+ name=foo;
+ type=int32;
+ };
+ {
+ name=bar;
+ type={
+ type_name=optional;
+ item=string;
+ };
+ };
+ ];
+} :: Struct<'foo': Int32, 'bar': Optional<String>> ;;
+
+{
+ type_name=struct;
+ members=[
+ {
+ name=foo;
+ type={
+ type_name=tagged;
+ item=string;
+ tag=foo;
+ };
+ };
+ ];
+} :: Struct<'foo': Tagged<String, 'foo'>> ;;
+
+{
+ type_name=struct;
+ unknown_column=ha;
+ members=[
+ {
+ unknown_column=ha;
+ name=foo;
+ type=int32;
+ };
+ {
+ name=bar;
+ type={
+ type_name=optional;
+ item=string;
+ };
+ };
+ ];
+} :: Struct<'foo': Int32, 'bar': Optional<String>> ;;
+
+#
+# Tuple
+#
+
+{
+ type_name=tuple;
+ elements=[];
+} :: Tuple<> ;;
+
+{
+ type_name=tuple;
+ elements=[
+ {
+ type=int32;
+ };
+ {
+ type={
+ type_name=optional;
+ item=string;
+ };
+ };
+ ];
+} :: Tuple<Int32, Optional<String>> ;;
+
+{
+ type_name=tuple;
+ elements=[
+ {
+ type={
+ type_name=tagged;
+ item=string;
+ tag=foo;
+ };
+ };
+ ];
+} :: Tuple<Tagged<String, 'foo'>> ;;
+
+{
+ type_name=tuple;
+ unknown_column=ha;
+ elements=[
+ {
+ type=int32;
+ unknown_column=ha;
+ };
+ {
+ type={
+ type_name=optional;
+ item=string;
+ };
+ };
+ ];
+} :: Tuple<Int32, Optional<String>> ;;
+
+#
+# Variant
+#
+
+{
+ type_name=variant;
+ elements=[
+ {
+ type=int32;
+ };
+ {
+ type={type_name=string};
+ };
+ ];
+} :: Variant<Int32, String> ;;
+
+{
+ type_name=variant;
+ members=[
+ {
+ name=foo;
+ type=int32;
+ };
+ {
+ name=bar;
+ type={
+ type_name=optional;
+ item=string;
+ };
+ };
+ ];
+} :: Variant<'foo': Int32, 'bar': Optional<String>> ;;
+
+{
+ type_name=variant;
+ elements=[
+ {
+ type={
+ type_name=tagged;
+ item=string;
+ tag=foo;
+ }
+ };
+ ];
+} :: Variant<Tagged<String, 'foo'>> ;;
+
+{
+ type_name=variant;
+ members=[
+ {
+ name=bar;
+ type={
+ type_name=tagged;
+ item=string;
+ tag=foo;
+ };
+ };
+ ];
+} :: Variant<'bar': Tagged<String, 'foo'>> ;;
+
+{
+ type_name=variant;
+ unknown_column=ha;
+ elements=[
+ {
+ type=int32;
+ unknown_column=ha;
+ };
+ {
+ type={
+ type_name=string;
+ unknown_column=ha;
+ };
+ unknown_column=ha;
+ };
+ ];
+} :: Variant<Int32, String> ;;
+
+{
+ type_name=variant;
+ unknown_column=ha;
+ members=[
+ {
+ unknown_column=ha;
+ name=foo;
+ type=int32;
+ };
+ {
+ name=bar;
+ type={
+ type_name=optional;
+ unknown_column=ha;
+ item=string;
+ };
+ };
+ ];
+} :: Variant<'foo': Int32, 'bar': Optional<String>> ;;
+
+#
+# Tagged
+#
+
+{
+ type_name=tagged;
+ item=string;
+ tag="image/png"
+} :: Tagged<String, 'image/png'> ;;
+
+{
+ type_name=tagged;
+ item={
+ type_name=optional;
+ item=string;
+ };
+ tag="image/png"
+} :: Tagged<Optional<String>, 'image/png'> ;;
+
+{
+ type_name=tagged;
+ item={
+ type_name=tagged;
+ item=string;
+ tag=foo;
+ };
+ tag=bar;
+} :: Tagged<Tagged<String, 'foo'>, 'bar'> ;;
+
+{
+ type_name=tagged;
+ item=string;
+ unknown_column=ha;
+ tag="image/png"
+} :: Tagged<String, 'image/png'> ;;
diff --git a/library/cpp/type_info/type.cpp b/library/cpp/type_info/type.cpp
new file mode 100644
index 0000000000..cee58a0a79
--- /dev/null
+++ b/library/cpp/type_info/type.cpp
@@ -0,0 +1,1662 @@
+#include "type.h"
+
+#include "type_factory.h"
+#include "type_equivalence.h"
+
+#include <util/generic/overloaded.h>
+
+#include <util/digest/murmur.h>
+#include <util/generic/hash_set.h>
+#include <util/string/escape.h>
+
+namespace {
+ inline ui64 Hash(NTi::ETypeName type) {
+ return IntHash(static_cast<ui64>(type));
+ }
+
+ inline ui64 Hash(ui64 value, ui64 seed) {
+ return MurmurHash(&value, sizeof(value), seed);
+ }
+
+ inline ui64 Hash(TStringBuf string, ui64 seed) {
+ seed = ::Hash(string.size(), seed);
+ return MurmurHash(string.data(), string.size(), seed);
+ }
+
+ inline ui64 Hash(TMaybe<TStringBuf> string, ui64 seed) {
+ if (string.Defined()) {
+ return MurmurHash(string->data(), string->size(), seed);
+ } else {
+ return seed;
+ }
+ }
+
+ TString Quote(TStringBuf s) {
+ TString result;
+ result.push_back('\'');
+ result += EscapeC(s);
+ result.push_back('\'');
+ return result;
+ }
+}
+
+namespace NTi {
+ TType::TType(TMaybe<ui64> hash, ETypeName typeName) noexcept
+ : TypeName_(typeName)
+ , HasHash_(hash.Defined())
+ , Hash_(hash.GetOrElse(0))
+ {
+ }
+
+ ui64 TType::CalculateHash() const noexcept {
+ return ::Hash(TypeName_);
+ }
+
+ TMaybe<ui64> TType::GetHashRaw() const noexcept {
+ if (HasHash_.load(std::memory_order_seq_cst)) {
+ return Hash_.load(std::memory_order_seq_cst);
+ } else {
+ return Nothing();
+ }
+ }
+
+ ui64 TType::GetHash() const {
+ if (HasHash_.load(std::memory_order_seq_cst)) {
+ return Hash_.load(std::memory_order_seq_cst);
+ } else {
+ ui64 hash = VisitRaw([](const auto* type) {
+ return type->CalculateHash();
+ });
+ Hash_.store(hash, std::memory_order_seq_cst);
+ HasHash_.store(true, std::memory_order_seq_cst);
+ return hash;
+ }
+ }
+
+ TTypePtr TType::StripTags() const noexcept {
+ return StripTagsRaw()->AsPtr();
+ }
+
+ const TType* TType::StripTagsRaw() const noexcept {
+ auto type = this;
+ while (type->IsTagged()) {
+ type = type->AsTaggedRaw()->GetItemTypeRaw();
+ }
+ return type;
+ }
+
+ TTypePtr TType::StripOptionals() const noexcept {
+ return StripOptionalsRaw()->AsPtr();
+ }
+
+ const TType* TType::StripOptionalsRaw() const noexcept {
+ auto type = this;
+ while (type->IsOptional()) {
+ type = type->AsOptionalRaw()->GetItemTypeRaw();
+ }
+ return type;
+ }
+
+ TTypePtr TType::StripTagsAndOptionals() const noexcept {
+ return StripTagsAndOptionalsRaw()->AsPtr();
+ }
+
+ const TType* TType::StripTagsAndOptionalsRaw() const noexcept {
+ auto type = this;
+ while (type->IsTagged() || type->IsOptional()) {
+ if (type->IsTagged()) {
+ type = type->AsTaggedRaw()->GetItemTypeRaw();
+ } else {
+ type = type->AsOptionalRaw()->GetItemTypeRaw();
+ }
+ }
+ return type;
+ }
+
+ const TType* TType::Clone(ITypeFactoryInternal& factory) const noexcept {
+ return VisitRaw([&factory](const auto* type) -> const NTi::TType* {
+ return type->Clone(factory);
+ });
+ }
+
+ void TType::Drop(ITypeFactoryInternal& factory) noexcept {
+ VisitRaw([&factory](const auto* type) {
+ using T = std::remove_const_t<std::remove_pointer_t<decltype(type)>>;
+ return const_cast<T*>(type)->Drop(factory);
+ });
+ }
+
+ ITypeFactoryInternal* TType::GetFactory() const noexcept {
+ size_t manager_or_rc = FactoryOrRc_.load(std::memory_order_relaxed);
+ if (IsRc(manager_or_rc)) {
+ return NPrivate::GetDefaultHeapFactory();
+ } else if (IsFactory(manager_or_rc)) {
+ return CastToFactory(manager_or_rc);
+ } else {
+ return nullptr;
+ }
+ }
+
+ void TType::SetFactory(ITypeFactoryInternal* factory) noexcept {
+ if (factory == NPrivate::GetDefaultHeapFactory()) {
+ FactoryOrRc_.store(0b1u, std::memory_order_release);
+ } else {
+ FactoryOrRc_.store(CastFromFactory(factory), std::memory_order_release);
+ }
+ }
+
+ ITypeFactoryInternal& TType::FactoryInternal(ITypeFactory& factory) noexcept {
+ return static_cast<ITypeFactoryInternal&>(factory);
+ }
+
+ template <bool RefFactory>
+ void TType::RefImpl() noexcept {
+ size_t factoryOrRc = FactoryOrRc_.load(std::memory_order_relaxed);
+ if (Y_LIKELY(IsRc(factoryOrRc))) {
+ FactoryOrRc_.fetch_add(0b10u, std::memory_order_acq_rel);
+ } else if (Y_LIKELY(IsFactory(factoryOrRc))) {
+ auto factory = CastToFactory(factoryOrRc);
+ if (RefFactory) {
+ factory->Ref();
+ }
+ factory->RefType(this);
+ }
+ }
+
+ template void TType::RefImpl<true>() noexcept;
+ template void TType::RefImpl<false>() noexcept;
+
+ template <bool UnRefFactory>
+ void TType::UnRefImpl() noexcept {
+ size_t factoryOrRc = FactoryOrRc_.load(std::memory_order_relaxed);
+ if (Y_LIKELY(IsRc(factoryOrRc))) {
+ size_t rc = FactoryOrRc_.fetch_sub(0b10u, std::memory_order_acq_rel);
+ if (rc == 0b11u) {
+ auto factory = NPrivate::GetDefaultHeapFactory();
+ Drop(*factory);
+ factory->Delete(this);
+ }
+ } else if (Y_LIKELY(IsFactory(factoryOrRc))) {
+ auto factory = CastToFactory(factoryOrRc);
+ factory->UnRefType(this);
+ if (UnRefFactory) {
+ factory->UnRef();
+ }
+ }
+ }
+
+ template void TType::UnRefImpl<true>() noexcept;
+ template void TType::UnRefImpl<false>() noexcept;
+
+ template <bool DecRefFactory>
+ void TType::DecRefImpl() noexcept {
+ size_t factoryOrRc = FactoryOrRc_.load(std::memory_order_relaxed);
+ if (Y_LIKELY(IsRc(factoryOrRc))) {
+ size_t rc = FactoryOrRc_.fetch_sub(2, std::memory_order_acq_rel);
+ if (rc == 2) {
+ Y_FAIL("DecRef isn't supposed to drop");
+ }
+ } else if (Y_LIKELY(IsFactory(factoryOrRc))) {
+ auto factory = CastToFactory(factoryOrRc);
+ factory->DecRefType(this);
+ if (DecRefFactory) {
+ factory->DecRef();
+ }
+ }
+ }
+
+ template void TType::DecRefImpl<true>() noexcept;
+ template void TType::DecRefImpl<false>() noexcept;
+
+ long TType::RefCountImpl() const noexcept {
+ size_t factoryOrRc = FactoryOrRc_.load(std::memory_order_relaxed);
+ if (Y_LIKELY(IsRc(factoryOrRc))) {
+ return factoryOrRc >> 1u;
+ } else if (Y_LIKELY(IsFactory(factoryOrRc))) {
+ return CastToFactory(factoryOrRc)->RefCountType(this);
+ } else {
+ return 0;
+ }
+ }
+
+ template <typename T, typename TCtor>
+ const T* TType::Cached(const T* type, ITypeFactoryInternal& factory, TCtor&& ctor) {
+ const TType* result = factory.LookupCache(type);
+
+ if (result == nullptr) {
+ result = std::forward<TCtor>(ctor)();
+ factory.SaveCache(result);
+ }
+
+ Y_VERIFY(result->GetTypeName() == type->GetTypeName());
+ Y_VERIFY_DEBUG(result->GetHash() == type->GetHash());
+ return static_cast<const T*>(result);
+ }
+
+ bool operator==(const TType& lhs, const TType& rhs) {
+ Y_VERIFY(&lhs);
+ Y_VERIFY(&rhs);
+ return NEq::TStrictlyEqual().IgnoreHash(&lhs, &rhs);
+ }
+
+ bool operator!=(const TType& lhs, const TType& rhs)
+ {
+ return !(lhs == rhs);
+ }
+
+ TVoidType::TVoidType()
+ : TType({}, ETypeName::Void)
+ {
+ }
+
+ TVoidTypePtr TVoidType::Instance() {
+ return InstanceRaw()->AsPtr();
+ }
+
+ const TVoidType* TVoidType::InstanceRaw() {
+ static auto singleton = TVoidType();
+ return &singleton;
+ }
+
+ const TVoidType* TVoidType::Clone(ITypeFactoryInternal& factory) const noexcept {
+ Y_UNUSED(factory);
+ return InstanceRaw();
+ }
+
+ void TVoidType::Drop(ITypeFactoryInternal& factory) noexcept {
+ Y_UNUSED(factory);
+ }
+
+ TNullType::TNullType()
+ : TType({}, ETypeName::Null)
+ {
+ }
+
+ TNullTypePtr TNullType::Instance() {
+ return InstanceRaw()->AsPtr();
+ }
+
+ const TNullType* TNullType::InstanceRaw() {
+ static auto singleton = TNullType();
+ return &singleton;
+ }
+
+ const TNullType* TNullType::Clone(ITypeFactoryInternal& factory) const noexcept {
+ Y_UNUSED(factory);
+ return InstanceRaw();
+ }
+
+ void TNullType::Drop(ITypeFactoryInternal& factory) noexcept {
+ Y_UNUSED(factory);
+ }
+
+ TPrimitiveType::TPrimitiveType(TMaybe<ui64> hash, EPrimitiveTypeName primitiveTypeName) noexcept
+ : TType(hash, ToTypeName(primitiveTypeName))
+ {
+ }
+
+ TBoolType::TBoolType()
+ : TPrimitiveType({}, EPrimitiveTypeName::Bool)
+ {
+ }
+
+ TBoolTypePtr TBoolType::Instance() {
+ return InstanceRaw()->AsPtr();
+ }
+
+ const TBoolType* TBoolType::InstanceRaw() {
+ static auto singleton = TBoolType();
+ return &singleton;
+ }
+
+ const TBoolType* TBoolType::Clone(ITypeFactoryInternal& factory) const noexcept {
+ Y_UNUSED(factory);
+ return InstanceRaw();
+ }
+
+ void TBoolType::Drop(ITypeFactoryInternal& factory) noexcept {
+ Y_UNUSED(factory);
+ }
+
+ TInt8Type::TInt8Type()
+ : TPrimitiveType({}, EPrimitiveTypeName::Int8)
+ {
+ }
+
+ TInt8TypePtr TInt8Type::Instance() {
+ return InstanceRaw()->AsPtr();
+ }
+
+ const TInt8Type* TInt8Type::InstanceRaw() {
+ static auto singleton = TInt8Type();
+ return &singleton;
+ }
+
+ const TInt8Type* TInt8Type::Clone(ITypeFactoryInternal& factory) const noexcept {
+ Y_UNUSED(factory);
+ return InstanceRaw();
+ }
+
+ void TInt8Type::Drop(ITypeFactoryInternal& factory) noexcept {
+ Y_UNUSED(factory);
+ }
+
+ TInt16Type::TInt16Type()
+ : TPrimitiveType({}, EPrimitiveTypeName::Int16)
+ {
+ }
+
+ TInt16TypePtr TInt16Type::Instance() {
+ return InstanceRaw()->AsPtr();
+ }
+
+ const TInt16Type* TInt16Type::InstanceRaw() {
+ static auto singleton = TInt16Type();
+ return &singleton;
+ }
+
+ const TInt16Type* TInt16Type::Clone(ITypeFactoryInternal& factory) const noexcept {
+ Y_UNUSED(factory);
+ return InstanceRaw();
+ }
+
+ void TInt16Type::Drop(ITypeFactoryInternal& factory) noexcept {
+ Y_UNUSED(factory);
+ }
+
+ TInt32Type::TInt32Type()
+ : TPrimitiveType({}, EPrimitiveTypeName::Int32)
+ {
+ }
+
+ TInt32TypePtr TInt32Type::Instance() {
+ return InstanceRaw()->AsPtr();
+ }
+
+ const TInt32Type* TInt32Type::InstanceRaw() {
+ static auto singleton = TInt32Type();
+ return &singleton;
+ }
+
+ const TInt32Type* TInt32Type::Clone(ITypeFactoryInternal& factory) const noexcept {
+ Y_UNUSED(factory);
+ return InstanceRaw();
+ }
+
+ void TInt32Type::Drop(ITypeFactoryInternal& factory) noexcept {
+ Y_UNUSED(factory);
+ }
+
+ TInt64Type::TInt64Type()
+ : TPrimitiveType({}, EPrimitiveTypeName::Int64)
+ {
+ }
+
+ TInt64TypePtr TInt64Type::Instance() {
+ return InstanceRaw()->AsPtr();
+ }
+
+ const TInt64Type* TInt64Type::InstanceRaw() {
+ static auto singleton = TInt64Type();
+ return &singleton;
+ }
+
+ const TInt64Type* TInt64Type::Clone(ITypeFactoryInternal& factory) const noexcept {
+ Y_UNUSED(factory);
+ return InstanceRaw();
+ }
+
+ void TInt64Type::Drop(ITypeFactoryInternal& factory) noexcept {
+ Y_UNUSED(factory);
+ }
+
+ TUint8Type::TUint8Type()
+ : TPrimitiveType({}, EPrimitiveTypeName::Uint8)
+ {
+ }
+
+ TUint8TypePtr TUint8Type::Instance() {
+ return InstanceRaw()->AsPtr();
+ }
+
+ const TUint8Type* TUint8Type::InstanceRaw() {
+ static auto singleton = TUint8Type();
+ return &singleton;
+ }
+
+ const TUint8Type* TUint8Type::Clone(ITypeFactoryInternal& factory) const noexcept {
+ Y_UNUSED(factory);
+ return InstanceRaw();
+ }
+
+ void TUint8Type::Drop(ITypeFactoryInternal& factory) noexcept {
+ Y_UNUSED(factory);
+ }
+
+ TUint16Type::TUint16Type()
+ : TPrimitiveType({}, EPrimitiveTypeName::Uint16)
+ {
+ }
+
+ TUint16TypePtr TUint16Type::Instance() {
+ return InstanceRaw()->AsPtr();
+ }
+
+ const TUint16Type* TUint16Type::InstanceRaw() {
+ static auto singleton = TUint16Type();
+ return &singleton;
+ }
+
+ const TUint16Type* TUint16Type::Clone(ITypeFactoryInternal& factory) const noexcept {
+ Y_UNUSED(factory);
+ return InstanceRaw();
+ }
+
+ void TUint16Type::Drop(ITypeFactoryInternal& factory) noexcept {
+ Y_UNUSED(factory);
+ }
+
+ TUint32Type::TUint32Type()
+ : TPrimitiveType({}, EPrimitiveTypeName::Uint32)
+ {
+ }
+
+ TUint32TypePtr TUint32Type::Instance() {
+ return InstanceRaw()->AsPtr();
+ }
+
+ const TUint32Type* TUint32Type::InstanceRaw() {
+ static auto singleton = TUint32Type();
+ return &singleton;
+ }
+
+ const TUint32Type* TUint32Type::Clone(ITypeFactoryInternal& factory) const noexcept {
+ Y_UNUSED(factory);
+ return InstanceRaw();
+ }
+
+ void TUint32Type::Drop(ITypeFactoryInternal& factory) noexcept {
+ Y_UNUSED(factory);
+ }
+
+ TUint64Type::TUint64Type()
+ : TPrimitiveType({}, EPrimitiveTypeName::Uint64)
+ {
+ }
+
+ TUint64TypePtr TUint64Type::Instance() {
+ return InstanceRaw()->AsPtr();
+ }
+
+ const TUint64Type* TUint64Type::InstanceRaw() {
+ static auto singleton = TUint64Type();
+ return &singleton;
+ }
+
+ const TUint64Type* TUint64Type::Clone(ITypeFactoryInternal& factory) const noexcept {
+ Y_UNUSED(factory);
+ return InstanceRaw();
+ }
+
+ void TUint64Type::Drop(ITypeFactoryInternal& factory) noexcept {
+ Y_UNUSED(factory);
+ }
+
+ TFloatType::TFloatType()
+ : TPrimitiveType({}, EPrimitiveTypeName::Float)
+ {
+ }
+
+ TFloatTypePtr TFloatType::Instance() {
+ return InstanceRaw()->AsPtr();
+ }
+
+ const TFloatType* TFloatType::InstanceRaw() {
+ static auto singleton = TFloatType();
+ return &singleton;
+ }
+
+ const TFloatType* TFloatType::Clone(ITypeFactoryInternal& factory) const noexcept {
+ Y_UNUSED(factory);
+ return InstanceRaw();
+ }
+
+ void TFloatType::Drop(ITypeFactoryInternal& factory) noexcept {
+ Y_UNUSED(factory);
+ }
+
+ TDoubleType::TDoubleType()
+ : TPrimitiveType({}, EPrimitiveTypeName::Double)
+ {
+ }
+
+ TDoubleTypePtr TDoubleType::Instance() {
+ return InstanceRaw()->AsPtr();
+ }
+
+ const TDoubleType* TDoubleType::InstanceRaw() {
+ static auto singleton = TDoubleType();
+ return &singleton;
+ }
+
+ const TDoubleType* TDoubleType::Clone(ITypeFactoryInternal& factory) const noexcept {
+ Y_UNUSED(factory);
+ return InstanceRaw();
+ }
+
+ void TDoubleType::Drop(ITypeFactoryInternal& factory) noexcept {
+ Y_UNUSED(factory);
+ }
+
+ TStringType::TStringType()
+ : TPrimitiveType({}, EPrimitiveTypeName::String)
+ {
+ }
+
+ TStringTypePtr TStringType::Instance() {
+ return InstanceRaw()->AsPtr();
+ }
+
+ const TStringType* TStringType::InstanceRaw() {
+ static auto singleton = TStringType();
+ return &singleton;
+ }
+
+ const TStringType* TStringType::Clone(ITypeFactoryInternal& factory) const noexcept {
+ Y_UNUSED(factory);
+ return InstanceRaw();
+ }
+
+ void TStringType::Drop(ITypeFactoryInternal& factory) noexcept {
+ Y_UNUSED(factory);
+ }
+
+ TUtf8Type::TUtf8Type()
+ : TPrimitiveType({}, EPrimitiveTypeName::Utf8)
+ {
+ }
+
+ TUtf8TypePtr TUtf8Type::Instance() {
+ return InstanceRaw()->AsPtr();
+ }
+
+ const TUtf8Type* TUtf8Type::InstanceRaw() {
+ static auto singleton = TUtf8Type();
+ return &singleton;
+ }
+
+ const TUtf8Type* TUtf8Type::Clone(ITypeFactoryInternal& factory) const noexcept {
+ Y_UNUSED(factory);
+ return InstanceRaw();
+ }
+
+ void TUtf8Type::Drop(ITypeFactoryInternal& factory) noexcept {
+ Y_UNUSED(factory);
+ }
+
+ TDateType::TDateType()
+ : TPrimitiveType({}, EPrimitiveTypeName::Date)
+ {
+ }
+
+ TDateTypePtr TDateType::Instance() {
+ return InstanceRaw()->AsPtr();
+ }
+
+ const TDateType* TDateType::InstanceRaw() {
+ static auto singleton = TDateType();
+ return &singleton;
+ }
+
+ const TDateType* TDateType::Clone(ITypeFactoryInternal& factory) const noexcept {
+ Y_UNUSED(factory);
+ return InstanceRaw();
+ }
+
+ void TDateType::Drop(ITypeFactoryInternal& factory) noexcept {
+ Y_UNUSED(factory);
+ }
+
+ TDatetimeType::TDatetimeType()
+ : TPrimitiveType({}, EPrimitiveTypeName::Datetime)
+ {
+ }
+
+ TDatetimeTypePtr TDatetimeType::Instance() {
+ return InstanceRaw()->AsPtr();
+ }
+
+ const TDatetimeType* TDatetimeType::InstanceRaw() {
+ static auto singleton = TDatetimeType();
+ return &singleton;
+ }
+
+ const TDatetimeType* TDatetimeType::Clone(ITypeFactoryInternal& factory) const noexcept {
+ Y_UNUSED(factory);
+ return InstanceRaw();
+ }
+
+ void TDatetimeType::Drop(ITypeFactoryInternal& factory) noexcept {
+ Y_UNUSED(factory);
+ }
+
+ TTimestampType::TTimestampType()
+ : TPrimitiveType({}, EPrimitiveTypeName::Timestamp)
+ {
+ }
+
+ TTimestampTypePtr TTimestampType::Instance() {
+ return InstanceRaw()->AsPtr();
+ }
+
+ const TTimestampType* TTimestampType::InstanceRaw() {
+ static auto singleton = TTimestampType();
+ return &singleton;
+ }
+
+ const TTimestampType* TTimestampType::Clone(ITypeFactoryInternal& factory) const noexcept {
+ Y_UNUSED(factory);
+ return InstanceRaw();
+ }
+
+ void TTimestampType::Drop(ITypeFactoryInternal& factory) noexcept {
+ Y_UNUSED(factory);
+ }
+
+ TTzDateType::TTzDateType()
+ : TPrimitiveType({}, EPrimitiveTypeName::TzDate)
+ {
+ }
+
+ TTzDateTypePtr TTzDateType::Instance() {
+ return InstanceRaw()->AsPtr();
+ }
+
+ const TTzDateType* TTzDateType::InstanceRaw() {
+ static auto singleton = TTzDateType();
+ return &singleton;
+ }
+
+ const TTzDateType* TTzDateType::Clone(ITypeFactoryInternal& factory) const noexcept {
+ Y_UNUSED(factory);
+ return InstanceRaw();
+ }
+
+ void TTzDateType::Drop(ITypeFactoryInternal& factory) noexcept {
+ Y_UNUSED(factory);
+ }
+
+ TTzDatetimeType::TTzDatetimeType()
+ : TPrimitiveType({}, EPrimitiveTypeName::TzDatetime)
+ {
+ }
+
+ TTzDatetimeTypePtr TTzDatetimeType::Instance() {
+ return InstanceRaw()->AsPtr();
+ }
+
+ const TTzDatetimeType* TTzDatetimeType::InstanceRaw() {
+ static auto singleton = TTzDatetimeType();
+ return &singleton;
+ }
+
+ const TTzDatetimeType* TTzDatetimeType::Clone(ITypeFactoryInternal& factory) const noexcept {
+ Y_UNUSED(factory);
+ return InstanceRaw();
+ }
+
+ void TTzDatetimeType::Drop(ITypeFactoryInternal& factory) noexcept {
+ Y_UNUSED(factory);
+ }
+
+ TTzTimestampType::TTzTimestampType()
+ : TPrimitiveType({}, EPrimitiveTypeName::TzTimestamp)
+ {
+ }
+
+ TTzTimestampTypePtr TTzTimestampType::Instance() {
+ return InstanceRaw()->AsPtr();
+ }
+
+ const TTzTimestampType* TTzTimestampType::InstanceRaw() {
+ static auto singleton = TTzTimestampType();
+ return &singleton;
+ }
+
+ const TTzTimestampType* TTzTimestampType::Clone(ITypeFactoryInternal& factory) const noexcept {
+ Y_UNUSED(factory);
+ return InstanceRaw();
+ }
+
+ void TTzTimestampType::Drop(ITypeFactoryInternal& factory) noexcept {
+ Y_UNUSED(factory);
+ }
+
+ TIntervalType::TIntervalType()
+ : TPrimitiveType({}, EPrimitiveTypeName::Interval)
+ {
+ }
+
+ TIntervalTypePtr TIntervalType::Instance() {
+ return InstanceRaw()->AsPtr();
+ }
+
+ const TIntervalType* TIntervalType::InstanceRaw() {
+ static auto singleton = TIntervalType();
+ return &singleton;
+ }
+
+ const TIntervalType* TIntervalType::Clone(ITypeFactoryInternal& factory) const noexcept {
+ Y_UNUSED(factory);
+ return InstanceRaw();
+ }
+
+ void TIntervalType::Drop(ITypeFactoryInternal& factory) noexcept {
+ Y_UNUSED(factory);
+ }
+
+ TDecimalType::TDecimalType(TMaybe<ui64> hash, ui8 precision, ui8 scale) noexcept
+ : TPrimitiveType(hash, EPrimitiveTypeName::Decimal)
+ , Precision_(precision)
+ , Scale_(scale)
+ {
+ }
+
+ TDecimalTypePtr TDecimalType::Create(ITypeFactory& factory, ui8 precision, ui8 scale) {
+ return CreateRaw(factory, precision, scale)->AsPtr();
+ }
+
+ const TDecimalType* TDecimalType::CreateRaw(ITypeFactory& factory, ui8 precision, ui8 scale) {
+ Y_ENSURE_EX(
+ scale <= precision,
+ TIllegalTypeException() << "decimal scale " << (ui32)scale
+ << " should be no greater than decimal precision " << (ui32)precision);
+
+ return TDecimalType({}, precision, scale).Clone(FactoryInternal(factory));
+ }
+
+ ui64 TDecimalType::CalculateHash() const noexcept {
+ auto hash = TType::CalculateHash();
+ hash = ::Hash(Precision_, hash);
+ hash = ::Hash(Scale_, hash);
+ return hash;
+ }
+
+ const TDecimalType* TDecimalType::Clone(ITypeFactoryInternal& factory) const noexcept {
+ return Cached(this, factory, [this, &factory]() -> const TDecimalType* {
+ auto hash = GetHashRaw();
+ auto precision = Precision_;
+ auto scale = Scale_;
+ return factory.New<TDecimalType>(hash, precision, scale);
+ });
+ }
+
+ void TDecimalType::Drop(ITypeFactoryInternal& factory) noexcept {
+ Y_UNUSED(factory);
+ }
+
+ TJsonType::TJsonType()
+ : TPrimitiveType({}, EPrimitiveTypeName::Json)
+ {
+ }
+
+ TJsonTypePtr TJsonType::Instance() {
+ return InstanceRaw()->AsPtr();
+ }
+
+ const TJsonType* TJsonType::InstanceRaw() {
+ static auto singleton = TJsonType();
+ return &singleton;
+ }
+
+ const TJsonType* TJsonType::Clone(ITypeFactoryInternal& factory) const noexcept {
+ Y_UNUSED(factory);
+ return InstanceRaw();
+ }
+
+ void TJsonType::Drop(ITypeFactoryInternal& factory) noexcept {
+ Y_UNUSED(factory);
+ }
+
+ TYsonType::TYsonType()
+ : TPrimitiveType({}, EPrimitiveTypeName::Yson)
+ {
+ }
+
+ TYsonTypePtr TYsonType::Instance() {
+ return InstanceRaw()->AsPtr();
+ }
+
+ const TYsonType* TYsonType::InstanceRaw() {
+ static auto singleton = TYsonType();
+ return &singleton;
+ }
+
+ const TYsonType* TYsonType::Clone(ITypeFactoryInternal& factory) const noexcept {
+ Y_UNUSED(factory);
+ return InstanceRaw();
+ }
+
+ void TYsonType::Drop(ITypeFactoryInternal& factory) noexcept {
+ Y_UNUSED(factory);
+ }
+
+ TUuidType::TUuidType()
+ : TPrimitiveType({}, EPrimitiveTypeName::Uuid)
+ {
+ }
+
+ TUuidTypePtr TUuidType::Instance() {
+ return InstanceRaw()->AsPtr();
+ }
+
+ const TUuidType* TUuidType::InstanceRaw() {
+ static auto singleton = TUuidType();
+ return &singleton;
+ }
+
+ const TUuidType* TUuidType::Clone(ITypeFactoryInternal& factory) const noexcept {
+ Y_UNUSED(factory);
+ return InstanceRaw();
+ }
+
+ void TUuidType::Drop(ITypeFactoryInternal& factory) noexcept {
+ Y_UNUSED(factory);
+ }
+
+ TOptionalType::TOptionalType(TMaybe<ui64> hash, const TType* item) noexcept
+ : TType(hash, ETypeName::Optional)
+ , Item_(item)
+ {
+ }
+
+ TOptionalTypePtr TOptionalType::Create(ITypeFactory& factory, TTypePtr item) {
+ return CreateRaw(factory, item.Get())->AsPtr();
+ }
+
+ const TOptionalType* TOptionalType::CreateRaw(ITypeFactory& factory, const TType* item) {
+ return TOptionalType({}, item).Clone(FactoryInternal(factory));
+ }
+
+ const TOptionalType* TOptionalType::Clone(ITypeFactoryInternal& factory) const noexcept {
+ return Cached(this, factory, [this, &factory]() -> const TOptionalType* {
+ auto hash = GetHashRaw();
+ auto item = factory.Own(Item_);
+ return factory.New<TOptionalType>(hash, item);
+ });
+ }
+
+ void TOptionalType::Drop(ITypeFactoryInternal& factory) noexcept {
+ factory.Disown(Item_);
+ }
+
+ ui64 TOptionalType::CalculateHash() const noexcept {
+ auto hash = TType::CalculateHash();
+ hash = ::Hash(Item_->GetHash(), hash);
+ return hash;
+ }
+
+ TListType::TListType(TMaybe<ui64> hash, const TType* item) noexcept
+ : TType(hash, ETypeName::List)
+ , Item_(item)
+ {
+ }
+
+ TListTypePtr TListType::Create(ITypeFactory& factory, TTypePtr item) {
+ return CreateRaw(factory, item.Get())->AsPtr();
+ }
+
+ const TListType* TListType::CreateRaw(ITypeFactory& factory, const TType* item) {
+ return TListType({}, item).Clone(FactoryInternal(factory));
+ }
+
+ const TListType* TListType::Clone(ITypeFactoryInternal& factory) const noexcept {
+ return Cached(this, factory, [this, &factory]() -> const TListType* {
+ auto hash = GetHashRaw();
+ auto item = factory.Own(Item_);
+ return factory.New<TListType>(hash, item);
+ });
+ }
+
+ void TListType::Drop(ITypeFactoryInternal& factory) noexcept {
+ factory.Disown(Item_);
+ }
+
+ ui64 TListType::CalculateHash() const noexcept {
+ auto hash = TType::CalculateHash();
+ hash = ::Hash(Item_->GetHash(), hash);
+ return hash;
+ }
+
+ TDictType::TDictType(TMaybe<ui64> hash, const TType* key, const TType* value) noexcept
+ : TType(hash, ETypeName::Dict)
+ , Key_(key)
+ , Value_(value)
+ {
+ }
+
+ TDictTypePtr TDictType::Create(ITypeFactory& factory, TTypePtr key, TTypePtr value) {
+ return CreateRaw(factory, key.Get(), value.Get())->AsPtr();
+ }
+
+ const TDictType* TDictType::CreateRaw(ITypeFactory& factory, const TType* key, const TType* value) {
+ return TDictType({}, key, value).Clone(FactoryInternal(factory));
+ }
+
+ const TDictType* TDictType::Clone(ITypeFactoryInternal& factory) const noexcept {
+ return Cached(this, factory, [this, &factory]() -> const TDictType* {
+ auto hash = GetHashRaw();
+ auto key = factory.Own(Key_);
+ auto value = factory.Own(Value_);
+ return factory.New<TDictType>(hash, key, value);
+ });
+ }
+
+ void TDictType::Drop(ITypeFactoryInternal& factory) noexcept {
+ factory.Disown(Key_);
+ factory.Disown(Value_);
+ }
+
+ ui64 TDictType::CalculateHash() const noexcept {
+ auto hash = TType::CalculateHash();
+ hash = ::Hash(Key_->GetHash(), hash);
+ hash = ::Hash(Value_->GetHash(), hash);
+ return hash;
+ }
+
+ TStructType::TMember::TMember(TStringBuf name, const TType* type)
+ : Name_(name)
+ , Type_(type)
+ {
+ }
+
+ ui64 TStructType::TMember::Hash() const {
+ auto hash = 0x10000;
+ hash = ::Hash(Name_, hash);
+ hash = ::Hash(Type_->GetHash(), hash);
+ return hash;
+ }
+
+ TStructType::TOwnedMember::TOwnedMember(TString name, TTypePtr type)
+ : Name_(std::move(name))
+ , Type_(std::move(type))
+ {
+ }
+
+ TStructType::TOwnedMember::operator TStructType::TMember() const& {
+ return TStructType::TMember(Name_, Type_.Get());
+ }
+
+ TStructType::TStructType(TMaybe<ui64> hash, TMaybe<TStringBuf> name, TMembers members, TConstArrayRef<size_t> sortedItems) noexcept
+ : TType(hash, ETypeName::Struct)
+ , Name_(name)
+ , Members_(members)
+ , SortedMembers_(sortedItems)
+ {
+ }
+
+ TStructTypePtr TStructType::Create(ITypeFactory& factory, TStructType::TOwnedMembers members) {
+ return Create(factory, Nothing(), members);
+ }
+
+ TStructTypePtr TStructType::Create(ITypeFactory& factory, TMaybe<TStringBuf> name, TStructType::TOwnedMembers members) {
+ auto rawItems = TTempArray<TMember>(members.size());
+ for (size_t i = 0; i < members.size(); ++i) {
+ new (rawItems.Data() + i) TMember(members[i]);
+ }
+ return CreateRaw(factory, name, TArrayRef(rawItems.Data(), members.size()))->AsPtr();
+ }
+
+ const TStructType* TStructType::CreateRaw(ITypeFactory& factory, TStructType::TMembers members) {
+ return CreateRaw(factory, Nothing(), members);
+ }
+
+ const TStructType* TStructType::CreateRaw(ITypeFactory& factory, TMaybe<TStringBuf> name, TStructType::TMembers members) {
+ auto sortedMembersArray = TTempArray<size_t>(members.size());
+ auto sortedMembers = TArrayRef(sortedMembersArray.Data(), members.size());
+ MakeSortedMembers(members, sortedMembers);
+ return TStructType({}, name, members, sortedMembers).Clone(FactoryInternal(factory));
+ }
+
+ void TStructType::MakeSortedMembers(TStructType::TMembers members, TArrayRef<size_t> sortedItems) {
+ Y_VERIFY(members.size() == sortedItems.size());
+
+ for (size_t i = 0; i < members.size(); ++i) {
+ sortedItems[i] = i;
+ }
+
+ Sort(sortedItems.begin(), sortedItems.end(), [members](size_t lhs, size_t rhs) {
+ return members[lhs].GetName() < members[rhs].GetName();
+ });
+
+ for (size_t i = 1; i < members.size(); ++i) {
+ if (members[sortedItems[i - 1]].GetName() == members[sortedItems[i]].GetName()) {
+ ythrow TIllegalTypeException() << "duplicate struct item " << Quote(members[sortedItems[i]].GetName());
+ }
+ }
+ }
+
+ const TStructType* TStructType::Clone(ITypeFactoryInternal& factory) const noexcept {
+ return Cached(this, factory, [this, &factory]() -> const TStructType* {
+ auto hash = GetHashRaw();
+ auto name = factory.AllocateStringMaybe(Name_);
+ auto members = factory.NewArray<TMember>(Members_.size(), [this, &factory](TMember* item, size_t i) {
+ auto name = factory.AllocateString(Members_[i].GetName());
+ auto type = factory.Own(Members_[i].GetTypeRaw());
+ new (item) TMember(name, type);
+ });
+ auto sortedItems = factory.AllocateArrayFor<size_t>(SortedMembers_.size());
+ Copy(SortedMembers_.begin(), SortedMembers_.end(), sortedItems);
+ return factory.New<TStructType>(hash, name, members, TArrayRef{sortedItems, SortedMembers_.size()});
+ });
+ }
+
+ void TStructType::Drop(ITypeFactoryInternal& factory) noexcept {
+ factory.FreeStringMaybe(Name_);
+ factory.DeleteArray(Members_, [&factory](const TMember* item, size_t) {
+ factory.FreeString(item->GetName());
+ factory.Disown(item->GetTypeRaw());
+ });
+ factory.Free(const_cast<void*>(static_cast<const void*>(SortedMembers_.data())));
+ }
+
+ ui64 TStructType::CalculateHash() const noexcept {
+ auto hash = TType::CalculateHash();
+ hash = ::Hash(Name_, hash);
+ hash = ::Hash(Members_.size(), hash);
+ for (auto& item : Members_) {
+ hash = ::Hash(item.Hash(), hash);
+ }
+ return hash;
+ }
+
+ bool TStructType::HasMember(TStringBuf name) const noexcept {
+ return GetMemberIndex(name) != -1;
+ }
+
+ const TStructType::TMember& TStructType::GetMember(TStringBuf name) const {
+ auto idx = GetMemberIndex(name);
+ if (idx == -1) {
+ ythrow TItemNotFound() << "no item named " << Quote(name);
+ } else {
+ return Members_[idx];
+ }
+ }
+
+ ssize_t TStructType::GetMemberIndex(TStringBuf name) const noexcept {
+ auto it = LowerBound(SortedMembers_.begin(), SortedMembers_.end(), name, [this](size_t i, TStringBuf name) {
+ return Members_[i].GetName() < name;
+ });
+
+ if (it == SortedMembers_.end() || Members_[*it].GetName() != name) {
+ return -1;
+ } else {
+ return *it;
+ }
+ }
+
+ TTupleType::TElement::TElement(const TType* type)
+ : Type_(type)
+ {
+ }
+
+ ui64 TTupleType::TElement::Hash() const {
+ auto hash = 0x10001;
+ hash = ::Hash(Type_->GetHash(), hash);
+ return hash;
+ }
+
+ TTupleType::TOwnedElement::TOwnedElement(TTypePtr type)
+ : Type_(std::move(type))
+ {
+ }
+
+ TTupleType::TOwnedElement::operator TTupleType::TElement() const& {
+ return TTupleType::TElement(Type_.Get());
+ }
+
+ TTupleType::TTupleType(TMaybe<ui64> hash, TMaybe<TStringBuf> name, TElements elements) noexcept
+ : TType(hash, ETypeName::Tuple)
+ , Name_(name)
+ , Elements_(elements)
+ {
+ }
+
+ TTupleTypePtr TTupleType::Create(ITypeFactory& factory, TTupleType::TOwnedElements elements) {
+ return Create(factory, Nothing(), elements);
+ }
+
+ TTupleTypePtr TTupleType::Create(ITypeFactory& factory, TMaybe<TStringBuf> name, TTupleType::TOwnedElements elements) {
+ auto rawItems = TTempArray<TElement>(elements.size());
+ for (size_t i = 0; i < elements.size(); ++i) {
+ new (rawItems.Data() + i) TElement(elements[i]);
+ }
+ return CreateRaw(factory, name, TArrayRef(rawItems.Data(), elements.size()))->AsPtr();
+ }
+
+ const TTupleType* TTupleType::CreateRaw(ITypeFactory& factory, TTupleType::TElements elements) {
+ return CreateRaw(factory, Nothing(), elements);
+ }
+
+ const TTupleType* TTupleType::CreateRaw(ITypeFactory& factory, TMaybe<TStringBuf> name, TTupleType::TElements elements) {
+ return TTupleType({}, name, elements).Clone(FactoryInternal(factory));
+ }
+
+ const TTupleType* TTupleType::Clone(ITypeFactoryInternal& factory) const noexcept {
+ return Cached(this, factory, [this, &factory]() -> const TTupleType* {
+ auto hash = GetHashRaw();
+ auto name = factory.AllocateStringMaybe(Name_);
+ auto elements = factory.NewArray<TElement>(Elements_.size(), [this, &factory](TElement* item, size_t i) {
+ auto type = factory.Own(Elements_[i].GetTypeRaw());
+ new (item) TElement(type);
+ });
+ return factory.New<TTupleType>(hash, name, elements);
+ });
+ }
+
+ void TTupleType::Drop(ITypeFactoryInternal& factory) noexcept {
+ factory.FreeStringMaybe(Name_);
+ factory.DeleteArray(Elements_, [&factory](const TElement* item, size_t) {
+ factory.Disown(item->GetTypeRaw());
+ });
+ }
+
+ ui64 TTupleType::CalculateHash() const noexcept {
+ auto hash = TType::CalculateHash();
+ hash = ::Hash(Name_, hash);
+ hash = ::Hash(Elements_.size(), hash);
+ for (auto& item : Elements_) {
+ hash = ::Hash(item.Hash(), hash);
+ }
+ return hash;
+ }
+
+ TVariantType::TVariantType(TMaybe<ui64> hash, TMaybe<TStringBuf> name, const TType* inner) noexcept
+ : TType(hash, ETypeName::Variant)
+ , Name_(name)
+ , Underlying_(inner)
+ {
+ }
+
+ TVariantTypePtr TVariantType::Create(ITypeFactory& factory, TTypePtr inner) {
+ return Create(factory, Nothing(), std::move(inner));
+ }
+
+ TVariantTypePtr TVariantType::Create(ITypeFactory& factory, TMaybe<TStringBuf> name, TTypePtr inner) {
+ return CreateRaw(factory, name, inner.Get())->AsPtr();
+ }
+
+ const TVariantType* TVariantType::CreateRaw(ITypeFactory& factory, const TType* inner) {
+ return CreateRaw(factory, Nothing(), inner);
+ }
+
+ const TVariantType* TVariantType::CreateRaw(ITypeFactory& factory, TMaybe<TStringBuf> name, const TType* inner) {
+ inner->VisitRaw(TOverloaded{
+ [&](const TStructType* s) {
+ Y_ENSURE_EX(
+ !s->GetMembers().empty(),
+ TIllegalTypeException() << "variant should contain at least one alternative");
+ },
+ [&](const TTupleType* t) {
+ Y_ENSURE_EX(
+ !t->GetElements().empty(),
+ TIllegalTypeException() << "variant should contain at least one alternative");
+ },
+ [](const TType* t) {
+ ythrow TIllegalTypeException() << "variants can only contain structs and tuples, got "
+ << t->GetTypeName() << " instead";
+ }});
+
+ return TVariantType({}, name, inner).Clone(FactoryInternal(factory));
+ }
+
+ const TVariantType* TVariantType::Clone(ITypeFactoryInternal& factory) const noexcept {
+ return Cached(this, factory, [this, &factory]() -> const TVariantType* {
+ auto hash = GetHashRaw();
+ auto name = factory.AllocateStringMaybe(Name_);
+ auto inner = factory.Own(Underlying_);
+ return factory.New<TVariantType>(hash, name, inner);
+ });
+ }
+
+ void TVariantType::Drop(ITypeFactoryInternal& factory) noexcept {
+ factory.FreeStringMaybe(Name_);
+ factory.Disown(Underlying_);
+ }
+
+ ui64 TVariantType::CalculateHash() const noexcept {
+ auto hash = TType::CalculateHash();
+ hash = ::Hash(Name_, hash);
+ hash = ::Hash(Underlying_->GetHash(), hash);
+ return hash;
+ }
+
+ TTaggedType::TTaggedType(TMaybe<ui64> hash, const TType* item, TStringBuf tag) noexcept
+ : TType(hash, ETypeName::Tagged)
+ , Item_(item)
+ , Tag_(tag)
+ {
+ }
+
+ TTaggedTypePtr TTaggedType::Create(ITypeFactory& factory, TTypePtr type, TStringBuf tag) {
+ return CreateRaw(factory, type.Get(), tag)->AsPtr();
+ }
+
+ const TTaggedType* TTaggedType::CreateRaw(ITypeFactory& factory, const TType* type, TStringBuf tag) {
+ return TTaggedType({}, type, tag).Clone(FactoryInternal(factory));
+ }
+
+ const TTaggedType* TTaggedType::Clone(ITypeFactoryInternal& factory) const noexcept {
+ return Cached(this, factory, [this, &factory]() -> const TTaggedType* {
+ auto hash = GetHashRaw();
+ auto item = factory.Own(Item_);
+ auto tag = factory.AllocateString(Tag_);
+ return factory.New<TTaggedType>(hash, item, tag);
+ });
+ }
+
+ void TTaggedType::Drop(ITypeFactoryInternal& factory) noexcept {
+ factory.FreeString(Tag_);
+ factory.Disown(Item_);
+ }
+
+ ui64 TTaggedType::CalculateHash() const noexcept {
+ auto hash = TType::CalculateHash();
+ hash = ::Hash(Tag_, hash);
+ hash = ::Hash(Item_->GetHash(), hash);
+ return hash;
+ }
+
+ TVoidTypePtr Void() {
+ return NPrivate::GetDefaultHeapFactory()->Void();
+ }
+
+ TNullTypePtr Null() {
+ return NPrivate::GetDefaultHeapFactory()->Null();
+ }
+
+ TBoolTypePtr Bool() {
+ return NPrivate::GetDefaultHeapFactory()->Bool();
+ }
+
+ TInt8TypePtr Int8() {
+ return NPrivate::GetDefaultHeapFactory()->Int8();
+ }
+
+ TInt16TypePtr Int16() {
+ return NPrivate::GetDefaultHeapFactory()->Int16();
+ }
+
+ TInt32TypePtr Int32() {
+ return NPrivate::GetDefaultHeapFactory()->Int32();
+ }
+
+ TInt64TypePtr Int64() {
+ return NPrivate::GetDefaultHeapFactory()->Int64();
+ }
+
+ TUint8TypePtr Uint8() {
+ return NPrivate::GetDefaultHeapFactory()->Uint8();
+ }
+
+ TUint16TypePtr Uint16() {
+ return NPrivate::GetDefaultHeapFactory()->Uint16();
+ }
+
+ TUint32TypePtr Uint32() {
+ return NPrivate::GetDefaultHeapFactory()->Uint32();
+ }
+
+ TUint64TypePtr Uint64() {
+ return NPrivate::GetDefaultHeapFactory()->Uint64();
+ }
+
+ TFloatTypePtr Float() {
+ return NPrivate::GetDefaultHeapFactory()->Float();
+ }
+
+ TDoubleTypePtr Double() {
+ return NPrivate::GetDefaultHeapFactory()->Double();
+ }
+
+ TStringTypePtr String() {
+ return NPrivate::GetDefaultHeapFactory()->String();
+ }
+
+ TUtf8TypePtr Utf8() {
+ return NPrivate::GetDefaultHeapFactory()->Utf8();
+ }
+
+ TDateTypePtr Date() {
+ return NPrivate::GetDefaultHeapFactory()->Date();
+ }
+
+ TDatetimeTypePtr Datetime() {
+ return NPrivate::GetDefaultHeapFactory()->Datetime();
+ }
+
+ TTimestampTypePtr Timestamp() {
+ return NPrivate::GetDefaultHeapFactory()->Timestamp();
+ }
+
+ TTzDateTypePtr TzDate() {
+ return NPrivate::GetDefaultHeapFactory()->TzDate();
+ }
+
+ TTzDatetimeTypePtr TzDatetime() {
+ return NPrivate::GetDefaultHeapFactory()->TzDatetime();
+ }
+
+ TTzTimestampTypePtr TzTimestamp() {
+ return NPrivate::GetDefaultHeapFactory()->TzTimestamp();
+ }
+
+ TIntervalTypePtr Interval() {
+ return NPrivate::GetDefaultHeapFactory()->Interval();
+ }
+
+ TDecimalTypePtr Decimal(ui8 precision, ui8 scale) {
+ return NPrivate::GetDefaultHeapFactory()->Decimal(precision, scale);
+ }
+
+ TJsonTypePtr Json() {
+ return NPrivate::GetDefaultHeapFactory()->Json();
+ }
+
+ TYsonTypePtr Yson() {
+ return NPrivate::GetDefaultHeapFactory()->Yson();
+ }
+
+ TUuidTypePtr Uuid() {
+ return NPrivate::GetDefaultHeapFactory()->Uuid();
+ }
+
+ TOptionalTypePtr Optional(TTypePtr item) {
+ return NPrivate::GetDefaultHeapFactory()->Optional(std::move(item));
+ }
+
+ TListTypePtr List(TTypePtr item) {
+ return NPrivate::GetDefaultHeapFactory()->List(std::move(item));
+ }
+
+ TDictTypePtr Dict(TTypePtr key, TTypePtr value) {
+ return NPrivate::GetDefaultHeapFactory()->Dict(std::move(key), std::move(value));
+ }
+
+ TStructTypePtr Struct(TStructType::TOwnedMembers members) {
+ return NPrivate::GetDefaultHeapFactory()->Struct(members);
+ }
+
+ TStructTypePtr Struct(TMaybe<TStringBuf> name, TStructType::TOwnedMembers members) {
+ return NPrivate::GetDefaultHeapFactory()->Struct(name, members);
+ }
+
+ TTupleTypePtr Tuple(TTupleType::TOwnedElements elements) {
+ return NPrivate::GetDefaultHeapFactory()->Tuple(elements);
+ }
+
+ TTupleTypePtr Tuple(TMaybe<TStringBuf> name, TTupleType::TOwnedElements elements) {
+ return NPrivate::GetDefaultHeapFactory()->Tuple(name, elements);
+ }
+
+ TVariantTypePtr Variant(TTypePtr underlying) {
+ return NPrivate::GetDefaultHeapFactory()->Variant(std::move(underlying));
+ }
+
+ TVariantTypePtr Variant(TMaybe<TStringBuf> name, TTypePtr underlying) {
+ return NPrivate::GetDefaultHeapFactory()->Variant(name, std::move(underlying));
+ }
+
+ TTaggedTypePtr Tagged(TTypePtr type, TStringBuf tag) {
+ return NPrivate::GetDefaultHeapFactory()->Tagged(std::move(type), tag);
+ }
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TType, o, v) {
+ v.VisitRaw([&o](const auto* v) { o << *v; });
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TPrimitiveType, o, v) {
+ v.VisitPrimitiveRaw([&o](const auto* v) { o << *v; });
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TVoidType, o, v) {
+ Y_UNUSED(v);
+ o << "Void";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TNullType, o, v) {
+ Y_UNUSED(v);
+ o << "Null";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TBoolType, o, v) {
+ Y_UNUSED(v);
+ o << "Bool";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TInt8Type, o, v) {
+ Y_UNUSED(v);
+ o << "Int8";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TInt16Type, o, v) {
+ Y_UNUSED(v);
+ o << "Int16";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TInt32Type, o, v) {
+ Y_UNUSED(v);
+ o << "Int32";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TInt64Type, o, v) {
+ Y_UNUSED(v);
+ o << "Int64";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TUint8Type, o, v) {
+ Y_UNUSED(v);
+ o << "Uint8";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TUint16Type, o, v) {
+ Y_UNUSED(v);
+ o << "Uint16";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TUint32Type, o, v) {
+ Y_UNUSED(v);
+ o << "Uint32";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TUint64Type, o, v) {
+ Y_UNUSED(v);
+ o << "Uint64";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TFloatType, o, v) {
+ Y_UNUSED(v);
+ o << "Float";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TDoubleType, o, v) {
+ Y_UNUSED(v);
+ o << "Double";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TStringType, o, v) {
+ Y_UNUSED(v);
+ o << "String";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TUtf8Type, o, v) {
+ Y_UNUSED(v);
+ o << "Utf8";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TDateType, o, v) {
+ Y_UNUSED(v);
+ o << "Date";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TDatetimeType, o, v) {
+ Y_UNUSED(v);
+ o << "Datetime";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TTimestampType, o, v) {
+ Y_UNUSED(v);
+ o << "Timestamp";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TTzDateType, o, v) {
+ Y_UNUSED(v);
+ o << "TzDate";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TTzDatetimeType, o, v) {
+ Y_UNUSED(v);
+ o << "TzDatetime";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TTzTimestampType, o, v) {
+ Y_UNUSED(v);
+ o << "TzTimestamp";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TIntervalType, o, v) {
+ Y_UNUSED(v);
+ o << "Interval";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TDecimalType, o, v) {
+ o << "Decimal(" << (i32)v.GetPrecision() << ", " << (i32)v.GetScale() << ')';
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TJsonType, o, v) {
+ Y_UNUSED(v);
+ o << "Json";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TYsonType, o, v) {
+ Y_UNUSED(v);
+ o << "Yson";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TUuidType, o, v) {
+ Y_UNUSED(v);
+ o << "Uuid";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TOptionalType, o, v) {
+ o << "Optional<" << *v.GetItemTypeRaw() << ">";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TListType, o, v) {
+ o << "List<" << *v.GetItemTypeRaw() << ">";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TDictType, o, v) {
+ o << "Dict<" << *v.GetKeyTypeRaw() << ", " << *v.GetValueTypeRaw() << ">";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TStructType, o, v) {
+ o << "Struct";
+
+ if (v.GetName().Defined()) {
+ o << "[" << Quote(*v.GetName()) << "]";
+ }
+
+ o << "<";
+ const char* sep = "";
+ for (auto& item : v.GetMembers()) {
+ o << sep << Quote(item.GetName()) << ": " << *item.GetTypeRaw();
+ sep = ", ";
+ }
+ o << ">";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TTupleType, o, v) {
+ o << "Tuple";
+
+ if (v.GetName().Defined()) {
+ o << "[" << Quote(*v.GetName()) << "]";
+ }
+
+ o << "<";
+ const char* sep = "";
+ for (auto& item : v.GetElements()) {
+ o << sep << *item.GetTypeRaw();
+ sep = ", ";
+ }
+ o << ">";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TVariantType, o, v) {
+ o << "Variant";
+
+ if (v.GetName().Defined()) {
+ o << "[" << Quote(*v.GetName()) << "]";
+ }
+
+ o << "<";
+ v.VisitUnderlyingRaw(
+ TOverloaded{
+ [&o](const NTi::TStructType* s) {
+ const char* sep = "";
+ for (auto& item : s->GetMembers()) {
+ o << sep << Quote(item.GetName()) << ": " << *item.GetTypeRaw();
+ sep = ", ";
+ }
+ },
+ [&o](const NTi::TTupleType* t) {
+ const char* sep = "";
+ for (auto& item : t->GetElements()) {
+ o << sep << *item.GetTypeRaw();
+ sep = ", ";
+ }
+ }});
+ o << ">";
+}
+
+Y_DECLARE_OUT_SPEC(, NTi::TTaggedType, o, v) {
+ o << "Tagged<" << *v.GetItemTypeRaw() << ", " << Quote(v.GetTag()) << ">";
+}
+
+static_assert(std::is_trivially_destructible_v<NTi::TVoidType>);
+static_assert(std::is_trivially_destructible_v<NTi::TNullType>);
+static_assert(std::is_trivially_destructible_v<NTi::TPrimitiveType>);
+static_assert(std::is_trivially_destructible_v<NTi::TBoolType>);
+static_assert(std::is_trivially_destructible_v<NTi::TInt8Type>);
+static_assert(std::is_trivially_destructible_v<NTi::TInt16Type>);
+static_assert(std::is_trivially_destructible_v<NTi::TInt32Type>);
+static_assert(std::is_trivially_destructible_v<NTi::TInt64Type>);
+static_assert(std::is_trivially_destructible_v<NTi::TUint8Type>);
+static_assert(std::is_trivially_destructible_v<NTi::TUint16Type>);
+static_assert(std::is_trivially_destructible_v<NTi::TUint32Type>);
+static_assert(std::is_trivially_destructible_v<NTi::TUint64Type>);
+static_assert(std::is_trivially_destructible_v<NTi::TFloatType>);
+static_assert(std::is_trivially_destructible_v<NTi::TDoubleType>);
+static_assert(std::is_trivially_destructible_v<NTi::TStringType>);
+static_assert(std::is_trivially_destructible_v<NTi::TUtf8Type>);
+static_assert(std::is_trivially_destructible_v<NTi::TDateType>);
+static_assert(std::is_trivially_destructible_v<NTi::TDatetimeType>);
+static_assert(std::is_trivially_destructible_v<NTi::TTimestampType>);
+static_assert(std::is_trivially_destructible_v<NTi::TTzDateType>);
+static_assert(std::is_trivially_destructible_v<NTi::TTzDatetimeType>);
+static_assert(std::is_trivially_destructible_v<NTi::TTzTimestampType>);
+static_assert(std::is_trivially_destructible_v<NTi::TIntervalType>);
+static_assert(std::is_trivially_destructible_v<NTi::TDecimalType>);
+static_assert(std::is_trivially_destructible_v<NTi::TJsonType>);
+static_assert(std::is_trivially_destructible_v<NTi::TYsonType>);
+static_assert(std::is_trivially_destructible_v<NTi::TUuidType>);
+static_assert(std::is_trivially_destructible_v<NTi::TOptionalType>);
+static_assert(std::is_trivially_destructible_v<NTi::TListType>);
+static_assert(std::is_trivially_destructible_v<NTi::TDictType>);
+static_assert(std::is_trivially_destructible_v<NTi::TStructType>);
+static_assert(std::is_trivially_destructible_v<NTi::TStructType::TMember>);
+static_assert(std::is_trivially_destructible_v<NTi::TTupleType>);
+static_assert(std::is_trivially_destructible_v<NTi::TTupleType::TElement>);
+static_assert(std::is_trivially_destructible_v<NTi::TVariantType>);
+static_assert(std::is_trivially_destructible_v<NTi::TTaggedType>);
diff --git a/library/cpp/type_info/type.h b/library/cpp/type_info/type.h
new file mode 100644
index 0000000000..1577f1cee3
--- /dev/null
+++ b/library/cpp/type_info/type.h
@@ -0,0 +1,2421 @@
+#pragma once
+
+//! @file type.h
+//!
+//! Hierarchy of classes that represent types.
+#include "fwd.h"
+
+#include "error.h"
+#include "type_list.h"
+
+#include <atomic>
+#include <util/generic/array_ref.h>
+#include <util/generic/maybe.h>
+#include <util/generic/strbuf.h>
+#include <util/generic/string.h>
+
+namespace NTi {
+ /// Represents a single type.
+ ///
+ /// Create instances of types using type factory (see `NTi::ITypeFactory`).
+ ///
+ /// Introspect them using associated methods and functions that work with `ETypeName`.
+ ///
+ /// Pattern-match them using the `Visit` method.
+ ///
+ /// Serialize and deserialize them using functions from `NTi::NIo`.
+ class TType {
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+ template <typename T>
+ friend class ::TDefaultIntrusivePtrOps;
+
+ public:
+ TTypePtr AsPtr() const noexcept {
+ return const_cast<TType*>(this);
+ }
+
+ protected:
+ explicit TType(TMaybe<ui64> hash, ETypeName typeName) noexcept;
+
+ protected:
+ /// Calculate hash for this type. This function is lazily called by `GetHash`.
+ ///
+ /// Note: this function is not marked as `virtual` because we use our own dispatch via `Visit`.
+ ui64 CalculateHash() const noexcept;
+
+ public:
+ /// Get hash of this type. Hashes follow the 'strict equivalence' relation (see `type_equivalence.h`).
+ ui64 GetHash() const;
+
+ /// Get hash of this type. If hash is not calculated, returns nothing.
+ TMaybe<ui64> GetHashRaw() const noexcept;
+
+ /// Get name of this type as a `NTi::ETypeName` enumerator.
+ ETypeName GetTypeName() const noexcept {
+ return TypeName_;
+ }
+
+ /// @name Simple type downcast functions
+ ///
+ /// Check if type is of the given subclass and convert between subclasses.
+ /// Conversions panic if downcasting into an incompatible type.
+ ///
+ /// @{
+ inline bool IsVoid() const noexcept;
+ inline TVoidTypePtr AsVoid() const noexcept;
+ inline const TVoidType* AsVoidRaw() const noexcept;
+
+ inline bool IsNull() const noexcept;
+ inline TNullTypePtr AsNull() const noexcept;
+ inline const TNullType* AsNullRaw() const noexcept;
+
+ inline bool IsPrimitive() const noexcept;
+ inline TPrimitiveTypePtr AsPrimitive() const noexcept;
+ inline const TPrimitiveType* AsPrimitiveRaw() const noexcept;
+
+ inline bool IsBool() const noexcept;
+ inline TBoolTypePtr AsBool() const noexcept;
+ inline const TBoolType* AsBoolRaw() const noexcept;
+
+ inline bool IsInt8() const noexcept;
+ inline TInt8TypePtr AsInt8() const noexcept;
+ inline const TInt8Type* AsInt8Raw() const noexcept;
+
+ inline bool IsInt16() const noexcept;
+ inline TInt16TypePtr AsInt16() const noexcept;
+ inline const TInt16Type* AsInt16Raw() const noexcept;
+
+ inline bool IsInt32() const noexcept;
+ inline TInt32TypePtr AsInt32() const noexcept;
+ inline const TInt32Type* AsInt32Raw() const noexcept;
+
+ inline bool IsInt64() const noexcept;
+ inline TInt64TypePtr AsInt64() const noexcept;
+ inline const TInt64Type* AsInt64Raw() const noexcept;
+
+ inline bool IsUint8() const noexcept;
+ inline TUint8TypePtr AsUint8() const noexcept;
+ inline const TUint8Type* AsUint8Raw() const noexcept;
+
+ inline bool IsUint16() const noexcept;
+ inline TUint16TypePtr AsUint16() const noexcept;
+ inline const TUint16Type* AsUint16Raw() const noexcept;
+
+ inline bool IsUint32() const noexcept;
+ inline TUint32TypePtr AsUint32() const noexcept;
+ inline const TUint32Type* AsUint32Raw() const noexcept;
+
+ inline bool IsUint64() const noexcept;
+ inline TUint64TypePtr AsUint64() const noexcept;
+ inline const TUint64Type* AsUint64Raw() const noexcept;
+
+ inline bool IsFloat() const noexcept;
+ inline TFloatTypePtr AsFloat() const noexcept;
+ inline const TFloatType* AsFloatRaw() const noexcept;
+
+ inline bool IsDouble() const noexcept;
+ inline TDoubleTypePtr AsDouble() const noexcept;
+ inline const TDoubleType* AsDoubleRaw() const noexcept;
+
+ inline bool IsString() const noexcept;
+ inline TStringTypePtr AsString() const noexcept;
+ inline const TStringType* AsStringRaw() const noexcept;
+
+ inline bool IsUtf8() const noexcept;
+ inline TUtf8TypePtr AsUtf8() const noexcept;
+ inline const TUtf8Type* AsUtf8Raw() const noexcept;
+
+ inline bool IsDate() const noexcept;
+ inline TDateTypePtr AsDate() const noexcept;
+ inline const TDateType* AsDateRaw() const noexcept;
+
+ inline bool IsDatetime() const noexcept;
+ inline TDatetimeTypePtr AsDatetime() const noexcept;
+ inline const TDatetimeType* AsDatetimeRaw() const noexcept;
+
+ inline bool IsTimestamp() const noexcept;
+ inline TTimestampTypePtr AsTimestamp() const noexcept;
+ inline const TTimestampType* AsTimestampRaw() const noexcept;
+
+ inline bool IsTzDate() const noexcept;
+ inline TTzDateTypePtr AsTzDate() const noexcept;
+ inline const TTzDateType* AsTzDateRaw() const noexcept;
+
+ inline bool IsTzDatetime() const noexcept;
+ inline TTzDatetimeTypePtr AsTzDatetime() const noexcept;
+ inline const TTzDatetimeType* AsTzDatetimeRaw() const noexcept;
+
+ inline bool IsTzTimestamp() const noexcept;
+ inline TTzTimestampTypePtr AsTzTimestamp() const noexcept;
+ inline const TTzTimestampType* AsTzTimestampRaw() const noexcept;
+
+ inline bool IsInterval() const noexcept;
+ inline TIntervalTypePtr AsInterval() const noexcept;
+ inline const TIntervalType* AsIntervalRaw() const noexcept;
+
+ inline bool IsDecimal() const noexcept;
+ inline TDecimalTypePtr AsDecimal() const noexcept;
+ inline const TDecimalType* AsDecimalRaw() const noexcept;
+
+ inline bool IsJson() const noexcept;
+ inline TJsonTypePtr AsJson() const noexcept;
+ inline const TJsonType* AsJsonRaw() const noexcept;
+
+ inline bool IsYson() const noexcept;
+ inline TYsonTypePtr AsYson() const noexcept;
+ inline const TYsonType* AsYsonRaw() const noexcept;
+
+ inline bool IsUuid() const noexcept;
+ inline TUuidTypePtr AsUuid() const noexcept;
+ inline const TUuidType* AsUuidRaw() const noexcept;
+
+ inline bool IsOptional() const noexcept;
+ inline TOptionalTypePtr AsOptional() const noexcept;
+ inline const TOptionalType* AsOptionalRaw() const noexcept;
+
+ inline bool IsList() const noexcept;
+ inline TListTypePtr AsList() const noexcept;
+ inline const TListType* AsListRaw() const noexcept;
+
+ inline bool IsDict() const noexcept;
+ inline TDictTypePtr AsDict() const noexcept;
+ inline const TDictType* AsDictRaw() const noexcept;
+
+ inline bool IsStruct() const noexcept;
+ inline TStructTypePtr AsStruct() const noexcept;
+ inline const TStructType* AsStructRaw() const noexcept;
+
+ inline bool IsTuple() const noexcept;
+ inline TTupleTypePtr AsTuple() const noexcept;
+ inline const TTupleType* AsTupleRaw() const noexcept;
+
+ inline bool IsVariant() const noexcept;
+ inline TVariantTypePtr AsVariant() const noexcept;
+ inline const TVariantType* AsVariantRaw() const noexcept;
+
+ inline bool IsTagged() const noexcept;
+ inline TTaggedTypePtr AsTagged() const noexcept;
+ inline const TTaggedType* AsTaggedRaw() const noexcept;
+
+ /// @}
+
+ /// Recursively descends to tagged types and returns first non-tagged type.
+ TTypePtr StripTags() const noexcept;
+
+ /// Like `StripTags`, but returns a raw pointer.
+ const TType* StripTagsRaw() const noexcept;
+
+ /// Recursively descends to optional types and returns first non-optional type.
+ TTypePtr StripOptionals() const noexcept;
+
+ /// Like `StripOptionals`, but returns a raw pointer.
+ const TType* StripOptionalsRaw() const noexcept;
+
+ /// Recursively descends to tagged and optional types and returns first non-tagged non-optional type.
+ TTypePtr StripTagsAndOptionals() const noexcept;
+
+ /// Like `StripTagsAndOptionals`, but returns a raw pointer.
+ const TType* StripTagsAndOptionalsRaw() const noexcept;
+
+ /// Cast this base class down to the most-derived class and pass it to the `visitor`.
+ ///
+ /// This function is used as a safer alternative to manually downcasting types via `IsType`/`AsType` calls.
+ /// It works like `std::visit` for types, except that it doesn't produce as much code bloat as `std::visit`
+ /// does, and can be optimized better. It casts an instance of `NTi::TType` down to the most-derived class,
+ /// and passes an intrusive pointer to that concrete type to the `visitor` functor. That is, `visitor` should
+ /// be a callable which can handle `NTi::TVoidTypePtr`, `NTi::TOptionalTypePtr`, etc.
+ ///
+ /// This function returns whatever the `visitor` returns.
+ ///
+ ///
+ /// # Example: visitor
+ ///
+ /// A simple visitor that returns name for a type would look like this:
+ ///
+ /// ```
+ /// struct TGetNameVisitor {
+ /// TString operator()(TVoidTypePtr) {
+ /// return "Void";
+ /// }
+ ///
+ /// TString operator()(TStringTypePtr) {
+ /// return "String";
+ /// }
+ ///
+ /// // ...
+ ///
+ /// TString operator()(TStructTypePtr type) {
+ /// return TString(type->GetName().GetOrElse("Struct"));;
+ /// }
+ ///
+ /// // ...
+ /// }
+ /// ```
+ ///
+ /// Now, we can use this visitor as following:
+ ///
+ /// ```
+ /// TString typeName = type->Visit(TGetNameVisitor());
+ /// ```
+ ///
+ ///
+ /// # Example: overloaded struct
+ ///
+ /// Writing a separate struct each time one needs a visitor is tedious. Thanks to C++17 magic, we may avoid it.
+ /// Using lambdas and `TOverloaded` from `library/cpp/overloaded` allows replacing separate struct with
+ /// a bunch of lambdas:
+ ///
+ /// ```
+ /// TString typeName = type->Visit(TOverloaded{
+ /// [](TVoidTypePtr) -> TString {
+ /// return "Void";
+ /// },
+ /// [](TStringTypePtr) -> TString {
+ /// return "String";
+ /// },
+ ///
+ /// // ...
+ ///
+ /// });
+ /// ```
+ ///
+ ///
+ /// # Example: handling all primitives at once
+ ///
+ /// Since all primitives derive from `TPrimitiveType`, they can be handled all at once,
+ /// by accepting `TPrimitiveTypePtr`:
+ ///
+ /// ```
+ /// TString typeName = type->Visit(TOverloaded{
+ /// // All primitive types are handled by this lambda.
+ /// [](TPrimitiveTypePtr) -> TString {
+ /// return "Primitive";
+ /// },
+ ///
+ /// // Special handler for string type. Strings are handled by this lambda
+ /// // because of how C++ prioritizes overloads.
+ /// [](TStringTypePtr) -> TString {
+ /// return "String";
+ /// },
+ ///
+ /// // ...
+ ///
+ /// });
+ /// ```
+ template <typename V>
+ inline decltype(auto) Visit(V&& visitor) const;
+
+ /// Like `Visit`, but passes const raw pointers to the visitor.
+ template <typename V>
+ inline decltype(auto) VisitRaw(V&& visitor) const;
+
+ /// @}
+
+ protected:
+ /// @name Internal interface for adoption semantics support
+ ///
+ /// Do not call these functions manually!
+ ///
+ /// See `type_factory.h`'s section on implementation details for more info.
+ ///
+ /// @{
+ //-
+ /// Create a new instance of the class using this instance as a prototype.
+ ///
+ /// This is a [virtual copy constructor]. Typical implementation does the following:
+ ///
+ /// 1. for nested types, if any, it calls the factory's `Own` function. The `Own` acquires internal
+ /// ownership over the nested types, thus guaranteeing that they'll outlive the object that've
+ /// owned them. Depending on the particular factory implementation, `Own` may either recursively deepcopy
+ /// the whole nested type, increment some reference counter, or do nothing;
+ /// 2. for other resources owned by this type (i.e. arrays, strings, etc.), it copies them into the given
+ /// factory by calling factory's `New` and `Allocate` functions;
+ /// 3. finally, it creates a new instance of the type by invoking its constructor via the factory's
+ /// `New` function.
+ ///
+ /// Note: there are no guarantees on the value stored in `FactoryOrRc_` during invocation of this function.
+ /// Specifically, creating types on the stack (`FactoryOrRc_` is `0` in this case) and moving them
+ /// into a factory is a valid technique used extensively throughout this library.
+ ///
+ /// See `type_factory.h`'s section on implementation details for more info.
+ ///
+ /// Note: this function is not marked as `virtual` because we use our own dispatch via `Visit`.
+ ///
+ /// [virtual move constructor]: https://isocpp.org/wiki/faq/virtual-functions#virtual-ctors
+ const TType* Clone(ITypeFactoryInternal& factory) const noexcept;
+
+ /// Release internal resources that were allocated in `Clone`.
+ ///
+ /// This function is the opposite of `Clone`. It releases all memory that was allocated within `Clone`,
+ /// and disowns nested types.
+ ///
+ /// This function is called by factories that perform active memory management, such as the default
+ /// heap factory. Typical implementation does the following:
+ ///
+ /// 1. for each `Clone`'s call to `Own` it calls `Disown`. The `Disown` releases internal ownership
+ /// over the nested types, thus allowing factory to free their memory. Depending
+ /// on the particular factory implementation, `Disown` may either decrement some reference counter,
+ /// call `Drop` and free the underlying memory, or do nothing;
+ /// 2. for each `Clone`'s call to `New` and `Allocate`, it calls `Delete` and `Free`;
+ /// 3. it should *not* call `Delete(this)` to mirror `Clone`'s final call to `New` (see the third bullet
+ /// in the `Clone`'s documentation). It is the factory's job to release memory under the type that's
+ /// being dropped.
+ ///
+ /// Note: there are no guarantees on whether this method will be called or not. For example, the default
+ /// heap factory will call it when some type's reference counter reaches zero. The default memory pool
+ /// factory will not call it.
+ ///
+ /// Note: this function is not marked as `virtual` because we use our own dispatch via `Visit`.
+ ///
+ /// See `type_factory.h`'s section on implementation details for more info.
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+
+ /// Get factory that manages this instance.
+ ///
+ /// If this instance is refcounted, returns the default heap factory. If it is unmanaged, returns `nullptr`.
+ /// Otherwise, returns pointer to the instance's factory.
+ ///
+ /// Remember that factories are not thread safe, thus using factory from this method may not be safe.
+ ITypeFactoryInternal* GetFactory() const noexcept;
+
+ /// Mark this instance as managed by the given factory.
+ void SetFactory(ITypeFactoryInternal* factory) noexcept;
+
+ /// Get factory's internal interface.
+ static ITypeFactoryInternal& FactoryInternal(ITypeFactory& factory) noexcept;
+
+ /// @}
+
+ protected:
+ /// @name Internal interface for reference counting
+ ///
+ /// See `type_factory.h`'s section on implementation details for more info.
+ ///
+ /// @{
+ //-
+ /// Increase reference count of this type.
+ void RefSelf() noexcept {
+ RefImpl</* RefFactory = */ false>();
+ }
+
+ /// Increase reference count of this type and its factory.
+ void Ref() noexcept {
+ RefImpl</* RefFactory = */ true>();
+ }
+
+ /// Decrease reference count of this type.
+ void UnRefSelf() noexcept {
+ UnRefImpl</* UnRefFactory = */ false>();
+ }
+
+ /// Decrease reference count of this type and its factory.
+ void UnRef() noexcept {
+ UnRefImpl</* UnRefFactory = */ true>();
+ }
+
+ /// Decrease reference count of this type. Panic if any of it reaches zero.
+ void DecRefSelf() noexcept {
+ DecRefImpl</* DecRefFactory = */ false>();
+ }
+
+ /// Decrease reference count of type and its factory. Panic if any of it reaches zero.
+ void DecRef() noexcept {
+ DecRefImpl</* DecRefFactory = */ true>();
+ }
+
+ /// Get current reference count for this type.
+ long RefCount() const noexcept {
+ return RefCountImpl();
+ }
+
+ /// @}
+
+ private:
+ template <bool RefFactory>
+ void RefImpl() noexcept;
+
+ template <bool UnRefFactory>
+ void UnRefImpl() noexcept;
+
+ template <bool DecRefFactory>
+ void DecRefImpl() noexcept;
+
+ long RefCountImpl() const noexcept;
+
+ protected:
+ /// Helper for implementing `Clone` with caching.
+ template <typename T, typename TCtor>
+ static const T* Cached(const T* type, ITypeFactoryInternal& factory, TCtor&& ctor);
+
+ private:
+ /// Pointer to the type factory that've created this instance.
+ /// If this instance is static, this variable contains zero.
+ /// If this instance was created by the default heap factory, this variable is used as a reference counter.
+ std::atomic<size_t> FactoryOrRc_ = 0;
+
+ /// Name of this type. Can be used to check before downcast.
+ ETypeName TypeName_;
+
+ /// Hash is calculated lazily.
+ mutable std::atomic<bool> HasHash_;
+ mutable std::atomic<ui64> Hash_;
+
+ private:
+ static bool IsRc(size_t factoryOrRc) noexcept {
+ return factoryOrRc & 1u;
+ }
+ static bool IsFactory(size_t factoryOrRc) noexcept {
+ return factoryOrRc != 0 && !IsRc(factoryOrRc);
+ }
+ static size_t CastFromFactory(ITypeFactoryInternal* factory) noexcept {
+ return reinterpret_cast<size_t>(factory);
+ }
+ static ITypeFactoryInternal* CastToFactory(size_t factoryOrRc) noexcept {
+ return reinterpret_cast<ITypeFactoryInternal*>(factoryOrRc);
+ }
+ };
+
+ static_assert(sizeof(TType) == 24);
+
+ bool operator==(const TTypePtr& lhs, const TTypePtr& rhs) = delete;
+ bool operator!=(const TTypePtr& lhs, const TTypePtr& rhs) = delete;
+
+ /// @brief Check for strict equivalence of the types.
+ ///
+ /// @see NTi::NEq::TStrictlyEqual
+ ///
+ /// @{
+ bool operator==(const TType& lhs, const TType& rhs);
+ bool operator!=(const TType& lhs, const TType& rhs);
+ /// @}
+
+ /// A singular type. This type has only one value. When serialized, it takes no space because it carries no data.
+ ///
+ /// Historically, YQL's `Void` is what's known as unit type (see https://en.wikipedia.org/wiki/Unit_type),
+ /// i.e. a type with only one possible value. This is similar to Python's `NoneType` or Rust's `()`.
+ class TVoidType final: public TType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TVoidTypePtr AsPtr() const noexcept {
+ return const_cast<TVoidType*>(this);
+ }
+
+ private:
+ explicit TVoidType();
+
+ public:
+ static TVoidTypePtr Instance();
+ static const TVoidType* InstanceRaw();
+
+ protected:
+ const TVoidType* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+ };
+
+ /// Create new `Void` type using the default heap factory.
+ TVoidTypePtr Void();
+
+ /// A singular type, value of an empty optional.
+ ///
+ /// This type is used by YQL for `NULL` literal. Use `TVoidType` unless you have reasons to use this one.
+ class TNullType final: public TType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TNullTypePtr AsPtr() const noexcept {
+ return const_cast<TNullType*>(this);
+ }
+
+ private:
+ explicit TNullType();
+
+ public:
+ static TNullTypePtr Instance();
+ static const TNullType* InstanceRaw();
+
+ protected:
+ const TNullType* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+ };
+
+ /// Base class for all primitive types.
+ class TPrimitiveType: public TType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TPrimitiveTypePtr AsPtr() const noexcept {
+ return const_cast<TPrimitiveType*>(this);
+ }
+
+ protected:
+ explicit TPrimitiveType(TMaybe<ui64> hash, EPrimitiveTypeName primitiveTypeName) noexcept;
+
+ public:
+ /// Get name of this primitive type as a `NTi::EPrimitiveTypeName` enumerator.
+ EPrimitiveTypeName GetPrimitiveTypeName() const noexcept {
+ return ToPrimitiveTypeName(GetTypeName());
+ }
+
+ /// Cast this scalar class down to the most-derived type and pass it to the `visitor`.
+ ///
+ /// This function works like `NTi::TType::Visit`, but only handles primitive types.
+ ///
+ ///
+ /// # Example:
+ ///
+ /// ```
+ /// auto name = scalar->VisitPrimitive(TOverloaded{
+ /// [](TBoolTypePtr t) { return "Bool" },
+ /// [](TStringTypePtr t) { return "String" },
+ /// // ...
+ /// });
+ /// ```
+ template <typename V>
+ inline decltype(auto) VisitPrimitive(V&& visitor) const;
+
+ /// Like `VisitPrimitive`, but passes raw pointers to the visitor.
+ template <typename V>
+ inline decltype(auto) VisitPrimitiveRaw(V&& visitor) const;
+ };
+
+ /// A logical type capable of holding one of the two values: true or false.
+ class TBoolType final: public TPrimitiveType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TBoolTypePtr AsPtr() const noexcept {
+ return const_cast<TBoolType*>(this);
+ }
+
+ private:
+ explicit TBoolType();
+
+ public:
+ static TBoolTypePtr Instance();
+ static const TBoolType* InstanceRaw();
+
+ protected:
+ const TBoolType* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+ };
+
+ /// A signed integer, one byte.
+ class TInt8Type final: public TPrimitiveType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TInt8TypePtr AsPtr() const noexcept {
+ return const_cast<TInt8Type*>(this);
+ }
+
+ private:
+ explicit TInt8Type();
+
+ public:
+ static TInt8TypePtr Instance();
+ static const TInt8Type* InstanceRaw();
+
+ protected:
+ const TInt8Type* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+ };
+
+ /// A signed integer, two bytes.
+ class TInt16Type final: public TPrimitiveType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TInt16TypePtr AsPtr() const noexcept {
+ return const_cast<TInt16Type*>(this);
+ }
+
+ private:
+ explicit TInt16Type();
+
+ public:
+ static TInt16TypePtr Instance();
+ static const TInt16Type* InstanceRaw();
+
+ protected:
+ const TInt16Type* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+ };
+
+ /// A signed integer, four bytes.
+ class TInt32Type final: public TPrimitiveType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TInt32TypePtr AsPtr() const noexcept {
+ return const_cast<TInt32Type*>(this);
+ }
+
+ private:
+ explicit TInt32Type();
+
+ public:
+ static TInt32TypePtr Instance();
+ static const TInt32Type* InstanceRaw();
+
+ protected:
+ const TInt32Type* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+ };
+
+ /// A signed integer, eight bytes.
+ class TInt64Type final: public TPrimitiveType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TInt64TypePtr AsPtr() const noexcept {
+ return const_cast<TInt64Type*>(this);
+ }
+
+ private:
+ explicit TInt64Type();
+
+ public:
+ static TInt64TypePtr Instance();
+ static const TInt64Type* InstanceRaw();
+
+ protected:
+ const TInt64Type* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+ };
+
+ /// An unsigned integer, one byte.
+ class TUint8Type final: public TPrimitiveType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TUint8TypePtr AsPtr() const noexcept {
+ return const_cast<TUint8Type*>(this);
+ }
+
+ private:
+ explicit TUint8Type();
+
+ public:
+ static TUint8TypePtr Instance();
+ static const TUint8Type* InstanceRaw();
+
+ protected:
+ const TUint8Type* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+ };
+
+ /// An unsigned integer, two bytes.
+ class TUint16Type final: public TPrimitiveType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TUint16TypePtr AsPtr() const noexcept {
+ return const_cast<TUint16Type*>(this);
+ }
+
+ private:
+ explicit TUint16Type();
+
+ public:
+ static TUint16TypePtr Instance();
+ static const TUint16Type* InstanceRaw();
+
+ protected:
+ const TUint16Type* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+ };
+
+ /// An unsigned integer, four bytes.
+ class TUint32Type final: public TPrimitiveType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TUint32TypePtr AsPtr() const noexcept {
+ return const_cast<TUint32Type*>(this);
+ }
+
+ private:
+ explicit TUint32Type();
+
+ public:
+ static TUint32TypePtr Instance();
+ static const TUint32Type* InstanceRaw();
+
+ protected:
+ const TUint32Type* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+ };
+
+ /// An unsigned integer, eight bytes.
+ class TUint64Type final: public TPrimitiveType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TUint64TypePtr AsPtr() const noexcept {
+ return const_cast<TUint64Type*>(this);
+ }
+
+ private:
+ explicit TUint64Type();
+
+ public:
+ static TUint64TypePtr Instance();
+ static const TUint64Type* InstanceRaw();
+
+ protected:
+ const TUint64Type* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+ };
+
+ /// A floating point number, four bytes.
+ class TFloatType final: public TPrimitiveType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TFloatTypePtr AsPtr() const noexcept {
+ return const_cast<TFloatType*>(this);
+ }
+
+ private:
+ explicit TFloatType();
+
+ public:
+ static TFloatTypePtr Instance();
+ static const TFloatType* InstanceRaw();
+
+ protected:
+ const TFloatType* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+ };
+
+ /// A floating point number, eight bytes.
+ class TDoubleType final: public TPrimitiveType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TDoubleTypePtr AsPtr() const noexcept {
+ return const_cast<TDoubleType*>(this);
+ }
+
+ private:
+ explicit TDoubleType();
+
+ public:
+ static TDoubleTypePtr Instance();
+ static const TDoubleType* InstanceRaw();
+
+ protected:
+ const TDoubleType* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+ };
+
+ /// A binary blob.
+ ///
+ /// This type can be used for any binary data. For text, consider using type `Utf8`.
+ class TStringType final: public TPrimitiveType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TStringTypePtr AsPtr() const noexcept {
+ return const_cast<TStringType*>(this);
+ }
+
+ private:
+ explicit TStringType();
+
+ public:
+ static TStringTypePtr Instance();
+ static const TStringType* InstanceRaw();
+
+ protected:
+ const TStringType* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+ };
+
+ /// A utf-8 encoded text.
+ class TUtf8Type final: public TPrimitiveType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TUtf8TypePtr AsPtr() const noexcept {
+ return const_cast<TUtf8Type*>(this);
+ }
+
+ private:
+ explicit TUtf8Type();
+
+ public:
+ static TUtf8TypePtr Instance();
+ static const TUtf8Type* InstanceRaw();
+
+ protected:
+ const TUtf8Type* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+ };
+
+ /// An absolute point in time in range `[1970-01-01, 2106-01-01)`, precision up to days.
+ class TDateType final: public TPrimitiveType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TDateTypePtr AsPtr() const noexcept {
+ return const_cast<TDateType*>(this);
+ }
+
+ private:
+ explicit TDateType();
+
+ public:
+ static TDateTypePtr Instance();
+ static const TDateType* InstanceRaw();
+
+ protected:
+ const TDateType* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+ };
+
+ /// An absolute point in time in range `[1970-01-01, 2106-01-01)`, precision up to seconds.
+ class TDatetimeType final: public TPrimitiveType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TDatetimeTypePtr AsPtr() const noexcept {
+ return const_cast<TDatetimeType*>(this);
+ }
+
+ private:
+ explicit TDatetimeType();
+
+ public:
+ static TDatetimeTypePtr Instance();
+ static const TDatetimeType* InstanceRaw();
+
+ protected:
+ const TDatetimeType* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+ };
+
+ /// An absolute point in time in range `[1970-01-01, 2106-01-01)`, precision up to microseconds.
+ class TTimestampType final: public TPrimitiveType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TTimestampTypePtr AsPtr() const noexcept {
+ return const_cast<TTimestampType*>(this);
+ }
+
+ private:
+ explicit TTimestampType();
+
+ public:
+ static TTimestampTypePtr Instance();
+ static const TTimestampType* InstanceRaw();
+
+ protected:
+ const TTimestampType* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+ };
+
+ /// `TDateType` with additional timezone mark.
+ class TTzDateType final: public TPrimitiveType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TTzDateTypePtr AsPtr() const noexcept {
+ return const_cast<TTzDateType*>(this);
+ }
+
+ private:
+ explicit TTzDateType();
+
+ public:
+ static TTzDateTypePtr Instance();
+ static const TTzDateType* InstanceRaw();
+
+ protected:
+ const TTzDateType* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+ };
+
+ /// `TDatetimeType` with additional timezone mark.
+ class TTzDatetimeType final: public TPrimitiveType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TTzDatetimeTypePtr AsPtr() const noexcept {
+ return const_cast<TTzDatetimeType*>(this);
+ }
+
+ private:
+ explicit TTzDatetimeType();
+
+ public:
+ static TTzDatetimeTypePtr Instance();
+ static const TTzDatetimeType* InstanceRaw();
+
+ protected:
+ const TTzDatetimeType* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+ };
+
+ /// `TTimestampType` with additional timezone mark.
+ class TTzTimestampType final: public TPrimitiveType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TTzTimestampTypePtr AsPtr() const noexcept {
+ return const_cast<TTzTimestampType*>(this);
+ }
+
+ private:
+ explicit TTzTimestampType();
+
+ public:
+ static TTzTimestampTypePtr Instance();
+ static const TTzTimestampType* InstanceRaw();
+
+ protected:
+ const TTzTimestampType* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+ };
+
+ /// Signed delta between two timestamps.
+ class TIntervalType final: public TPrimitiveType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TIntervalTypePtr AsPtr() const noexcept {
+ return const_cast<TIntervalType*>(this);
+ }
+
+ private:
+ explicit TIntervalType();
+
+ public:
+ static TIntervalTypePtr Instance();
+ static const TIntervalType* InstanceRaw();
+
+ protected:
+ const TIntervalType* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+ };
+
+ /// A 128-bit number with controlled exponent/significand size.
+ ///
+ /// Decimal is a type for extra precise calculations. Internally, it is represented by a float-like 128-bit number.
+ ///
+ /// Two parameters control number of decimal digits in the decimal value. `Precision` is the total number
+ /// of decimal digits. `Scale` is the number of digits after the decimal point.
+ class TDecimalType final: public TPrimitiveType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TDecimalTypePtr AsPtr() const noexcept {
+ return const_cast<TDecimalType*>(this);
+ }
+
+ private:
+ explicit TDecimalType(TMaybe<ui64> hash, ui8 precision, ui8 scale) noexcept;
+ static TDecimalTypePtr Create(ITypeFactory& factory, ui8 precision, ui8 scale);
+ static const TDecimalType* CreateRaw(ITypeFactory& factory, ui8 precision, ui8 scale);
+
+ protected:
+ ui64 CalculateHash() const noexcept;
+
+ public:
+ /// Get total number of decimal digits.
+ ui8 GetPrecision() const noexcept {
+ return Precision_;
+ }
+
+ /// Get number of decimal digits after the decimal point.
+ ui8 GetScale() const noexcept {
+ return Scale_;
+ }
+
+ protected:
+ const TDecimalType* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+
+ private:
+ ui8 Precision_;
+ ui8 Scale_;
+ };
+
+ /// A string with valid JSON.
+ class TJsonType final: public TPrimitiveType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TJsonTypePtr AsPtr() const noexcept {
+ return const_cast<TJsonType*>(this);
+ }
+
+ private:
+ explicit TJsonType();
+
+ public:
+ static TJsonTypePtr Instance();
+ static const TJsonType* InstanceRaw();
+
+ protected:
+ const TJsonType* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+ };
+
+ /// A string with valid YSON.
+ class TYsonType final: public TPrimitiveType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TYsonTypePtr AsPtr() const noexcept {
+ return const_cast<TYsonType*>(this);
+ }
+
+ private:
+ explicit TYsonType();
+
+ public:
+ static TYsonTypePtr Instance();
+ static const TYsonType* InstanceRaw();
+
+ protected:
+ const TYsonType* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+ };
+
+ /// A string with valid UUID.
+ class TUuidType final: public TPrimitiveType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TUuidTypePtr AsPtr() const noexcept {
+ return const_cast<TUuidType*>(this);
+ }
+
+ private:
+ explicit TUuidType();
+
+ public:
+ static TUuidTypePtr Instance();
+ static const TUuidType* InstanceRaw();
+
+ protected:
+ const TUuidType* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+ };
+
+ /// Object which can store a value or a singular `NULL` value.
+ ///
+ /// This type is used to encode a value or its absence.
+ class TOptionalType final: public TType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TOptionalTypePtr AsPtr() const noexcept {
+ return const_cast<TOptionalType*>(this);
+ }
+
+ private:
+ explicit TOptionalType(TMaybe<ui64> hash, const TType* item) noexcept;
+ static TOptionalTypePtr Create(ITypeFactory& factory, TTypePtr item);
+ static const TOptionalType* CreateRaw(ITypeFactory& factory, const TType* item);
+
+ protected:
+ ui64 CalculateHash() const noexcept;
+
+ public:
+ /// Get underlying type.
+ TTypePtr GetItemType() const noexcept {
+ return GetItemTypeRaw()->AsPtr();
+ }
+
+ /// Like `GetMemberType`, but returns a raw pointer.
+ const TType* GetItemTypeRaw() const noexcept {
+ return Item_;
+ }
+
+ protected:
+ const TOptionalType* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+
+ private:
+ const TType* Item_;
+ };
+
+ /// A variable-size collection of homogeneous values.
+ class TListType final: public TType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TListTypePtr AsPtr() const noexcept {
+ return const_cast<TListType*>(this);
+ }
+
+ private:
+ explicit TListType(TMaybe<ui64> hash, const TType* item) noexcept;
+ static TListTypePtr Create(ITypeFactory& factory, TTypePtr item);
+ static const TListType* CreateRaw(ITypeFactory& factory, const TType* item);
+
+ protected:
+ ui64 CalculateHash() const noexcept;
+
+ public:
+ /// Get underlying type.
+ TTypePtr GetItemType() const noexcept {
+ return GetItemTypeRaw()->AsPtr();
+ }
+
+ /// Like `GetMemberType`, but returns a raw pointer.
+ const TType* GetItemTypeRaw() const noexcept {
+ return Item_;
+ }
+
+ protected:
+ const TListType* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+
+ private:
+ const TType* Item_;
+ };
+
+ /// An associative key-value container.
+ ///
+ /// Values of this type are usually represented as hashmaps.
+ class TDictType final: public TType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TDictTypePtr AsPtr() const noexcept {
+ return const_cast<TDictType*>(this);
+ }
+
+ private:
+ explicit TDictType(TMaybe<ui64> hash, const TType* key, const TType* value) noexcept;
+ static TDictTypePtr Create(ITypeFactory& factory, TTypePtr key, TTypePtr value);
+ static const TDictType* CreateRaw(ITypeFactory& factory, const TType* key, const TType* value);
+
+ protected:
+ ui64 CalculateHash() const noexcept;
+
+ public:
+ /// Get the key type.
+ TTypePtr GetKeyType() const noexcept {
+ return GetKeyTypeRaw()->AsPtr();
+ }
+
+ /// Like `GetKeyType`, but returns a raw pointer.
+ const TType* GetKeyTypeRaw() const noexcept {
+ return Key_;
+ }
+
+ /// Get the value type.
+ TTypePtr GetValueType() const noexcept {
+ return GetValueTypeRaw()->AsPtr();
+ }
+
+ /// Like `GetValueType`, but returns a raw pointer.
+ const TType* GetValueTypeRaw() const noexcept {
+ return Value_;
+ }
+
+ protected:
+ const TDictType* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+
+ private:
+ const TType* Key_;
+ const TType* Value_;
+ };
+
+ /// A fixed-size collection of named heterogeneous values.
+ ///
+ /// Structs represent multiple values grouped into a single entity. Values stored in a struct are called items.
+ /// Each item have an associated type and a name.
+ ///
+ /// Struct type is represented by an array of item types and their names.
+ ///
+ /// Even though struct elements are accessed by their names, we use vector to represent struct type because order
+ /// of struct items matters. If affects memory layout of a struct, how struct is serialized and deserialized.
+ /// Items order is vital for struct versioning. New fields should always be added to the end of the struct.
+ /// This way older parsers can read values serialized by newer writers: they'll simply read known head of a struct
+ /// and skip tail that contains unknown fields.
+ ///
+ ///
+ /// # Struct names
+ ///
+ /// Each struct defined by YDL must have an associated name. This name is used to refer struct in code, to generate
+ /// code representing this struct in other programming languages, and to report errors. The struct's name is saved
+ /// in this field.
+ ///
+ /// Note that, even though YDL requires struct names, name field is optional because other systems might
+ /// use anonymous structs (or maybe they dont't use struct names at all).
+ ///
+ /// Note also that type aliases (especially `newtype`) use tags to name types, so primitives don't have name field.
+ class TStructType final: public TType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+ friend class TStructBuilderRaw;
+
+ public:
+ TStructTypePtr AsPtr() const noexcept {
+ return const_cast<TStructType*>(this);
+ }
+
+ public:
+ /// A single struct element.
+ class TMember {
+ public:
+ TMember(TStringBuf name, const TType* type);
+
+ public:
+ /// Get name of this item.
+ TStringBuf GetName() const {
+ return Name_;
+ }
+
+ /// Get type of this item.
+ TTypePtr GetType() const {
+ return GetTypeRaw()->AsPtr();
+ }
+
+ /// Like `GetType`, but returns a raw pointer.
+ const TType* GetTypeRaw() const {
+ return Type_;
+ }
+
+ /// Calculate this item's hash. Hashes follow the 'strict equivalence' (see type_equivalence.h).
+ ui64 Hash() const;
+
+ private:
+ TStringBuf Name_;
+ const TType* Type_;
+ };
+
+ using TMembers = TConstArrayRef<TMember>;
+
+ /// Like `TMember`, but owns its contents. Used in non-raw constructors to guarantee data validity.
+ class TOwnedMember {
+ public:
+ TOwnedMember(TString name, TTypePtr type);
+
+ public:
+ operator TMember() const&;
+
+ private:
+ TString Name_;
+ TTypePtr Type_;
+ };
+
+ using TOwnedMembers = TConstArrayRef<TOwnedMember>;
+
+ private:
+ explicit TStructType(TMaybe<ui64> hash, TMaybe<TStringBuf> name, TMembers members, TConstArrayRef<size_t> sortedMembers) noexcept;
+ static TStructTypePtr Create(ITypeFactory& factory, TOwnedMembers members);
+ static TStructTypePtr Create(ITypeFactory& factory, TMaybe<TStringBuf> name, TOwnedMembers members);
+ static const TStructType* CreateRaw(ITypeFactory& factory, TMembers members);
+ static const TStructType* CreateRaw(ITypeFactory& factory, TMaybe<TStringBuf> name, TMembers members);
+ static void MakeSortedMembers(TMembers members, TArrayRef<size_t> sortedItems);
+
+ protected:
+ ui64 CalculateHash() const noexcept;
+
+ public:
+ /// Get name of this struct.
+ TMaybe<TStringBuf> GetName() const noexcept {
+ return Name_;
+ }
+
+ /// Get description of structure members.
+ TMembers GetMembers() const noexcept {
+ return Members_;
+ }
+
+ /// Check if there is an item with the given name in this struct.
+ bool HasMember(TStringBuf name) const noexcept;
+
+ /// Lookup struct item by name. Throw an error if there is no such item.
+ const TMember& GetMember(TStringBuf name) const;
+
+ /// Lookup struct item by name, return its index or `-1`, if item was not found.
+ /// This function works in `O(log(n))` time, where `n` is number of struct members.
+ ssize_t GetMemberIndex(TStringBuf name) const noexcept;
+
+ protected:
+ const TStructType* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+
+ private:
+ TMaybe<TStringBuf> Name_;
+ TMembers Members_;
+ TConstArrayRef<size_t> SortedMembers_;
+ };
+
+ /// A fixed-size collection of named heterogeneous values.
+ ///
+ /// Tuples, like structs, represent multiple values, also called items, grouped into a single entity. Unlike
+ /// structs, though, tuple items are unnamed. Instead of names, they are accessed by their indexes.
+ ///
+ /// For a particular tuple type, number of items, their order and types are fixed, they should be known before
+ /// creating instances of tuples and can't change over time.
+ ///
+ ///
+ /// # Tuple names
+ ///
+ /// YDL requires each tuple definition to have a name (see `TStructType`). The name might not be mandatory
+ /// in other systems, so name field is optional.
+ class TTupleType: public TType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+ friend class TTupleBuilderRaw;
+
+ public:
+ TTupleTypePtr AsPtr() const noexcept {
+ return const_cast<TTupleType*>(this);
+ }
+
+ public:
+ /// A single tuple element.
+ class TElement {
+ public:
+ TElement(const TType* type);
+
+ public:
+ /// Get type of this item.
+ TTypePtr GetType() const {
+ return GetTypeRaw()->AsPtr();
+ }
+
+ /// Like `GetType`, but returns a raw pointer.
+ const TType* GetTypeRaw() const {
+ return Type_;
+ }
+
+ /// Calculate this item's hash. Hashes follow the 'strict equivalence' (see type_equivalence.h).
+ ui64 Hash() const;
+
+ private:
+ const TType* Type_;
+ };
+
+ using TElements = TConstArrayRef<TElement>;
+
+ /// Like `TElement`, but owns its contents. Used in non-raw constructors to guarantee data validity.
+ class TOwnedElement {
+ public:
+ TOwnedElement(TTypePtr type);
+
+ public:
+ operator TElement() const&;
+
+ private:
+ TTypePtr Type_;
+ };
+
+ using TOwnedElements = TConstArrayRef<TOwnedElement>;
+
+ private:
+ explicit TTupleType(TMaybe<ui64> hash, TMaybe<TStringBuf> name, TElements items) noexcept;
+ static TTupleTypePtr Create(ITypeFactory& factory, TOwnedElements items);
+ static TTupleTypePtr Create(ITypeFactory& factory, TMaybe<TStringBuf> name, TOwnedElements items);
+ static const TTupleType* CreateRaw(ITypeFactory& factory, TElements items);
+ static const TTupleType* CreateRaw(ITypeFactory& factory, TMaybe<TStringBuf> name, TElements items);
+
+ protected:
+ ui64 CalculateHash() const noexcept;
+
+ public:
+ /// Get name of this type.
+ TMaybe<TStringBuf> GetName() const noexcept {
+ return Name_;
+ }
+
+ /// Get description of tuple items.
+ TElements GetElements() const noexcept {
+ return Elements_;
+ }
+
+ protected:
+ const TTupleType* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+
+ private:
+ TMaybe<TStringBuf> Name_;
+ TElements Elements_;
+ };
+
+ /// A tagged union with named or unnamed alternatives (a.k.a. variant over struct or variant over tuple).
+ ///
+ /// Variants are used to store values of different types.
+ ///
+ /// For example, a variant over struct which holds an ip address could look like
+ /// `Ip = Variant<v4: IpV4, v6: IpV6>`, where `IpV4` and `IpV6` are some other types. Now, a value of type `Ip`
+ /// could store either a value of type `IpV4` or a value of type `IpV6`.
+ ///
+ /// Even though item types can be the same, each item represents a distinct state of a variant. For example,
+ /// a variant for a user identifier can look like `Uid = Variant<yuid: String, ip: String>`. This variant can
+ /// contain either user's yandexuid of user's ip. Despite both items are of the same type `String`, `Uid` which
+ /// contains a `yuid` and `Uid` which contains an `ip` are never equal.
+ /// That is, `Uid.yuid("000000") != Uid.ip("000000")`.
+ ///
+ /// Exactly like with structs or tuples, order of variant items matter. Indexes of variant items are used
+ /// instead of names when variant is serialized.
+ ///
+ ///
+ /// # Variant names
+ ///
+ /// YDL requires each variant definition to have a name (see `TStructType`). The name might not be mandatory
+ /// in other systems, so name field is optional.
+ class TVariantType final: public TType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+ friend class TStructBuilderRaw;
+ friend class TTupleBuilderRaw;
+
+ public:
+ TVariantTypePtr AsPtr() const noexcept {
+ return const_cast<TVariantType*>(this);
+ }
+
+ private:
+ explicit TVariantType(TMaybe<ui64> hash, TMaybe<TStringBuf> name, const TType* inner) noexcept;
+ static TVariantTypePtr Create(ITypeFactory& factory, TTypePtr inner);
+ static TVariantTypePtr Create(ITypeFactory& factory, TMaybe<TStringBuf> name, TTypePtr inner);
+ static const TVariantType* CreateRaw(ITypeFactory& factory, const TType* inner);
+ static const TVariantType* CreateRaw(ITypeFactory& factory, TMaybe<TStringBuf> name, const TType* inner);
+
+ protected:
+ ui64 CalculateHash() const noexcept;
+
+ public:
+ /// Get name of this variant.
+ TMaybe<TStringBuf> GetName() const noexcept {
+ return Name_;
+ }
+
+ /// Get vector of variant items.
+ TTypePtr GetUnderlyingType() const noexcept {
+ return Underlying_->AsPtr();
+ }
+
+ /// Like `GetUnderlyingType`, but returns a raw pointer.
+ const TType* GetUnderlyingTypeRaw() const noexcept {
+ return Underlying_;
+ }
+
+ /// Check if this variant's inner type is a struct.
+ bool IsVariantOverStruct() const noexcept {
+ return GetUnderlyingTypeRaw()->GetTypeName() == ETypeName::Struct;
+ }
+
+ /// Check if this variant's inner type is a tuple.
+ bool IsVariantOverTuple() const noexcept {
+ return GetUnderlyingTypeRaw()->GetTypeName() == ETypeName::Tuple;
+ }
+
+ /// Visit inner type of this variant. This function works like `Visit`, but only casts inner type
+ /// to struct or tuple, so you don't need to handle other types in a visitor.
+ template <typename V>
+ inline decltype(auto) VisitUnderlying(V&& visitor) const;
+
+ /// Like `VisitUnderlying`, but passes const raw pointers to the visitor.
+ template <typename V>
+ inline decltype(auto) VisitUnderlyingRaw(V&& visitor) const;
+
+ protected:
+ const TVariantType* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+
+ private:
+ TMaybe<TStringBuf> Name_;
+ const TType* Underlying_;
+ };
+
+ /// Named or tagged or user-defined type.
+ ///
+ /// Tags are used to create new types from existing ones by assigning them a tag, i.e. a name. They wrap other types
+ /// adding them additional semantics.
+ ///
+ /// On physical level, tags do not change types. Both `Tagged<Int32, 'GeoId'>` and `Int32` have exactly the same
+ /// representation when serialized.
+ ///
+ /// On logical level, tags change semantics of a type. This can affect how types are displayed, how they
+ /// are checked and converted, etc.
+ class TTaggedType final: public TType {
+ friend class TType;
+ friend class ITypeFactoryInternal;
+ friend class ITypeFactory;
+ friend class IPoolTypeFactory;
+
+ public:
+ TTaggedTypePtr AsPtr() const noexcept {
+ return const_cast<TTaggedType*>(this);
+ }
+
+ private:
+ explicit TTaggedType(TMaybe<ui64> hash, const TType* type, TStringBuf tag) noexcept;
+ static TTaggedTypePtr Create(ITypeFactory& factory, TTypePtr type, TStringBuf tag);
+ static const TTaggedType* CreateRaw(ITypeFactory& factory, const TType* type, TStringBuf tag);
+
+ protected:
+ ui64 CalculateHash() const noexcept;
+
+ public:
+ /// Get tag content, i.e. name of the new type.
+ TStringBuf GetTag() const noexcept {
+ return Tag_;
+ }
+
+ /// Get the wrapped type.
+ TTypePtr GetItemType() const noexcept {
+ return GetItemTypeRaw()->AsPtr();
+ }
+
+ /// Like `GetMemberType`, but returns a raw pointer.
+ const TType* GetItemTypeRaw() const noexcept {
+ return Item_;
+ }
+
+ protected:
+ const TTaggedType* Clone(ITypeFactoryInternal& factory) const noexcept;
+ void Drop(ITypeFactoryInternal& factory) noexcept;
+
+ private:
+ const TType* Item_;
+ TStringBuf Tag_;
+ };
+
+#ifdef __JETBRAINS_IDE__
+#pragma clang diagnostic push
+#pragma ide diagnostic ignored "cppcoreguidelines-pro-type-static-cast-downcast"
+#endif
+
+ bool TType::IsPrimitive() const noexcept {
+ return ::NTi::IsPrimitive(TypeName_);
+ }
+
+ TPrimitiveTypePtr TType::AsPrimitive() const noexcept {
+ return AsPrimitiveRaw()->AsPtr();
+ }
+
+ const TPrimitiveType* TType::AsPrimitiveRaw() const noexcept {
+ Y_VERIFY(IsPrimitive());
+ return static_cast<const TPrimitiveType*>(this);
+ }
+
+ bool TType::IsVoid() const noexcept {
+ return TypeName_ == ETypeName::Void;
+ }
+
+ TVoidTypePtr TType::AsVoid() const noexcept {
+ return AsVoidRaw()->AsPtr();
+ }
+
+ const TVoidType* TType::AsVoidRaw() const noexcept {
+ Y_VERIFY(IsVoid());
+ return static_cast<const TVoidType*>(this);
+ }
+
+ bool TType::IsNull() const noexcept {
+ return TypeName_ == ETypeName::Null;
+ }
+
+ TNullTypePtr TType::AsNull() const noexcept {
+ return AsNullRaw()->AsPtr();
+ }
+
+ const TNullType* TType::AsNullRaw() const noexcept {
+ Y_VERIFY(IsNull());
+ return static_cast<const TNullType*>(this);
+ }
+
+ bool TType::IsBool() const noexcept {
+ return TypeName_ == ETypeName::Bool;
+ }
+
+ TBoolTypePtr TType::AsBool() const noexcept {
+ return AsBoolRaw()->AsPtr();
+ }
+
+ const TBoolType* TType::AsBoolRaw() const noexcept {
+ Y_VERIFY(IsBool());
+ return static_cast<const TBoolType*>(this);
+ }
+
+ bool TType::IsInt8() const noexcept {
+ return TypeName_ == ETypeName::Int8;
+ }
+
+ TInt8TypePtr TType::AsInt8() const noexcept {
+ return AsInt8Raw()->AsPtr();
+ }
+
+ const TInt8Type* TType::AsInt8Raw() const noexcept {
+ Y_VERIFY(IsInt8());
+ return static_cast<const TInt8Type*>(this);
+ }
+
+ bool TType::IsInt16() const noexcept {
+ return TypeName_ == ETypeName::Int16;
+ }
+
+ TInt16TypePtr TType::AsInt16() const noexcept {
+ return AsInt16Raw()->AsPtr();
+ }
+
+ const TInt16Type* TType::AsInt16Raw() const noexcept {
+ Y_VERIFY(IsInt16());
+ return static_cast<const TInt16Type*>(this);
+ }
+
+ bool TType::IsInt32() const noexcept {
+ return TypeName_ == ETypeName::Int32;
+ }
+
+ TInt32TypePtr TType::AsInt32() const noexcept {
+ return AsInt32Raw()->AsPtr();
+ }
+
+ const TInt32Type* TType::AsInt32Raw() const noexcept {
+ Y_VERIFY(IsInt32());
+ return static_cast<const TInt32Type*>(this);
+ }
+
+ bool TType::IsInt64() const noexcept {
+ return TypeName_ == ETypeName::Int64;
+ }
+
+ TInt64TypePtr TType::AsInt64() const noexcept {
+ return AsInt64Raw()->AsPtr();
+ }
+
+ const TInt64Type* TType::AsInt64Raw() const noexcept {
+ Y_VERIFY(IsInt64());
+ return static_cast<const TInt64Type*>(this);
+ }
+
+ bool TType::IsUint8() const noexcept {
+ return TypeName_ == ETypeName::Uint8;
+ }
+
+ TUint8TypePtr TType::AsUint8() const noexcept {
+ return AsUint8Raw()->AsPtr();
+ }
+
+ const TUint8Type* TType::AsUint8Raw() const noexcept {
+ Y_VERIFY(IsUint8());
+ return static_cast<const TUint8Type*>(this);
+ }
+
+ bool TType::IsUint16() const noexcept {
+ return TypeName_ == ETypeName::Uint16;
+ }
+
+ TUint16TypePtr TType::AsUint16() const noexcept {
+ return AsUint16Raw()->AsPtr();
+ }
+
+ const TUint16Type* TType::AsUint16Raw() const noexcept {
+ Y_VERIFY(IsUint16());
+ return static_cast<const TUint16Type*>(this);
+ }
+
+ bool TType::IsUint32() const noexcept {
+ return TypeName_ == ETypeName::Uint32;
+ }
+
+ TUint32TypePtr TType::AsUint32() const noexcept {
+ return AsUint32Raw()->AsPtr();
+ }
+
+ const TUint32Type* TType::AsUint32Raw() const noexcept {
+ Y_VERIFY(IsUint32());
+ return static_cast<const TUint32Type*>(this);
+ }
+
+ bool TType::IsUint64() const noexcept {
+ return TypeName_ == ETypeName::Uint64;
+ }
+
+ TUint64TypePtr TType::AsUint64() const noexcept {
+ return AsUint64Raw()->AsPtr();
+ }
+
+ const TUint64Type* TType::AsUint64Raw() const noexcept {
+ Y_VERIFY(IsUint64());
+ return static_cast<const TUint64Type*>(this);
+ }
+
+ bool TType::IsFloat() const noexcept {
+ return TypeName_ == ETypeName::Float;
+ }
+
+ TFloatTypePtr TType::AsFloat() const noexcept {
+ return AsFloatRaw()->AsPtr();
+ }
+
+ const TFloatType* TType::AsFloatRaw() const noexcept {
+ Y_VERIFY(IsFloat());
+ return static_cast<const TFloatType*>(this);
+ }
+
+ bool TType::IsDouble() const noexcept {
+ return TypeName_ == ETypeName::Double;
+ }
+
+ TDoubleTypePtr TType::AsDouble() const noexcept {
+ return AsDoubleRaw()->AsPtr();
+ }
+
+ const TDoubleType* TType::AsDoubleRaw() const noexcept {
+ Y_VERIFY(IsDouble());
+ return static_cast<const TDoubleType*>(this);
+ }
+
+ bool TType::IsString() const noexcept {
+ return TypeName_ == ETypeName::String;
+ }
+
+ TStringTypePtr TType::AsString() const noexcept {
+ return AsStringRaw()->AsPtr();
+ }
+
+ const TStringType* TType::AsStringRaw() const noexcept {
+ Y_VERIFY(IsString());
+ return static_cast<const TStringType*>(this);
+ }
+
+ bool TType::IsUtf8() const noexcept {
+ return TypeName_ == ETypeName::Utf8;
+ }
+
+ TUtf8TypePtr TType::AsUtf8() const noexcept {
+ return AsUtf8Raw()->AsPtr();
+ }
+
+ const TUtf8Type* TType::AsUtf8Raw() const noexcept {
+ Y_VERIFY(IsUtf8());
+ return static_cast<const TUtf8Type*>(this);
+ }
+
+ bool TType::IsDate() const noexcept {
+ return TypeName_ == ETypeName::Date;
+ }
+
+ TDateTypePtr TType::AsDate() const noexcept {
+ return AsDateRaw()->AsPtr();
+ }
+
+ const TDateType* TType::AsDateRaw() const noexcept {
+ Y_VERIFY(IsDate());
+ return static_cast<const TDateType*>(this);
+ }
+
+ bool TType::IsDatetime() const noexcept {
+ return TypeName_ == ETypeName::Datetime;
+ }
+
+ TDatetimeTypePtr TType::AsDatetime() const noexcept {
+ return AsDatetimeRaw()->AsPtr();
+ }
+
+ const TDatetimeType* TType::AsDatetimeRaw() const noexcept {
+ Y_VERIFY(IsDatetime());
+ return static_cast<const TDatetimeType*>(this);
+ }
+
+ bool TType::IsTimestamp() const noexcept {
+ return TypeName_ == ETypeName::Timestamp;
+ }
+
+ TTimestampTypePtr TType::AsTimestamp() const noexcept {
+ return AsTimestampRaw()->AsPtr();
+ }
+
+ const TTimestampType* TType::AsTimestampRaw() const noexcept {
+ Y_VERIFY(IsTimestamp());
+ return static_cast<const TTimestampType*>(this);
+ }
+
+ bool TType::IsTzDate() const noexcept {
+ return TypeName_ == ETypeName::TzDate;
+ }
+
+ TTzDateTypePtr TType::AsTzDate() const noexcept {
+ return AsTzDateRaw()->AsPtr();
+ }
+
+ const TTzDateType* TType::AsTzDateRaw() const noexcept {
+ Y_VERIFY(IsTzDate());
+ return static_cast<const TTzDateType*>(this);
+ }
+
+ bool TType::IsTzDatetime() const noexcept {
+ return TypeName_ == ETypeName::TzDatetime;
+ }
+
+ TTzDatetimeTypePtr TType::AsTzDatetime() const noexcept {
+ return AsTzDatetimeRaw()->AsPtr();
+ }
+
+ const TTzDatetimeType* TType::AsTzDatetimeRaw() const noexcept {
+ Y_VERIFY(IsTzDatetime());
+ return static_cast<const TTzDatetimeType*>(this);
+ }
+
+ bool TType::IsTzTimestamp() const noexcept {
+ return TypeName_ == ETypeName::TzTimestamp;
+ }
+
+ TTzTimestampTypePtr TType::AsTzTimestamp() const noexcept {
+ return AsTzTimestampRaw()->AsPtr();
+ }
+
+ const TTzTimestampType* TType::AsTzTimestampRaw() const noexcept {
+ Y_VERIFY(IsTzTimestamp());
+ return static_cast<const TTzTimestampType*>(this);
+ }
+
+ bool TType::IsInterval() const noexcept {
+ return TypeName_ == ETypeName::Interval;
+ }
+
+ TIntervalTypePtr TType::AsInterval() const noexcept {
+ return AsIntervalRaw()->AsPtr();
+ }
+
+ const TIntervalType* TType::AsIntervalRaw() const noexcept {
+ Y_VERIFY(IsInterval());
+ return static_cast<const TIntervalType*>(this);
+ }
+
+ bool TType::IsDecimal() const noexcept {
+ return TypeName_ == ETypeName::Decimal;
+ }
+
+ TDecimalTypePtr TType::AsDecimal() const noexcept {
+ return AsDecimalRaw()->AsPtr();
+ }
+
+ const TDecimalType* TType::AsDecimalRaw() const noexcept {
+ Y_VERIFY(IsDecimal());
+ return static_cast<const TDecimalType*>(this);
+ }
+
+ bool TType::IsJson() const noexcept {
+ return TypeName_ == ETypeName::Json;
+ }
+
+ TJsonTypePtr TType::AsJson() const noexcept {
+ return AsJsonRaw()->AsPtr();
+ }
+
+ const TJsonType* TType::AsJsonRaw() const noexcept {
+ Y_VERIFY(IsJson());
+ return static_cast<const TJsonType*>(this);
+ }
+
+ bool TType::IsYson() const noexcept {
+ return TypeName_ == ETypeName::Yson;
+ }
+
+ TYsonTypePtr TType::AsYson() const noexcept {
+ return AsYsonRaw()->AsPtr();
+ }
+
+ const TYsonType* TType::AsYsonRaw() const noexcept {
+ Y_VERIFY(IsYson());
+ return static_cast<const TYsonType*>(this);
+ }
+
+ bool TType::IsUuid() const noexcept {
+ return TypeName_ == ETypeName::Uuid;
+ }
+
+ TUuidTypePtr TType::AsUuid() const noexcept {
+ return AsUuidRaw()->AsPtr();
+ }
+
+ const TUuidType* TType::AsUuidRaw() const noexcept {
+ Y_VERIFY(IsUuid());
+ return static_cast<const TUuidType*>(this);
+ }
+
+ bool TType::IsOptional() const noexcept {
+ return TypeName_ == ETypeName::Optional;
+ }
+
+ TOptionalTypePtr TType::AsOptional() const noexcept {
+ return AsOptionalRaw()->AsPtr();
+ }
+
+ const TOptionalType* TType::AsOptionalRaw() const noexcept {
+ Y_VERIFY(IsOptional());
+ return static_cast<const TOptionalType*>(this);
+ }
+
+ bool TType::IsList() const noexcept {
+ return TypeName_ == ETypeName::List;
+ }
+
+ TListTypePtr TType::AsList() const noexcept {
+ return AsListRaw()->AsPtr();
+ }
+
+ const TListType* TType::AsListRaw() const noexcept {
+ Y_VERIFY(IsList());
+ return static_cast<const TListType*>(this);
+ }
+
+ bool TType::IsDict() const noexcept {
+ return TypeName_ == ETypeName::Dict;
+ }
+
+ TDictTypePtr TType::AsDict() const noexcept {
+ return AsDictRaw()->AsPtr();
+ }
+
+ const TDictType* TType::AsDictRaw() const noexcept {
+ Y_VERIFY(IsDict());
+ return static_cast<const TDictType*>(this);
+ }
+
+ bool TType::IsStruct() const noexcept {
+ return TypeName_ == ETypeName::Struct;
+ }
+
+ TStructTypePtr TType::AsStruct() const noexcept {
+ return AsStructRaw()->AsPtr();
+ }
+
+ const TStructType* TType::AsStructRaw() const noexcept {
+ Y_VERIFY(IsStruct());
+ return static_cast<const TStructType*>(this);
+ }
+
+ bool TType::IsTuple() const noexcept {
+ return TypeName_ == ETypeName::Tuple;
+ }
+
+ TTupleTypePtr TType::AsTuple() const noexcept {
+ return AsTupleRaw()->AsPtr();
+ }
+
+ const TTupleType* TType::AsTupleRaw() const noexcept {
+ Y_VERIFY(IsTuple());
+ return static_cast<const TTupleType*>(this);
+ }
+
+ bool TType::IsVariant() const noexcept {
+ return TypeName_ == ETypeName::Variant;
+ }
+
+ TVariantTypePtr TType::AsVariant() const noexcept {
+ return AsVariantRaw()->AsPtr();
+ }
+
+ const TVariantType* TType::AsVariantRaw() const noexcept {
+ Y_VERIFY(IsVariant());
+ return static_cast<const TVariantType*>(this);
+ }
+
+ bool TType::IsTagged() const noexcept {
+ return TypeName_ == ETypeName::Tagged;
+ }
+
+ TTaggedTypePtr TType::AsTagged() const noexcept {
+ return AsTaggedRaw()->AsPtr();
+ }
+
+ const TTaggedType* TType::AsTaggedRaw() const noexcept {
+ Y_VERIFY(IsTagged());
+ return static_cast<const TTaggedType*>(this);
+ }
+
+#ifdef __JETBRAINS_IDE__
+#pragma clang diagnostic pop
+#endif
+
+ template <typename V>
+ decltype(auto) TType::Visit(V&& visitor) const {
+ switch (TypeName_) {
+ case ETypeName::Bool:
+ return std::forward<V>(visitor)(this->AsBool());
+ case ETypeName::Int8:
+ return std::forward<V>(visitor)(this->AsInt8());
+ case ETypeName::Int16:
+ return std::forward<V>(visitor)(this->AsInt16());
+ case ETypeName::Int32:
+ return std::forward<V>(visitor)(this->AsInt32());
+ case ETypeName::Int64:
+ return std::forward<V>(visitor)(this->AsInt64());
+ case ETypeName::Uint8:
+ return std::forward<V>(visitor)(this->AsUint8());
+ case ETypeName::Uint16:
+ return std::forward<V>(visitor)(this->AsUint16());
+ case ETypeName::Uint32:
+ return std::forward<V>(visitor)(this->AsUint32());
+ case ETypeName::Uint64:
+ return std::forward<V>(visitor)(this->AsUint64());
+ case ETypeName::Float:
+ return std::forward<V>(visitor)(this->AsFloat());
+ case ETypeName::Double:
+ return std::forward<V>(visitor)(this->AsDouble());
+ case ETypeName::String:
+ return std::forward<V>(visitor)(this->AsString());
+ case ETypeName::Utf8:
+ return std::forward<V>(visitor)(this->AsUtf8());
+ case ETypeName::Date:
+ return std::forward<V>(visitor)(this->AsDate());
+ case ETypeName::Datetime:
+ return std::forward<V>(visitor)(this->AsDatetime());
+ case ETypeName::Timestamp:
+ return std::forward<V>(visitor)(this->AsTimestamp());
+ case ETypeName::TzDate:
+ return std::forward<V>(visitor)(this->AsTzDate());
+ case ETypeName::TzDatetime:
+ return std::forward<V>(visitor)(this->AsTzDatetime());
+ case ETypeName::TzTimestamp:
+ return std::forward<V>(visitor)(this->AsTzTimestamp());
+ case ETypeName::Interval:
+ return std::forward<V>(visitor)(this->AsInterval());
+ case ETypeName::Decimal:
+ return std::forward<V>(visitor)(this->AsDecimal());
+ case ETypeName::Json:
+ return std::forward<V>(visitor)(this->AsJson());
+ case ETypeName::Yson:
+ return std::forward<V>(visitor)(this->AsYson());
+ case ETypeName::Uuid:
+ return std::forward<V>(visitor)(this->AsUuid());
+ case ETypeName::Void:
+ return std::forward<V>(visitor)(this->AsVoid());
+ case ETypeName::Null:
+ return std::forward<V>(visitor)(this->AsNull());
+ case ETypeName::Optional:
+ return std::forward<V>(visitor)(this->AsOptional());
+ case ETypeName::List:
+ return std::forward<V>(visitor)(this->AsList());
+ case ETypeName::Dict:
+ return std::forward<V>(visitor)(this->AsDict());
+ case ETypeName::Struct:
+ return std::forward<V>(visitor)(this->AsStruct());
+ case ETypeName::Tuple:
+ return std::forward<V>(visitor)(this->AsTuple());
+ case ETypeName::Variant:
+ return std::forward<V>(visitor)(this->AsVariant());
+ case ETypeName::Tagged:
+ return std::forward<V>(visitor)(this->AsTagged());
+ }
+
+ Y_UNREACHABLE();
+ }
+
+ template <typename V>
+ decltype(auto) TType::VisitRaw(V&& visitor) const {
+ switch (TypeName_) {
+ case ETypeName::Bool:
+ return std::forward<V>(visitor)(this->AsBoolRaw());
+ case ETypeName::Int8:
+ return std::forward<V>(visitor)(this->AsInt8Raw());
+ case ETypeName::Int16:
+ return std::forward<V>(visitor)(this->AsInt16Raw());
+ case ETypeName::Int32:
+ return std::forward<V>(visitor)(this->AsInt32Raw());
+ case ETypeName::Int64:
+ return std::forward<V>(visitor)(this->AsInt64Raw());
+ case ETypeName::Uint8:
+ return std::forward<V>(visitor)(this->AsUint8Raw());
+ case ETypeName::Uint16:
+ return std::forward<V>(visitor)(this->AsUint16Raw());
+ case ETypeName::Uint32:
+ return std::forward<V>(visitor)(this->AsUint32Raw());
+ case ETypeName::Uint64:
+ return std::forward<V>(visitor)(this->AsUint64Raw());
+ case ETypeName::Float:
+ return std::forward<V>(visitor)(this->AsFloatRaw());
+ case ETypeName::Double:
+ return std::forward<V>(visitor)(this->AsDoubleRaw());
+ case ETypeName::String:
+ return std::forward<V>(visitor)(this->AsStringRaw());
+ case ETypeName::Utf8:
+ return std::forward<V>(visitor)(this->AsUtf8Raw());
+ case ETypeName::Date:
+ return std::forward<V>(visitor)(this->AsDateRaw());
+ case ETypeName::Datetime:
+ return std::forward<V>(visitor)(this->AsDatetimeRaw());
+ case ETypeName::Timestamp:
+ return std::forward<V>(visitor)(this->AsTimestampRaw());
+ case ETypeName::TzDate:
+ return std::forward<V>(visitor)(this->AsTzDateRaw());
+ case ETypeName::TzDatetime:
+ return std::forward<V>(visitor)(this->AsTzDatetimeRaw());
+ case ETypeName::TzTimestamp:
+ return std::forward<V>(visitor)(this->AsTzTimestampRaw());
+ case ETypeName::Interval:
+ return std::forward<V>(visitor)(this->AsIntervalRaw());
+ case ETypeName::Decimal:
+ return std::forward<V>(visitor)(this->AsDecimalRaw());
+ case ETypeName::Json:
+ return std::forward<V>(visitor)(this->AsJsonRaw());
+ case ETypeName::Yson:
+ return std::forward<V>(visitor)(this->AsYsonRaw());
+ case ETypeName::Uuid:
+ return std::forward<V>(visitor)(this->AsUuidRaw());
+ case ETypeName::Void:
+ return std::forward<V>(visitor)(this->AsVoidRaw());
+ case ETypeName::Null:
+ return std::forward<V>(visitor)(this->AsNullRaw());
+ case ETypeName::Optional:
+ return std::forward<V>(visitor)(this->AsOptionalRaw());
+ case ETypeName::List:
+ return std::forward<V>(visitor)(this->AsListRaw());
+ case ETypeName::Dict:
+ return std::forward<V>(visitor)(this->AsDictRaw());
+ case ETypeName::Struct:
+ return std::forward<V>(visitor)(this->AsStructRaw());
+ case ETypeName::Tuple:
+ return std::forward<V>(visitor)(this->AsTupleRaw());
+ case ETypeName::Variant:
+ return std::forward<V>(visitor)(this->AsVariantRaw());
+ case ETypeName::Tagged:
+ return std::forward<V>(visitor)(this->AsTaggedRaw());
+ }
+
+ Y_UNREACHABLE();
+ }
+
+ template <typename V>
+ decltype(auto) TPrimitiveType::VisitPrimitive(V&& visitor) const {
+ switch (GetPrimitiveTypeName()) {
+ case EPrimitiveTypeName::Bool:
+ return std::forward<V>(visitor)(this->AsBool());
+ case EPrimitiveTypeName::Int8:
+ return std::forward<V>(visitor)(this->AsInt8());
+ case EPrimitiveTypeName::Int16:
+ return std::forward<V>(visitor)(this->AsInt16());
+ case EPrimitiveTypeName::Int32:
+ return std::forward<V>(visitor)(this->AsInt32());
+ case EPrimitiveTypeName::Int64:
+ return std::forward<V>(visitor)(this->AsInt64());
+ case EPrimitiveTypeName::Uint8:
+ return std::forward<V>(visitor)(this->AsUint8());
+ case EPrimitiveTypeName::Uint16:
+ return std::forward<V>(visitor)(this->AsUint16());
+ case EPrimitiveTypeName::Uint32:
+ return std::forward<V>(visitor)(this->AsUint32());
+ case EPrimitiveTypeName::Uint64:
+ return std::forward<V>(visitor)(this->AsUint64());
+ case EPrimitiveTypeName::Float:
+ return std::forward<V>(visitor)(this->AsFloat());
+ case EPrimitiveTypeName::Double:
+ return std::forward<V>(visitor)(this->AsDouble());
+ case EPrimitiveTypeName::String:
+ return std::forward<V>(visitor)(this->AsString());
+ case EPrimitiveTypeName::Utf8:
+ return std::forward<V>(visitor)(this->AsUtf8());
+ case EPrimitiveTypeName::Date:
+ return std::forward<V>(visitor)(this->AsDate());
+ case EPrimitiveTypeName::Datetime:
+ return std::forward<V>(visitor)(this->AsDatetime());
+ case EPrimitiveTypeName::Timestamp:
+ return std::forward<V>(visitor)(this->AsTimestamp());
+ case EPrimitiveTypeName::TzDate:
+ return std::forward<V>(visitor)(this->AsTzDate());
+ case EPrimitiveTypeName::TzDatetime:
+ return std::forward<V>(visitor)(this->AsTzDatetime());
+ case EPrimitiveTypeName::TzTimestamp:
+ return std::forward<V>(visitor)(this->AsTzTimestamp());
+ case EPrimitiveTypeName::Interval:
+ return std::forward<V>(visitor)(this->AsInterval());
+ case EPrimitiveTypeName::Decimal:
+ return std::forward<V>(visitor)(this->AsDecimal());
+ case EPrimitiveTypeName::Json:
+ return std::forward<V>(visitor)(this->AsJson());
+ case EPrimitiveTypeName::Yson:
+ return std::forward<V>(visitor)(this->AsYson());
+ case EPrimitiveTypeName::Uuid:
+ return std::forward<V>(visitor)(this->AsUuid());
+ }
+
+ Y_UNREACHABLE();
+ }
+
+ template <typename V>
+ decltype(auto) TPrimitiveType::VisitPrimitiveRaw(V&& visitor) const {
+ switch (GetPrimitiveTypeName()) {
+ case EPrimitiveTypeName::Bool:
+ return std::forward<V>(visitor)(this->AsBoolRaw());
+ case EPrimitiveTypeName::Int8:
+ return std::forward<V>(visitor)(this->AsInt8Raw());
+ case EPrimitiveTypeName::Int16:
+ return std::forward<V>(visitor)(this->AsInt16Raw());
+ case EPrimitiveTypeName::Int32:
+ return std::forward<V>(visitor)(this->AsInt32Raw());
+ case EPrimitiveTypeName::Int64:
+ return std::forward<V>(visitor)(this->AsInt64Raw());
+ case EPrimitiveTypeName::Uint8:
+ return std::forward<V>(visitor)(this->AsUint8Raw());
+ case EPrimitiveTypeName::Uint16:
+ return std::forward<V>(visitor)(this->AsUint16Raw());
+ case EPrimitiveTypeName::Uint32:
+ return std::forward<V>(visitor)(this->AsUint32Raw());
+ case EPrimitiveTypeName::Uint64:
+ return std::forward<V>(visitor)(this->AsUint64Raw());
+ case EPrimitiveTypeName::Float:
+ return std::forward<V>(visitor)(this->AsFloatRaw());
+ case EPrimitiveTypeName::Double:
+ return std::forward<V>(visitor)(this->AsDoubleRaw());
+ case EPrimitiveTypeName::String:
+ return std::forward<V>(visitor)(this->AsStringRaw());
+ case EPrimitiveTypeName::Utf8:
+ return std::forward<V>(visitor)(this->AsUtf8Raw());
+ case EPrimitiveTypeName::Date:
+ return std::forward<V>(visitor)(this->AsDateRaw());
+ case EPrimitiveTypeName::Datetime:
+ return std::forward<V>(visitor)(this->AsDatetimeRaw());
+ case EPrimitiveTypeName::Timestamp:
+ return std::forward<V>(visitor)(this->AsTimestampRaw());
+ case EPrimitiveTypeName::TzDate:
+ return std::forward<V>(visitor)(this->AsTzDateRaw());
+ case EPrimitiveTypeName::TzDatetime:
+ return std::forward<V>(visitor)(this->AsTzDatetimeRaw());
+ case EPrimitiveTypeName::TzTimestamp:
+ return std::forward<V>(visitor)(this->AsTzTimestampRaw());
+ case EPrimitiveTypeName::Interval:
+ return std::forward<V>(visitor)(this->AsIntervalRaw());
+ case EPrimitiveTypeName::Decimal:
+ return std::forward<V>(visitor)(this->AsDecimalRaw());
+ case EPrimitiveTypeName::Json:
+ return std::forward<V>(visitor)(this->AsJsonRaw());
+ case EPrimitiveTypeName::Yson:
+ return std::forward<V>(visitor)(this->AsYsonRaw());
+ case EPrimitiveTypeName::Uuid:
+ return std::forward<V>(visitor)(this->AsUuidRaw());
+ }
+
+ Y_UNREACHABLE();
+ }
+
+ template <typename V>
+ decltype(auto) TVariantType::VisitUnderlying(V&& visitor) const {
+ switch (GetUnderlyingTypeRaw()->GetTypeName()) {
+ case ETypeName::Struct:
+ return std::forward<V>(visitor)(this->GetUnderlyingTypeRaw()->AsStruct());
+ case ETypeName::Tuple:
+ return std::forward<V>(visitor)(this->GetUnderlyingTypeRaw()->AsTuple());
+ default:
+ Y_UNREACHABLE();
+ }
+ }
+
+ template <typename V>
+ decltype(auto) TVariantType::VisitUnderlyingRaw(V&& visitor) const {
+ switch (GetUnderlyingTypeRaw()->GetTypeName()) {
+ case ETypeName::Struct:
+ return std::forward<V>(visitor)(this->GetUnderlyingTypeRaw()->AsStructRaw());
+ case ETypeName::Tuple:
+ return std::forward<V>(visitor)(this->GetUnderlyingTypeRaw()->AsTupleRaw());
+ default:
+ Y_UNREACHABLE();
+ }
+ }
+} // namespace NTi
diff --git a/library/cpp/type_info/type_complexity.cpp b/library/cpp/type_info/type_complexity.cpp
new file mode 100644
index 0000000000..073c24b909
--- /dev/null
+++ b/library/cpp/type_info/type_complexity.cpp
@@ -0,0 +1,78 @@
+#include "type_complexity.h"
+
+#include "type.h"
+
+
+namespace NTi {
+
+int ComputeTypeComplexity(const TTypePtr& type)
+{
+ return ComputeTypeComplexity(type.Get());
+}
+
+int ComputeTypeComplexity(const TType* type)
+{
+ switch (type->GetTypeName()) {
+ case ETypeName::Bool:
+ case ETypeName::Int8:
+ case ETypeName::Int16:
+ case ETypeName::Int32:
+ case ETypeName::Int64:
+ case ETypeName::Uint8:
+ case ETypeName::Uint16:
+ case ETypeName::Uint32:
+ case ETypeName::Uint64:
+ case ETypeName::Float:
+ case ETypeName::Double:
+ case ETypeName::String:
+ case ETypeName::Utf8:
+ case ETypeName::Date:
+ case ETypeName::Datetime:
+ case ETypeName::Timestamp:
+ case ETypeName::TzDate:
+ case ETypeName::TzDatetime:
+ case ETypeName::TzTimestamp:
+ case ETypeName::Interval:
+ case ETypeName::Decimal:
+ case ETypeName::Json:
+ case ETypeName::Yson:
+ case ETypeName::Uuid:
+ case ETypeName::Void:
+ case ETypeName::Null:
+ return 1;
+
+ case ETypeName::Optional:
+ return 1 + ComputeTypeComplexity(type->AsOptionalRaw()->GetItemTypeRaw());
+
+ case ETypeName::List:
+ return 1 + ComputeTypeComplexity(type->AsListRaw()->GetItemTypeRaw());
+
+ case ETypeName::Dict:
+ return 1 + ComputeTypeComplexity(type->AsDictRaw()->GetKeyTypeRaw())
+ + ComputeTypeComplexity(type->AsDictRaw()->GetValueTypeRaw());
+ case ETypeName::Struct: {
+ int result = 1;
+ for (const auto& member : type->AsStructRaw()->GetMembers()) {
+ result += ComputeTypeComplexity(member.GetTypeRaw());
+ }
+ return result;
+ }
+ case ETypeName::Tuple: {
+ int result = 1;
+ for (const auto& element : type->AsTupleRaw()->GetElements()) {
+ result += ComputeTypeComplexity(element.GetTypeRaw());
+ }
+ return result;
+ }
+ case ETypeName::Variant: {
+ return ComputeTypeComplexity(type->AsVariantRaw()->GetUnderlyingTypeRaw());
+ }
+ case ETypeName::Tagged: {
+ return 1 + ComputeTypeComplexity(type->AsTaggedRaw()->GetItemType());
+ }
+ }
+ Y_FAIL("internal error: unreachable code");
+}
+
+} // namespace NTi
+
diff --git a/library/cpp/type_info/type_complexity.h b/library/cpp/type_info/type_complexity.h
new file mode 100644
index 0000000000..fcf367eb81
--- /dev/null
+++ b/library/cpp/type_info/type_complexity.h
@@ -0,0 +1,18 @@
+#pragma once
+#include "fwd.h"
+
+namespace NTi {
+
+ ///
+ /// Compute type complexity.
+ /// Roughly speaking type complexity is a number of nodes in the schema tree.
+ /// Examples:
+ /// - Type complexity of simple or singular type (i.e. Int64, String, Null, Decimal) is 1.
+ /// - Type complexity of `Optional<Int64>` is 2
+ /// - Type complexity of `Struct<a:Int64,b:Optional<String>` is 4 (1 for Int64, 2 for Optional<String> and 1 for struct)
+ ///
+ /// Systems might impose restrictions on the type complexity.
+ int ComputeTypeComplexity(const TTypePtr& type);
+ int ComputeTypeComplexity(const TType* type);
+
+} // namespace NTi
diff --git a/library/cpp/type_info/type_constructors.h b/library/cpp/type_info/type_constructors.h
new file mode 100644
index 0000000000..439d5a1887
--- /dev/null
+++ b/library/cpp/type_info/type_constructors.h
@@ -0,0 +1,110 @@
+#pragma once
+
+//! @file type_constructors.h
+
+#include "type.h"
+
+namespace NTi {
+ /// Create new `Null` type using the default heap factory.
+ TNullTypePtr Null();
+
+ /// Create new `Bool` type using the default heap factory.
+ TBoolTypePtr Bool();
+
+ /// Create new `Int8` type using the default heap factory.
+ TInt8TypePtr Int8();
+
+ /// Create new `Int16` type using the default heap factory.
+ TInt16TypePtr Int16();
+
+ /// Create new `Int32` type using the default heap factory.
+ TInt32TypePtr Int32();
+
+ /// Create new `Int64` type using the default heap factory.
+ TInt64TypePtr Int64();
+
+ /// Create new `Uint8` type using the default heap factory.
+ TUint8TypePtr Uint8();
+
+ /// Create new `Uint16` type using the default heap factory.
+ TUint16TypePtr Uint16();
+
+ /// Create new `Uint32` type using the default heap factory.
+ TUint32TypePtr Uint32();
+
+ /// Create new `Uint64` type using the default heap factory.
+ TUint64TypePtr Uint64();
+
+ /// Create new `Float` type using the default heap factory.
+ TFloatTypePtr Float();
+
+ /// Create new `Double` type using the default heap factory.
+ TDoubleTypePtr Double();
+
+ /// Create new `String` type using the default heap factory.
+ TStringTypePtr String();
+
+ /// Create new `Utf8` type using the default heap factory.
+ TUtf8TypePtr Utf8();
+
+ /// Create new `Date` type using the default heap factory.
+ TDateTypePtr Date();
+
+ /// Create new `Datetime` type using the default heap factory.
+ TDatetimeTypePtr Datetime();
+
+ /// Create new `Timestamp` type using the default heap factory.
+ TTimestampTypePtr Timestamp();
+
+ /// Create new `TzDate` type using the default heap factory.
+ TTzDateTypePtr TzDate();
+
+ /// Create new `TzDatetime` type using the default heap factory.
+ TTzDatetimeTypePtr TzDatetime();
+
+ /// Create new `TzTimestamp` type using the default heap factory.
+ TTzTimestampTypePtr TzTimestamp();
+
+ /// Create new `Interval` type using the default heap factory.
+ TIntervalTypePtr Interval();
+
+ /// Create new `Decimal` type using the default heap factory.
+ TDecimalTypePtr Decimal(ui8 precision, ui8 scale);
+
+ /// Create new `Json` type using the default heap factory.
+ TJsonTypePtr Json();
+
+ /// Create new `Yson` type using the default heap factory.
+ TYsonTypePtr Yson();
+
+ /// Create new `Uuid` type using the default heap factory.
+ TUuidTypePtr Uuid();
+
+ /// Create new `Optional` type using the default heap factory.
+ TOptionalTypePtr Optional(TTypePtr item);
+
+ /// Create new `List` type using the default heap factory.
+ TListTypePtr List(TTypePtr item);
+
+ /// Create new `Dict` type using the default heap factory.
+ TDictTypePtr Dict(TTypePtr key, TTypePtr value);
+
+ /// Create new `Struct` type using the default heap factory.
+ TStructTypePtr Struct(TStructType::TOwnedMembers items);
+ /// Create new `Struct` type using the default heap factory.
+ TStructTypePtr Struct(TMaybe<TStringBuf> name, TStructType::TOwnedMembers items);
+
+ /// Create new `Tuple` type using the default heap factory.
+ TTupleTypePtr Tuple(TTupleType::TOwnedElements items);
+ /// Create new `Tuple` type using the default heap factory.
+ TTupleTypePtr Tuple(TMaybe<TStringBuf> name, TTupleType::TOwnedElements items);
+
+ /// Create new `Variant` type using the default heap factory.
+ TVariantTypePtr Variant(TTypePtr underlying);
+ /// Create new `Variant` type using the default heap factory.
+ TVariantTypePtr Variant(TMaybe<TStringBuf> name, TTypePtr underlying);
+
+ /// Create new `Tagged` type using the default heap factory.
+ TTaggedTypePtr Tagged(TTypePtr type, TStringBuf tag);
+
+} // namespace NTi
diff --git a/library/cpp/type_info/type_equivalence.cpp b/library/cpp/type_info/type_equivalence.cpp
new file mode 100644
index 0000000000..9f22f0308c
--- /dev/null
+++ b/library/cpp/type_info/type_equivalence.cpp
@@ -0,0 +1,286 @@
+#include "type_equivalence.h"
+
+#include <util/generic/overloaded.h>
+
+#include "type.h"
+
+namespace NTi::NEq {
+ namespace {
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TType* lhs, const TType* rhs);
+
+ // template <bool IgnoreHash>
+ // bool StrictlyEqual(const TDocumentation& lhs, const TDocumentation& rhs) {
+ // ...
+ // }
+
+ // template <bool IgnoreHash>
+ // bool StrictlyEqual(const TAnnotations& lhs, const TAnnotations& rhs) {
+ // ...
+ // }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TVoidType&, const TVoidType&) {
+ return true;
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TNullType&, const TNullType&) {
+ return true;
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TBoolType&, const TBoolType&) {
+ return true;
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TInt8Type&, const TInt8Type&) {
+ return true;
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TInt16Type&, const TInt16Type&) {
+ return true;
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TInt32Type&, const TInt32Type&) {
+ return true;
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TInt64Type&, const TInt64Type&) {
+ return true;
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TUint8Type&, const TUint8Type&) {
+ return true;
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TUint16Type&, const TUint16Type&) {
+ return true;
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TUint32Type&, const TUint32Type&) {
+ return true;
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TUint64Type&, const TUint64Type&) {
+ return true;
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TFloatType&, const TFloatType&) {
+ return true;
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TDoubleType&, const TDoubleType&) {
+ return true;
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TStringType&, const TStringType&) {
+ return true;
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TUtf8Type&, const TUtf8Type&) {
+ return true;
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TDateType&, const TDateType&) {
+ return true;
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TDatetimeType&, const TDatetimeType&) {
+ return true;
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TTimestampType&, const TTimestampType&) {
+ return true;
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TTzDateType&, const TTzDateType&) {
+ return true;
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TTzDatetimeType&, const TTzDatetimeType&) {
+ return true;
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TTzTimestampType&, const TTzTimestampType&) {
+ return true;
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TIntervalType&, const TIntervalType&) {
+ return true;
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TDecimalType& lhs, const TDecimalType& rhs) {
+ return lhs.GetPrecision() == rhs.GetPrecision() && lhs.GetScale() == rhs.GetScale();
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TJsonType&, const TJsonType&) {
+ return true;
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TYsonType&, const TYsonType&) {
+ return true;
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TUuidType&, const TUuidType&) {
+ return true;
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TOptionalType& lhs, const TOptionalType& rhs) {
+ return StrictlyEqual<IgnoreHash>(lhs.GetItemTypeRaw(), rhs.GetItemTypeRaw());
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TListType& lhs, const TListType& rhs) {
+ return StrictlyEqual<IgnoreHash>(lhs.GetItemTypeRaw(), rhs.GetItemTypeRaw());
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TDictType& lhs, const TDictType& rhs) {
+ return StrictlyEqual<IgnoreHash>(lhs.GetKeyTypeRaw(), rhs.GetKeyTypeRaw()) &&
+ StrictlyEqual<IgnoreHash>(lhs.GetValueTypeRaw(), rhs.GetValueTypeRaw());
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TStructType& lhs, const TStructType& rhs) {
+ if (lhs.GetName() != rhs.GetName()) {
+ return false;
+ }
+
+ return std::equal(
+ lhs.GetMembers().begin(), lhs.GetMembers().end(),
+ rhs.GetMembers().begin(), rhs.GetMembers().end(),
+ [](const TStructType::TMember& lhs, const TStructType::TMember& rhs) -> bool {
+ return lhs.GetName() == rhs.GetName() &&
+ StrictlyEqual<IgnoreHash>(lhs.GetTypeRaw(), rhs.GetTypeRaw());
+ // && StrictlyEqual(lhs.GetAnnotations(), rhs.GetAnnotations())
+ // && StrictlyEqual(lhs.GetDocumentation(), rhs.GetDocumentation());
+ });
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TTupleType& lhs, const TTupleType& rhs) {
+ if (lhs.GetName() != rhs.GetName()) {
+ return false;
+ }
+
+ return std::equal(
+ lhs.GetElements().begin(), lhs.GetElements().end(),
+ rhs.GetElements().begin(), rhs.GetElements().end(),
+ [](const TTupleType::TElement& lhs, const TTupleType::TElement& rhs) -> bool {
+ return StrictlyEqual<IgnoreHash>(lhs.GetTypeRaw(), rhs.GetTypeRaw());
+ // && StrictlyEqual(lhs.GetAnnotations(), rhs.GetAnnotations())
+ // && StrictlyEqual(lhs.GetDocumentation(), rhs.GetDocumentation());
+ });
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TVariantType& lhs, const TVariantType& rhs) {
+ if (lhs.GetName() != rhs.GetName()) {
+ return false;
+ }
+
+ return StrictlyEqual<IgnoreHash>(lhs.GetUnderlyingTypeRaw(), rhs.GetUnderlyingTypeRaw());
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TTaggedType& lhs, const TTaggedType& rhs) {
+ return lhs.GetTag() == rhs.GetTag() &&
+ StrictlyEqual<IgnoreHash>(lhs.GetItemTypeRaw(), rhs.GetItemTypeRaw());
+ }
+
+ template <bool IgnoreHash>
+ bool StrictlyEqual(const TType* lhs, const TType* rhs) {
+ if (lhs == rhs) {
+ return true;
+ }
+
+ if (lhs == nullptr || rhs == nullptr) {
+ return false;
+ }
+
+ if (!IgnoreHash && lhs->GetHash() != rhs->GetHash()) {
+ return false;
+ }
+
+ if (lhs->GetTypeName() != rhs->GetTypeName()) {
+ return false;
+ }
+
+ // FIXME: update `TStrictlyEqual`'s docs to explicitly state that documentation and annotations
+ // must be the same for types to compare strictly equal.
+
+ // if (!StrictlyEqual(lhs->GetAnnotations(), rhs->GetAnnotations())) {
+ // return false;
+ // }
+
+ // if (!StrictlyEqual(lhs->GetDocumentation(), rhs->GetDocumentation())) {
+ // return false;
+ // }
+
+ return lhs->VisitRaw([&rhs](const auto* lhs) {
+ using TSameType = decltype(lhs);
+ return rhs->VisitRaw(TOverloaded{
+ [lhs](TSameType rhs) {
+ return StrictlyEqual<IgnoreHash>(*lhs, *rhs);
+ },
+ [](const auto* rhs) {
+ static_assert(!std::is_same_v<decltype(lhs), decltype(rhs)>);
+ return false;
+ }});
+ });
+ }
+ }
+
+ bool TStrictlyEqual::operator()(const TType* lhs, const TType* rhs) const {
+ return StrictlyEqual<false>(lhs, rhs);
+ }
+
+ bool TStrictlyEqual::operator()(TTypePtr lhs, TTypePtr rhs) const {
+ return operator()(lhs.Get(), rhs.Get());
+ }
+
+ bool TStrictlyEqual::IgnoreHash(const TType* lhs, const TType* rhs) const {
+ return StrictlyEqual<true>(lhs, rhs);
+ }
+
+ bool TStrictlyEqual::IgnoreHash(TTypePtr lhs, TTypePtr rhs) const {
+ return IgnoreHash(lhs.Get(), rhs.Get());
+ }
+
+ ui64 TStrictlyEqualHash::operator()(const TType* type) const {
+ if (type == nullptr) {
+ return 0;
+ }
+
+ return type->GetHash();
+ }
+
+ ui64 TStrictlyEqualHash::operator()(TTypePtr type) const {
+ return operator()(type.Get());
+ }
+}
diff --git a/library/cpp/type_info/type_equivalence.h b/library/cpp/type_info/type_equivalence.h
new file mode 100644
index 0000000000..70351dc689
--- /dev/null
+++ b/library/cpp/type_info/type_equivalence.h
@@ -0,0 +1,36 @@
+#pragma once
+
+//! @file type_equivalence.h
+//!
+//! Relations between types.
+//!
+//! Type info declares multiple ways to compare types. There's strict, nominal, structural and other equivalences,
+//! as well as subtyping. See corresponding functors for more info.
+//!
+//! At the moment, only strict equivalence is implemented because others are not yet standartized.
+
+#include <type_traits>
+
+#include "fwd.h"
+
+#include <util/system/types.h>
+
+namespace NTi::NEq {
+ /// Strict equivalence is the strongest form of type equivalence. If two types are strictly equal,
+ /// they're literally the same type for all intents and purposes. This includes struct, tuple, variant and enum
+ /// names, order of their items, names of their items, names of tagged types, and so on.
+ struct TStrictlyEqual {
+ bool operator()(const TType* lhs, const TType* rhs) const;
+ bool operator()(TTypePtr lhs, TTypePtr rhs) const;
+
+ /// Compare types without calculating and comparing their hashes first.
+ bool IgnoreHash(const TType* lhs, const TType* rhs) const;
+ bool IgnoreHash(TTypePtr lhs, TTypePtr rhs) const;
+ };
+
+ /// Hash that follows the strict equality rules (see `TStrictlyEqual`).
+ struct TStrictlyEqualHash {
+ ui64 operator()(const TType* type) const;
+ ui64 operator()(TTypePtr type) const;
+ };
+}
diff --git a/library/cpp/type_info/type_factory.cpp b/library/cpp/type_info/type_factory.cpp
new file mode 100644
index 0000000000..9d60307938
--- /dev/null
+++ b/library/cpp/type_info/type_factory.cpp
@@ -0,0 +1,495 @@
+#include "type_factory.h"
+
+#include "type.h"
+#include "type_equivalence.h"
+
+#include <util/memory/pool.h>
+#include <util/generic/hash_set.h>
+
+#include <cstdlib>
+
+namespace NTi {
+ TVoidTypePtr ITypeFactory::Void() {
+ return TVoidType::Instance();
+ }
+
+ const TVoidType* IPoolTypeFactory::VoidRaw() {
+ return TVoidType::InstanceRaw();
+ }
+
+ TNullTypePtr ITypeFactory::Null() {
+ return TNullType::Instance();
+ }
+
+ const TNullType* IPoolTypeFactory::NullRaw() {
+ return TNullType::InstanceRaw();
+ }
+
+ TBoolTypePtr ITypeFactory::Bool() {
+ return TBoolType::Instance();
+ }
+
+ const TBoolType* IPoolTypeFactory::BoolRaw() {
+ return TBoolType::InstanceRaw();
+ }
+
+ TInt8TypePtr ITypeFactory::Int8() {
+ return TInt8Type::Instance();
+ }
+
+ const TInt8Type* IPoolTypeFactory::Int8Raw() {
+ return TInt8Type::InstanceRaw();
+ }
+
+ TInt16TypePtr ITypeFactory::Int16() {
+ return TInt16Type::Instance();
+ }
+
+ const TInt16Type* IPoolTypeFactory::Int16Raw() {
+ return TInt16Type::InstanceRaw();
+ }
+
+ TInt32TypePtr ITypeFactory::Int32() {
+ return TInt32Type::Instance();
+ }
+
+ const TInt32Type* IPoolTypeFactory::Int32Raw() {
+ return TInt32Type::InstanceRaw();
+ }
+
+ TInt64TypePtr ITypeFactory::Int64() {
+ return TInt64Type::Instance();
+ }
+
+ const TInt64Type* IPoolTypeFactory::Int64Raw() {
+ return TInt64Type::InstanceRaw();
+ }
+
+ TUint8TypePtr ITypeFactory::Uint8() {
+ return TUint8Type::Instance();
+ }
+
+ const TUint8Type* IPoolTypeFactory::Uint8Raw() {
+ return TUint8Type::InstanceRaw();
+ }
+
+ TUint16TypePtr ITypeFactory::Uint16() {
+ return TUint16Type::Instance();
+ }
+
+ const TUint16Type* IPoolTypeFactory::Uint16Raw() {
+ return TUint16Type::InstanceRaw();
+ }
+
+ TUint32TypePtr ITypeFactory::Uint32() {
+ return TUint32Type::Instance();
+ }
+
+ const TUint32Type* IPoolTypeFactory::Uint32Raw() {
+ return TUint32Type::InstanceRaw();
+ }
+
+ TUint64TypePtr ITypeFactory::Uint64() {
+ return TUint64Type::Instance();
+ }
+
+ const TUint64Type* IPoolTypeFactory::Uint64Raw() {
+ return TUint64Type::InstanceRaw();
+ }
+
+ TFloatTypePtr ITypeFactory::Float() {
+ return TFloatType::Instance();
+ }
+
+ const TFloatType* IPoolTypeFactory::FloatRaw() {
+ return TFloatType::InstanceRaw();
+ }
+
+ TDoubleTypePtr ITypeFactory::Double() {
+ return TDoubleType::Instance();
+ }
+
+ const TDoubleType* IPoolTypeFactory::DoubleRaw() {
+ return TDoubleType::InstanceRaw();
+ }
+
+ TStringTypePtr ITypeFactory::String() {
+ return TStringType::Instance();
+ }
+
+ const TStringType* IPoolTypeFactory::StringRaw() {
+ return TStringType::InstanceRaw();
+ }
+
+ TUtf8TypePtr ITypeFactory::Utf8() {
+ return TUtf8Type::Instance();
+ }
+
+ const TUtf8Type* IPoolTypeFactory::Utf8Raw() {
+ return TUtf8Type::InstanceRaw();
+ }
+
+ TDateTypePtr ITypeFactory::Date() {
+ return TDateType::Instance();
+ }
+
+ const TDateType* IPoolTypeFactory::DateRaw() {
+ return TDateType::InstanceRaw();
+ }
+
+ TDatetimeTypePtr ITypeFactory::Datetime() {
+ return TDatetimeType::Instance();
+ }
+
+ const TDatetimeType* IPoolTypeFactory::DatetimeRaw() {
+ return TDatetimeType::InstanceRaw();
+ }
+
+ TTimestampTypePtr ITypeFactory::Timestamp() {
+ return TTimestampType::Instance();
+ }
+
+ const TTimestampType* IPoolTypeFactory::TimestampRaw() {
+ return TTimestampType::InstanceRaw();
+ }
+
+ TTzDateTypePtr ITypeFactory::TzDate() {
+ return TTzDateType::Instance();
+ }
+
+ const TTzDateType* IPoolTypeFactory::TzDateRaw() {
+ return TTzDateType::InstanceRaw();
+ }
+
+ TTzDatetimeTypePtr ITypeFactory::TzDatetime() {
+ return TTzDatetimeType::Instance();
+ }
+
+ const TTzDatetimeType* IPoolTypeFactory::TzDatetimeRaw() {
+ return TTzDatetimeType::InstanceRaw();
+ }
+
+ TTzTimestampTypePtr ITypeFactory::TzTimestamp() {
+ return TTzTimestampType::Instance();
+ }
+
+ const TTzTimestampType* IPoolTypeFactory::TzTimestampRaw() {
+ return TTzTimestampType::InstanceRaw();
+ }
+
+ TIntervalTypePtr ITypeFactory::Interval() {
+ return TIntervalType::Instance();
+ }
+
+ const TIntervalType* IPoolTypeFactory::IntervalRaw() {
+ return TIntervalType::InstanceRaw();
+ }
+
+ TDecimalTypePtr ITypeFactory::Decimal(ui8 precision, ui8 scale) {
+ return TDecimalType::Create(*this, precision, scale);
+ }
+
+ const TDecimalType* IPoolTypeFactory::DecimalRaw(ui8 precision, ui8 scale) {
+ return TDecimalType::CreateRaw(*this, precision, scale);
+ }
+
+ TJsonTypePtr ITypeFactory::Json() {
+ return TJsonType::Instance();
+ }
+
+ const TJsonType* IPoolTypeFactory::JsonRaw() {
+ return TJsonType::InstanceRaw();
+ }
+
+ TYsonTypePtr ITypeFactory::Yson() {
+ return TYsonType::Instance();
+ }
+
+ const TYsonType* IPoolTypeFactory::YsonRaw() {
+ return TYsonType::InstanceRaw();
+ }
+
+ TUuidTypePtr ITypeFactory::Uuid() {
+ return TUuidType::Instance();
+ }
+
+ const TUuidType* IPoolTypeFactory::UuidRaw() {
+ return TUuidType::InstanceRaw();
+ }
+
+ TOptionalTypePtr ITypeFactory::Optional(TTypePtr item) {
+ return TOptionalType::Create(*this, std::move(item));
+ }
+
+ const TOptionalType* IPoolTypeFactory::OptionalRaw(const TType* item) {
+ return TOptionalType::CreateRaw(*this, item);
+ }
+
+ TListTypePtr ITypeFactory::List(TTypePtr item) {
+ return TListType::Create(*this, std::move(item));
+ }
+
+ const TListType* IPoolTypeFactory::ListRaw(const TType* item) {
+ return TListType::CreateRaw(*this, item);
+ }
+
+ TDictTypePtr ITypeFactory::Dict(TTypePtr key, TTypePtr value) {
+ return TDictType::Create(*this, std::move(key), std::move(value));
+ }
+
+ const TDictType* IPoolTypeFactory::DictRaw(const TType* key, const TType* value) {
+ return TDictType::CreateRaw(*this, key, value);
+ }
+
+ TStructTypePtr ITypeFactory::Struct(TStructType::TOwnedMembers items) {
+ return TStructType::Create(*this, items);
+ }
+
+ TStructTypePtr ITypeFactory::Struct(TMaybe<TStringBuf> name, TStructType::TOwnedMembers items) {
+ return TStructType::Create(*this, name, items);
+ }
+
+ const TStructType* IPoolTypeFactory::StructRaw(TStructType::TMembers items) {
+ return TStructType::CreateRaw(*this, items);
+ }
+
+ const TStructType* IPoolTypeFactory::StructRaw(TMaybe<TStringBuf> name, TStructType::TMembers items) {
+ return TStructType::CreateRaw(*this, name, items);
+ }
+
+ TTupleTypePtr ITypeFactory::Tuple(TTupleType::TOwnedElements items) {
+ return TTupleType::Create(*this, items);
+ }
+
+ TTupleTypePtr ITypeFactory::Tuple(TMaybe<TStringBuf> name, TTupleType::TOwnedElements items) {
+ return TTupleType::Create(*this, name, items);
+ }
+
+ const TTupleType* IPoolTypeFactory::TupleRaw(TTupleType::TElements items) {
+ return TTupleType::CreateRaw(*this, items);
+ }
+
+ const TTupleType* IPoolTypeFactory::TupleRaw(TMaybe<TStringBuf> name, TTupleType::TElements items) {
+ return TTupleType::CreateRaw(*this, name, items);
+ }
+
+ TVariantTypePtr ITypeFactory::Variant(TTypePtr inner) {
+ return TVariantType::Create(*this, std::move(inner));
+ }
+
+ TVariantTypePtr ITypeFactory::Variant(TMaybe<TStringBuf> name, TTypePtr inner) {
+ return TVariantType::Create(*this, name, std::move(inner));
+ }
+
+ const TVariantType* IPoolTypeFactory::VariantRaw(const TType* inner) {
+ return TVariantType::CreateRaw(*this, inner);
+ }
+
+ const TVariantType* IPoolTypeFactory::VariantRaw(TMaybe<TStringBuf> name, const TType* inner) {
+ return TVariantType::CreateRaw(*this, name, inner);
+ }
+
+ TTaggedTypePtr ITypeFactory::Tagged(TTypePtr type, TStringBuf tag) {
+ return TTaggedType::Create(*this, std::move(type), tag);
+ }
+
+ const TTaggedType* IPoolTypeFactory::TaggedRaw(const TType* type, TStringBuf tag) {
+ return TTaggedType::CreateRaw(*this, type, tag);
+ }
+
+ namespace {
+ class TPoolFactory: public NTi::IPoolTypeFactory {
+ public:
+ TPoolFactory(size_t initial, TMemoryPool::IGrowPolicy* grow, IAllocator* alloc, TMemoryPool::TOptions options)
+ : Pool_(initial, grow, alloc, options)
+ {
+ }
+
+ public:
+ void* Allocate(size_t size, size_t align) noexcept override {
+ return Pool_.Allocate(size, align);
+ }
+
+ void Free(void* data) noexcept override {
+ Y_UNUSED(data);
+ }
+
+ protected:
+ const NTi::TType* LookupCache(const NTi::TType* type) noexcept override {
+ Y_UNUSED(type);
+ return nullptr;
+ }
+
+ void SaveCache(const NTi::TType* type) noexcept override {
+ Y_UNUSED(type);
+ }
+
+ void Ref() noexcept override {
+ Counter_.Inc();
+ }
+
+ void UnRef() noexcept override {
+ if (Counter_.Dec() == 0) {
+ delete this;
+ }
+ }
+
+ void DecRef() noexcept override {
+ if (Counter_.Dec() == 0) {
+ Y_FAIL("DecRef is not supposed to drop");
+ }
+ }
+
+ long RefCount() const noexcept override {
+ return Counter_.Val();
+ }
+
+ void RefType(NTi::TType* type) noexcept override {
+ Y_UNUSED(type);
+ }
+
+ void UnRefType(NTi::TType* type) noexcept override {
+ Y_UNUSED(type);
+ }
+
+ void DecRefType(NTi::TType* type) noexcept override {
+ Y_UNUSED(type);
+ }
+
+ long RefCountType(const NTi::TType* type) const noexcept override {
+ Y_UNUSED(type);
+ return RefCount();
+ }
+
+ public:
+ size_t Available() const noexcept override {
+ return Pool_.Available();
+ }
+
+ size_t MemoryAllocated() const noexcept override {
+ return Pool_.MemoryAllocated();
+ }
+
+ size_t MemoryWaste() const noexcept override {
+ return Pool_.MemoryWaste();
+ }
+
+ private:
+ TAtomicCounter Counter_;
+ TMemoryPool Pool_;
+ };
+
+ class TPoolFactoryDedup: public TPoolFactory {
+ public:
+ using TPoolFactory::TPoolFactory;
+
+ public:
+ const NTi::TType* LookupCache(const NTi::TType* type) noexcept override {
+ if (auto it = Cache_.find(type); it != Cache_.end()) {
+ return *it;
+ } else {
+ return nullptr;
+ }
+ }
+
+ void SaveCache(const NTi::TType* type) noexcept override {
+ Cache_.insert(type);
+ }
+
+ protected:
+ TStringBuf AllocateString(TStringBuf str) noexcept override {
+ if (str.empty()) {
+ return TStringBuf(); // `str` could still point somewhere whereas empty strbuf points to NULL
+ }
+
+ if (auto it = StringCache_.find(str); it != StringCache_.end()) {
+ return *it;
+ } else {
+ return *StringCache_.insert(it, ITypeFactoryInternal::AllocateString(str));
+ }
+ }
+
+ private:
+ THashSet<const NTi::TType*, NTi::NEq::TStrictlyEqualHash, NTi::NEq::TStrictlyEqual> Cache_;
+ THashSet<TStringBuf> StringCache_;
+ };
+
+ class THeapFactory: public NTi::ITypeFactory {
+ public:
+ void* Allocate(size_t size, size_t align) noexcept override {
+ Y_UNUSED(align);
+ return malloc(size);
+ }
+
+ void Free(void* data) noexcept override {
+ free(data);
+ }
+
+ protected:
+ const NTi::TType* LookupCache(const NTi::TType* type) noexcept override {
+ Y_UNUSED(type);
+ return nullptr;
+ }
+
+ void SaveCache(const NTi::TType* type) noexcept override {
+ Y_UNUSED(type);
+ }
+
+ void Ref() noexcept override {
+ // nothing
+ }
+
+ void UnRef() noexcept override {
+ // nothing
+ }
+
+ void DecRef() noexcept override {
+ // nothing
+ }
+
+ long RefCount() const noexcept override {
+ return 0;
+ }
+
+ void RefType(NTi::TType* type) noexcept override {
+ Y_UNUSED(type);
+ Y_FAIL("not supposed to be called");
+ }
+
+ void UnRefType(NTi::TType* type) noexcept override {
+ Y_UNUSED(type);
+ Y_FAIL("not supposed to be called");
+ }
+
+ void DecRefType(NTi::TType* type) noexcept override {
+ Y_UNUSED(type);
+ Y_FAIL("not supposed to be called");
+ }
+
+ long RefCountType(const NTi::TType* type) const noexcept override {
+ Y_UNUSED(type);
+ Y_FAIL("not supposed to be called");
+ }
+ };
+
+ THeapFactory HEAP_FACTORY;
+ }
+
+ IPoolTypeFactoryPtr PoolFactory(bool deduplicate, size_t initial, TMemoryPool::IGrowPolicy* grow, IAllocator* alloc, TMemoryPool::TOptions options) {
+ if (deduplicate) {
+ return new TPoolFactoryDedup(initial, grow, alloc, options);
+ } else {
+ return new TPoolFactory(initial, grow, alloc, options);
+ }
+ }
+
+ namespace NPrivate {
+ ITypeFactory* GetDefaultHeapFactory() {
+ return &HEAP_FACTORY;
+ }
+ }
+
+ ITypeFactoryPtr HeapFactory() {
+ return NPrivate::GetDefaultHeapFactory();
+ }
+}
diff --git a/library/cpp/type_info/type_factory.h b/library/cpp/type_info/type_factory.h
new file mode 100644
index 0000000000..df47c7082b
--- /dev/null
+++ b/library/cpp/type_info/type_factory.h
@@ -0,0 +1,906 @@
+#pragma once
+
+//! @file type_factory.h
+//!
+//! Type factory creates type instances and manages their lifetimes and destruction.
+//!
+//! Type info supports multiple ways of allocating type instances and managing their lifetimes:
+//!
+//! - [heap-based allocation] is the standard memory allocation method for C++. It offers great flexibility
+//! as it can allocate memory regions of almost arbitrary size and alignment, deallocate them, re-use them
+//! for new allocations or return the to the operating system. This flexibility comes with a price, though.
+//! Heap allocators usually require synchronisation (i.e. a mutex or a spinlock), and some memory overhead to track
+//! allocated regions. Also, they provide no guarantees on data locality and CPU cache friendliness whatsoever.
+//!
+//! When using a heap-based factory, each type instance have a separate reference counter. `TTypePtr`s will
+//! increment and decrement this counter. Whenever it reaches zero, they will destroy the type instance and free
+//! its memory. This is the standard reference counting technique, just like in `TRefCounted`.
+//!
+//! - [memory pool] is a data structure for faster memory allocation. It pre-allocates large chunks of memory and uses
+//! them to allocate smaller objects. This way, we don't have to `malloc` memory for each new object.
+//! Also, we can drop all objects at once by discarding all memory chunks.
+//!
+//! When using memory-pool-based factory, types don't have individual reference counters. Instead, they store
+//! a pointer to the factory that've created them. Whenever such type is referenced or unreferenced, the factory's
+//! own reference counter is incremented or decremented. Factory dies and releases all pool's memory when the last
+//! reference to a type in its pool dies.
+//!
+//! [heap-based allocation]: https://en.wikipedia.org/wiki/Memory_management#Dynamic_memory_allocation
+//! [Reference counting]: https://en.wikipedia.org/wiki/Reference_counting
+//! [memory pool]: https://en.wikipedia.org/wiki/Region-based_memory_management
+//!
+//! The rule of thumb is: if you have an intrusive pointer to a type, you can be sure it points to an alive object;
+//! if you have a raw pointer to a type, it's your responsibility to ensure its validity.
+//!
+//! Whenever you have a valid raw pointer to a type, you can promote it to an intrusive pointer by calling `AsPtr`.
+//! This is always safe because there's no way to create a type instance on the stack or in the static memory
+//! of a program.
+//!
+//!
+//! # Implementation details
+//!
+//! We're building a hierarchy of classes that work equally well with both heap-based allocation model
+//! and memory-pool-based allocation model. In order to understand how we do it, we should first understand how
+//! object ownership works in both models.
+//!
+//!
+//! ## Ownership schema
+//!
+//! Depending on the chosen factory implementation, ownership schema may vary.
+//!
+//! When using the heap-based factory, each type has its own reference counter. User own types, and types own
+//! their nested types. For `Optional<String>`, user owns the `Optional` type, and `Optional` type
+//! owns the `String` type:
+//!
+//! ```text
+//! Legend: A ───> B -- A owns B | A ─ ─> B -- A points to B
+//!
+//! User code: Objects:
+//! ┌─────────────┐ ┌────────────────┐
+//! │ IFactoryPtr │───────>│ Factory │
+//! └─────────────┘ └────────────────┘
+//! ┌─────────────┐ ┌────────────────┐
+//! │ TTypePtr │───────>│ Type: Optional │──┐
+//! └─────────────┘ └────────────────┘ │
+//! ┌──────────────────────┘
+//! │ ┌────────────────┐
+//! └─>│ Type: String │
+//! └────────────────┘
+//! ```
+//!
+//! When using a pool-based factory, all allocated types are owned by pool. So, in the `Optional<String>` example,
+//! user owns factory, factory owns the `Optional` type and the `String` type. All references between types
+//! are just borrows:
+//!
+//! ```text
+//! Legend: A ───> B -- A owns B | A ─ ─> B -- A points to B
+//!
+//! User code: Objects:
+//! ┌─────────────┐ ┌────────────────┐
+//! │ IFactoryPtr │─┌─────>│ Factory │──┐
+//! └─────────────┘ │ └────────────────┘ │
+//! ┌─────────────┐ │ ┌────────────────┐ │
+//! │ TTypePtr │─┘─ ─ ─>│ Type: Optional │<─┤
+//! └─────────────┘ └───────┬────────┘ │
+//! ┌ ─ ─ ─ ─ ─ ─┘ │
+//! ╎ ┌────────────────┐ │
+//! └─ ─>│ Type: String │<─┘
+//! └────────────────┘
+//! ```
+//!
+//! Notice how `TTypePtr` points to `Optional`, but doesn't own it. Instead, it owns the factory which,
+//! transitively, owns `Optional`. This way we can be sure that the factory will not be destroyed before
+//! all `IFactoryPtr`s and `TTypePtr`s die, thus guaranteeing that all `TTypePtr` always stay valid.
+//!
+//! With that in mind, we can derive two types of ownership here.
+//!
+//! Whenever `TTypePtr` owns some type, we call it 'external ownership' (because it's code that is external
+//! to this library owns something). In this situation, ref and unref procedures must increment type's own reference
+//! counter, if there is one, and also increment factory's reference counter. `NTi::TType::Ref` and `NTi::TType::Unref`
+//! do this by calling `NTi::ITypeFactoryInternal::Ref`, `NTi::ITypeFactoryInternal::RefType`,
+//! `NTi::ITypeFactoryInternal::UnRef`, `NTi::ITypeFactoryInternal::UnRefType`.
+//!
+//! Whenever one type owns another type, we call it 'internal ownership'. In this situation, ref and unref procedures
+//! must only increment and decrement type's own reference counter. They should *not* increment or decrement
+//! factory's reference counter. `NTi::TType::RefSelf` and `NTi::TType::UnrefSelf` do this by calling
+//! `NTi::ITypeFactoryInternal::RefType` and `NTi::ITypeFactoryInternal::UnRefType`, and not calling
+//! `NTi::ITypeFactoryInternal::Ref` and `NTi::ITypeFactoryInternal::UnRef`.
+//!
+//!
+//! ## Adoption semantics
+//!
+//! Let's see now what should happen when we create some complex type using more that one factory.
+//!
+//! Suppose we're creating a container type, such as `Optional`, using factory `a`. If its nested type is
+//! managed by the same factory, we have no issues at all:
+//!
+//! ```
+//! auto a = NTi::PoolFactory();
+//! auto string = a->String();
+//! auto optional = a->Optional(string);
+//! ```
+//!
+//! But what if the nested type is managed by some other factory `b`? Consider:
+//!
+//! ```
+//! auto b = NTi::PoolFactory();
+//! auto a = NTi::PoolFactory();
+//! auto string = b->String();
+//! auto optional = a->Optional(string);
+//! ```
+//!
+//! Well, we're in trouble. It's not enough for `optional` to just acquire internal ownership over `string`.
+//! Actually, `optional` has to own both `string` and its factory, `b`. Otherwise, if `b` dies, `string` will
+//! die with it, leaving `optional` with a dangling pointer.
+//!
+//! However, we can't have a type owning its factory. It will create a loop. The only safe solution is to copy
+//! the nested type from one factory to another. If we have some type managed by factory `a`, all nested types
+//! must also be managed by the same factory `a`.
+//!
+//! So, whenever we create a type, we must ensure that all its nested types are managed by the same factory.
+//! If they're not, we must deep-copy them to the current factory. This is called the 'adoption semantics'. That is,
+//! we adopt nested types into the current factory.
+//!
+//! In fact, adoption semantics is a bit more complicated that this. Some types are not managed by any factory.
+//! They have static storage duration and therefore need no management. So, if we see that there's no factory
+//! associated with some type, we don't perform deep-copy. Instead, we just return it as is.
+//!
+//!
+//! ## Overview
+//!
+//! So, now we're ready to put this puzzle together.
+//!
+//! When we create a new type instance, we do the following:
+//!
+//! 1: we adopt all nested types and acquire internal ownership over them. This is done by the `Own` function;
+//! 2: we allocate some memory for the type. This is done by `New`, `Allocate` and other functions;
+//! 3: we initialize all this memory;
+//! 4: finally, we acquire external ownership over the newly created type and return it to the user.
+//!
+//! Steps 1 through 3 are performed by the `NTi::TType::Clone` function. Step 4 is performed by the factory.
+//!
+//! When we adopt some type, we check if it's managed by any factory other that the current one. If needed,
+//! we deep-copy it by the `NTi::TType::Clone` function. That is, `Clone` copies type to a new factory and adopts
+//! nested types, adoption calls nested type's `Clone`, causing recursive deep-copying.
+//!
+//! When we release external or internal ownership over some type, factory might decide to destroy it (if, for
+//! example, this type's reference count reached zero). In this case, we need to release internal ownership
+//! over the nested types, and release all memory that was allocated in step 3.
+//! This is done by `NTi::TType::Drop` function.
+
+#include <util/generic/maybe.h>
+#include <util/generic/ptr.h>
+#include <util/generic/strbuf.h>
+#include <util/memory/pool.h>
+
+#include "type.h"
+
+#include "fwd.h"
+
+namespace NTi {
+ namespace NPrivate {
+ /// If `T` is derived from `NTi::TType` (ignoring cv-qualification), provides the member constant `value`
+ /// equal to `true`. Otherwise `value` is `false`.
+ template <typename T>
+ struct TIsType: public std::is_base_of<TType, T> {
+ };
+
+ /// Helper template variable for `TIsType`.
+ template <typename T>
+ inline constexpr const bool IsTypeV = TIsType<T>::value;
+
+ /// Statically assert that `T` is a managed object.
+ template <typename T>
+ void AssertIsType() {
+ static_assert(IsTypeV<T>, "object should be derived from 'NTi::TType'");
+ }
+ }
+
+ /// Internal interface for type factory.
+ ///
+ /// This interface is accessible from `TType` implementation, but not from outside.
+ class ITypeFactoryInternal {
+ friend class TType;
+ template <typename T>
+ friend class ::TDefaultIntrusivePtrOps;
+
+ public:
+ virtual ~ITypeFactoryInternal() = default;
+
+ public:
+ /// @name Object creation and lifetime management interface
+ ///
+ /// @{
+ //-
+ /// Create a new object of type `T` by allocating memory for it and constructing it with arguments `args`.
+ ///
+ /// If `T` is derived from `TType`, it will be linked to this factory via the `NTi::TType::SetFactory` method.
+ ///
+ /// This method does not acquire any ownership over the created object, not internal nor external. It just
+ /// allocates some memory and calls a constructor and that's it.
+ template <typename T, typename... Args>
+ inline T* New(Args&&... args) {
+ // Note: it's important to understand difference between `New` and `Create` - read the docs!
+
+ static_assert(std::is_trivially_destructible_v<T>, "can only create trivially destructible types");
+
+ auto value = new (AllocateFor<T>()) T(std::forward<Args>(args)...);
+
+ if constexpr (NPrivate::IsTypeV<T>) {
+ value->SetFactory(this);
+ }
+
+ return value;
+ }
+
+ /// Delete an object that was created via the `New` function.
+ ///
+ /// This function essentially just calls `Free`. Note that `New` can only create trivially destructible types,
+ /// thus `Delete` does not neet to call object's destructor.
+ template <typename T>
+ inline void Delete(T* obj) noexcept {
+ Free(obj);
+ }
+
+ /// Create a new array of `count` objects of type `T`.
+ ///
+ /// This function will allocate an array of `count` objects of type `T`. Then, for each object in the array,
+ /// it will call the `ctor(T* memory, size_t index)`, passing a pointer to the uninitialized object
+ /// and its index in the array. `ctor` should call placement new on the passed pointer.
+ ///
+ /// If `T` is derived from `TType`, it will be linked to this factory via the `NTi::TType::SetFactory` method.
+ ///
+ /// This method does not acquire any ownership over the created array elements, not internal nor external.
+ /// It just allocates some memory and calls a constructor and that's it.
+ ///
+ ///
+ /// # Examples
+ ///
+ /// Copy vector to factory:
+ ///
+ /// ```
+ /// TVector<T> source = ...;
+ /// TArrayRef<T> copied = NMem::NewArray<T>(factory, source.size(), [&source](T* ptr, size_t i) {
+ /// new (ptr) T(source[i]);
+ /// });
+ /// ```
+ template <typename T, typename TCtor>
+ inline TArrayRef<T> NewArray(size_t count, TCtor&& ctor) {
+ // Note: it's important to understand difference between `New` and `Create` - read the docs!
+
+ static_assert(std::is_trivially_destructible_v<T>, "can only create trivially destructible types");
+
+ auto items = AllocateArrayFor<T>(count);
+
+ for (size_t i = 0; i < count; ++i) {
+ auto value = items + i;
+ ctor(value, i);
+ if constexpr (NPrivate::IsTypeV<T>) {
+ value->SetFactory(this);
+ }
+ }
+
+ return TArrayRef(items, count);
+ }
+
+ /// Delete an object that was created via the `NewArray` function.
+ ///
+ /// This function runs `dtor` on each element of the array and then just calls `Free`. Note that `NewArray`
+ /// can only create arrays for trivially destructible types, thus `DeleteArray`
+ /// does not call object destructors.
+ template <typename T, typename TDtor>
+ inline void DeleteArray(TArrayRef<T> obj, TDtor&& dtor) noexcept {
+ for (size_t i = 0; i < obj.size(); ++i) {
+ auto value = obj.data() + i;
+ dtor(value, i);
+ }
+
+ Free(const_cast<void*>(static_cast<const void*>(obj.data())));
+ }
+
+ /// Adopt `type` into this factory.
+ ///
+ /// This function works like `AdoptRaw`, but it wraps its result to an intrusive pointer, thus acquiring
+ /// external ownership over it.
+ ///
+ /// It is used by external code to copy types between factories.
+ template <typename T>
+ inline TIntrusiveConstPtr<T> Adopt(TIntrusiveConstPtr<T> type) noexcept {
+ NPrivate::AssertIsType<T>();
+
+ return TIntrusiveConstPtr<T>(const_cast<T*>(AdoptRaw<T>(type.Get())));
+ }
+
+ /// Adopt `type` into this factory.
+ ///
+ /// This function is used to transfer types between factories. It has the following semantics:
+ ///
+ /// - if `type` is managed by this factory, this function returns its argument unchanged;
+ /// - if `type` is not managed by any factory, this function also returns its argument unchanged;
+ /// - if `type` is managed by another factory, this function deep-copies `type` into this factory and returns
+ /// a pointer to the new deep-copied value.
+ ///
+ /// This function uses `NTi::TType::Clone` to deep-copy types. It does not acquire any ownership over
+ /// the returned object, not internal nor external. If you need to adopt and acquire external ownership
+ /// (i.e. you're returning type to a user), use `Adopt`. If you need to adopt and acquire internal ownership
+ /// (i.e. you're writing `NTi::TType::Clone` implementation), use `Own`.
+ template <typename T>
+ inline const T* AdoptRaw(const T* type) noexcept {
+ NPrivate::AssertIsType<T>();
+
+ ITypeFactoryInternal* typeFactory = type->GetFactory();
+ if (typeFactory == nullptr || typeFactory == this) {
+ return type;
+ } else {
+ return type->Clone(*this);
+ }
+ }
+
+ /// Adopt some type and acquire internal ownership over it.
+ ///
+ /// This function works like `AdoptRaw`, but acquires internal ownership over the returned type.
+ ///
+ /// It is used by `NTi::TType::Clone` implementations to acquire ownership over the nested types.
+ template <typename T>
+ inline const T* Own(const T* type) noexcept {
+ NPrivate::AssertIsType<T>();
+
+ type = AdoptRaw(type);
+ const_cast<T*>(type)->RefSelf();
+ return type;
+ }
+
+ /// Release internal ownership over some type.
+ ///
+ /// This function is used by `NTi::TType::Drop` to release internal ownership over nested types.
+ template <typename T>
+ inline void Disown(const T* type) noexcept {
+ NPrivate::AssertIsType<T>();
+
+ const_cast<T*>(type)->UnRefSelf();
+ }
+
+ /// @}
+
+ public:
+ /// @name Memory management interface
+ ///
+ /// Functions for dealing with raw unmanaged memory.
+ ///
+ /// @{
+ //-
+ /// Allocate a new chunk of memory of size `size` aligned on `align`.
+ ///
+ /// The behaviour is undefined if `size` is zero, `align` is zero, is not a power of two,
+ /// or greater than `PLATFORM_DATA_ALIGN` (i.e., over-aligned).
+ ///
+ /// This function panics if memory can't be allocated.
+ ///
+ /// Returned memory may or may not be initialized.
+ virtual void* Allocate(size_t size, size_t align) noexcept = 0;
+
+ /// Allocate a chunk of memory suitable for storing an object of type `T`.
+ ///
+ /// This function panics if memory can't be allocated.
+ ///
+ /// Returned memory may or may not be initialized.
+ template <typename T>
+ T* AllocateFor() noexcept {
+ static_assert(alignof(T) <= PLATFORM_DATA_ALIGN, "over-aligned types are not supported");
+ return static_cast<T*>(Allocate(sizeof(T), alignof(T)));
+ }
+
+ /// Allocate a new chunk of memory suitable for storing `count` of objects of size `size` aligned on `align`.
+ ///
+ /// The behaviour is undefined if `count` is zero, `size` is zero, `align` is zero, is not a power of two,
+ /// or greater than `PLATFORM_DATA_ALIGN` (i.e., over-aligned).
+ ///
+ /// This function panics if memory can't be allocated.
+ ///
+ /// Returned memory may or may not be initialized.
+ void* AllocateArray(size_t count, size_t size, size_t align) noexcept {
+ return Allocate(size * count, align);
+ }
+
+ /// Allocate a chunk of memory suitable for storing an array of `count` objects of type `T`.
+ ///
+ /// This function panics if memory can't be allocated.
+ ///
+ /// Returned memory may or may not be initialized.
+ template <typename T>
+ T* AllocateArrayFor(size_t count) noexcept {
+ static_assert(alignof(T) <= PLATFORM_DATA_ALIGN, "over-aligned types are not supported");
+ return static_cast<T*>(AllocateArray(count, sizeof(T), alignof(T)));
+ }
+
+ /// Reclaim a chunk of memory memory that was allocated via one of the above `Allocate*` functions.
+ ///
+ /// This function is not suitable for freeing memory allocated via `AllocateString` or `AllocateStringMaybe`.
+ ///
+ /// Behaviour of this function varies between implementations. Specifically, in the memory-pool-based factory,
+ /// this function does nothing, while in heap-based factory, it calls `free`.
+ ///
+ /// Passing a null pointer here is ok and does nothing.
+ ///
+ /// The behaviour is undefined if the given pointer is not null and it wasn't obtained from a call
+ /// to the `Allocate` function or it was obtained from a call to the `Allocate` function
+ /// of a different factory.
+ virtual void Free(void* data) noexcept = 0;
+
+ /// Allocate memory for string `str` and copy `str` to the allocated memory.
+ ///
+ /// Note: the returned string is not guaranteed to be null-terminated.
+ ///
+ /// Note: some factories may cache results of this function to avoid repeated allocations. Read documentation
+ /// for specific factory for more info.
+ virtual TStringBuf AllocateString(TStringBuf str) noexcept {
+ return TStringBuf(MemCopy(AllocateArrayFor<char>(str.size()), str.data(), str.size()), str.size());
+ }
+
+ /// Reclaim a chunk of memory memory that was allocated via the `AllocateString` function.
+ virtual void FreeString(TStringBuf str) noexcept {
+ Free(const_cast<char*>(str.Data()));
+ }
+
+ /// Like `AllocateString`, but works with `TMaybe<TStringBuf>`.
+ ///
+ /// If the given `str` contains a value, pass it to `AllocateString`.
+ /// If it contains no value, return an empty `TMaybe`.
+ TMaybe<TStringBuf> AllocateStringMaybe(TMaybe<TStringBuf> str) noexcept {
+ if (str.Defined()) {
+ return AllocateString(str.GetRef());
+ } else {
+ return Nothing();
+ }
+ }
+
+ /// Reclaim a chunk of memory memory that was allocated via the `AllocateStringMaybe` function.
+ void FreeStringMaybe(TMaybe<TStringBuf> str) noexcept {
+ if (str.Defined()) {
+ FreeString(str.GetRef());
+ }
+ }
+
+ /// @}
+
+ public:
+ /// @name Type caching and deduplication interface
+ ///
+ /// @{
+ //-
+ /// Lookup the given type in the factory cache. Return cached version of the type or `nullptr`.
+ virtual const TType* LookupCache(const TType* type) noexcept = 0;
+
+ /// Save the given type to the factory cache. Type must be allocated via this factory.
+ virtual void SaveCache(const TType* type) noexcept = 0;
+
+ /// @}
+
+ public:
+ /// @name Factory reference counting interface
+ ///
+ /// @{
+ //-
+ /// Increase reference count of this factory.
+ virtual void Ref() noexcept = 0;
+
+ /// Decrease reference count of this factory.
+ /// If reference count reaches zero, drop this factory.
+ virtual void UnRef() noexcept = 0;
+
+ /// Decrease reference count of this factory.
+ /// If reference count reaches zero, panic.
+ virtual void DecRef() noexcept = 0;
+
+ /// Get current reference count of this factory.
+ virtual long RefCount() const noexcept = 0;
+
+ /// @}
+
+ public:
+ /// @name Type instance reference counting interface
+ ///
+ /// @{
+ //-
+ /// Increase reference count of some object managed by this factory.
+ virtual void RefType(TType*) noexcept = 0;
+
+ /// Decrease reference count of some object managed by this factory.
+ /// If reference count reaches zero, call `TType::Drop` and free object's memory, if needed.
+ virtual void UnRefType(TType*) noexcept = 0;
+
+ /// Decrease reference count of some object managed by this factory.
+ /// If reference count reaches zero, panic.
+ virtual void DecRefType(TType*) noexcept = 0;
+
+ /// Get current reference count of some object managed by this factory.
+ virtual long RefCountType(const TType*) const noexcept = 0;
+
+ /// @}
+ };
+
+ /// Base interface for type factory. There are functions to create type instances, and also a function to
+ /// adopt a type from one factory to another. See the file-level documentation for more info.
+ class ITypeFactory: protected ITypeFactoryInternal {
+ friend class TType;
+ template <typename T>
+ friend class ::TDefaultIntrusivePtrOps;
+
+ public:
+ /// Adopt type into this factory.
+ ///
+ /// This function is used to transfer types between factories. It has the following semantics:
+ ///
+ /// - if `type` is managed by this factory, this function returns its argument unchanged;
+ /// - if `type` is not managed by any factory, this function also returns its argument unchanged;
+ /// - if `type` is managed by another factory, this function deep-copies `type` into this factory and returns
+ /// a pointer to the new deep-copied value.
+ ///
+ /// This function should be used on API borders, whenever you receive ownership over some type that wasn't
+ /// created by you. For example, you may ensure that the type you got is allocated in heap, or you may want to
+ /// copy some type into a memory pool that you have control over.
+ template <typename T>
+ inline TIntrusiveConstPtr<T> Adopt(TIntrusiveConstPtr<T> value) noexcept {
+ return ITypeFactoryInternal::Adopt<T>(std::move(value));
+ }
+
+ public:
+ /// Create a new instance of a type using this factory.
+ /// @{
+ //-
+ /// Create a new `Void` type. See `NTi::TVoidType` for more info.
+ TVoidTypePtr Void();
+
+ /// Create a new `Null` type. See `NTi::TNullType` for more info.
+ TNullTypePtr Null();
+
+ /// Create a new `Bool` type. See `NTi::TBoolType` for more info.
+ TBoolTypePtr Bool();
+
+ /// Create a new `Int8` type. See `NTi::TInt8Type` for more info.
+ TInt8TypePtr Int8();
+
+ /// Create a new `Int16` type. See `NTi::TInt16Type` for more info.
+ TInt16TypePtr Int16();
+
+ /// Create a new `Int32` type. See `NTi::TInt32Type` for more info.
+ TInt32TypePtr Int32();
+
+ /// Create a new `Int64` type. See `NTi::TInt64Type` for more info.
+ TInt64TypePtr Int64();
+
+ /// Create a new `Uint8` type. See `NTi::TUint8Type` for more info.
+ TUint8TypePtr Uint8();
+
+ /// Create a new `Uint16` type. See `NTi::TUint16Type` for more info.
+ TUint16TypePtr Uint16();
+
+ /// Create a new `Uint32` type. See `NTi::TUint32Type` for more info.
+ TUint32TypePtr Uint32();
+
+ /// Create a new `Uint64` type. See `NTi::TUint64Type` for more info.
+ TUint64TypePtr Uint64();
+
+ /// Create a new `Float` type. See `NTi::TFloatType` for more info.
+ TFloatTypePtr Float();
+
+ /// Create a new `Double` type. See `NTi::TDoubleType` for more info.
+ TDoubleTypePtr Double();
+
+ /// Create a new `String` type. See `NTi::TStringType` for more info.
+ TStringTypePtr String();
+
+ /// Create a new `Utf8` type. See `NTi::TUtf8Type` for more info.
+ TUtf8TypePtr Utf8();
+
+ /// Create a new `Date` type. See `NTi::TDateType` for more info.
+ TDateTypePtr Date();
+
+ /// Create a new `Datetime` type. See `NTi::TDatetimeType` for more info.
+ TDatetimeTypePtr Datetime();
+
+ /// Create a new `Timestamp` type. See `NTi::TTimestampType` for more info.
+ TTimestampTypePtr Timestamp();
+
+ /// Create a new `TzDate` type. See `NTi::TTzDateType` for more info.
+ TTzDateTypePtr TzDate();
+
+ /// Create a new `TzDatetime` type. See `NTi::TTzDatetimeType` for more info.
+ TTzDatetimeTypePtr TzDatetime();
+
+ /// Create a new `TzTimestamp` type. See `NTi::TTzTimestampType` for more info.
+ TTzTimestampTypePtr TzTimestamp();
+
+ /// Create a new `Interval` type. See `NTi::TIntervalType` for more info.
+ TIntervalTypePtr Interval();
+
+ /// Create a new `Decimal` type. See `NTi::TDecimalType` for more info.
+ TDecimalTypePtr Decimal(ui8 precision, ui8 scale);
+
+ /// Create a new `Json` type. See `NTi::TJsonType` for more info.
+ TJsonTypePtr Json();
+
+ /// Create a new `Yson` type. See `NTi::TYsonType` for more info.
+ TYsonTypePtr Yson();
+
+ /// Create a new `Uuid` type. See `NTi::TUuidType` for more info.
+ TUuidTypePtr Uuid();
+
+ /// Create a new `Optional` type. See `NTi::TOptionalType` for more info.
+ /// If `item` is managed by some other factory, it will be deep-copied into this factory.
+ TOptionalTypePtr Optional(TTypePtr item);
+
+ /// Create a new `List` type. See `NTi::TListType` for more info.
+ /// If `item` is managed by some other factory, it will be deep-copied into this factory.
+ TListTypePtr List(TTypePtr item);
+
+ /// Create a new `Dict` type. See `NTi::TDictType` for more info.
+ /// If `key` or `value` are managed by some other factory, they will be deep-copied into this factory.
+ TDictTypePtr Dict(TTypePtr key, TTypePtr value);
+
+ /// Create a new `Struct` type. See `NTi::TStructType` for more info.
+ /// If item types are managed by some other factory, they will be deep-copied into this factory.
+ TStructTypePtr Struct(TStructType::TOwnedMembers items);
+ TStructTypePtr Struct(TMaybe<TStringBuf> name, TStructType::TOwnedMembers items);
+
+ /// Create a new `Tuple` type. See `NTi::TTupleType` for more info.
+ /// If item types are managed by some other factory, they will be deep-copied into this factory.
+ TTupleTypePtr Tuple(TTupleType::TOwnedElements items);
+ TTupleTypePtr Tuple(TMaybe<TStringBuf> name, TTupleType::TOwnedElements items);
+
+ /// Create a new `Variant` type. See `NTi::TVariantType` for more info.
+ /// If `inner` is managed by some other factory, it will be deep-copied into this factory.
+ TVariantTypePtr Variant(TTypePtr inner);
+ TVariantTypePtr Variant(TMaybe<TStringBuf> name, TTypePtr inner);
+
+ /// Create a new `Tagged` type. See `NTi::TTaggedType` for more info.
+ /// If `type` is managed by some other factory, it will be deep-copied into this factory.
+ TTaggedTypePtr Tagged(TTypePtr type, TStringBuf tag);
+
+ /// @}
+ };
+
+ /// Type factory over a memory pool.
+ ///
+ /// This interface provides some additional info related to the underlying memory pool state.
+ ///
+ /// Also, since all types that're created via this factory will live exactly as long as this factory lives,
+ /// one doesn't have to actually refcount them. Thus, it is safe to work with raw pointers instead of intrusive
+ /// pointers when using this kind of factory. So we provide the 'raw' interface that creates type instances
+ /// as usual, but doesn't increment any reference counters, thus saving some atomic operations. It still
+ /// adopts nested types into this factory, though.
+ class IPoolTypeFactory: public ITypeFactory {
+ friend class TNamedTypeBuilderRaw;
+ friend class TStructBuilderRaw;
+ friend class TTupleBuilderRaw;
+ friend class TTaggedBuilderRaw;
+
+ public:
+ /// Memory available in the current chunk.
+ ///
+ /// Allocating object of size greater than this number will cause allocation of a new chunk. When this happens,
+ /// available memory in the current chunk is lost, i.e. it'll never be used again.
+ virtual size_t Available() const noexcept = 0;
+
+ /// Number of bytes that're currently used by some object (i.e. they were allocated).
+ ///
+ /// Total memory consumed by arena is `MemoryAllocated() + MemoryWaste()`.
+ virtual size_t MemoryAllocated() const noexcept = 0;
+
+ /// Number of bytes that're not used by anyone.
+ ///
+ /// Total memory consumed by arena is `MemoryAllocated() + MemoryWaste()`.
+ ///
+ /// Memory that's lost and will not be reused is `MemoryWaste() - Available()`.
+ virtual size_t MemoryWaste() const noexcept = 0;
+
+ public:
+ /// Adopt type into this factory.
+ ///
+ /// This works like `ITypeFactory::Adopt`, but returns a raw pointer.
+ template <typename T>
+ inline const T* AdoptRaw(const T* value) noexcept {
+ return ITypeFactoryInternal::AdoptRaw<T>(value);
+ }
+
+ public:
+ /// Raw versions of type constructors. See the class-level documentation for more info.
+ ///
+ /// @{
+ //-
+ /// Create a new `Void` type. See `NTi::TVoidType` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TVoidType* VoidRaw();
+
+ /// Create a new `Null` type. See `NTi::TNullType` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TNullType* NullRaw();
+
+ /// Create a new `Bool` type. See `NTi::TBoolType` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TBoolType* BoolRaw();
+
+ /// Create a new `Int8` type. See `NTi::TInt8Type` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TInt8Type* Int8Raw();
+
+ /// Create a new `Int16` type. See `NTi::TInt16Type` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TInt16Type* Int16Raw();
+
+ /// Create a new `Int32` type. See `NTi::TInt32Type` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TInt32Type* Int32Raw();
+
+ /// Create a new `Int64` type. See `NTi::TInt64Type` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TInt64Type* Int64Raw();
+
+ /// Create a new `Uint8` type. See `NTi::TUint8Type` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TUint8Type* Uint8Raw();
+
+ /// Create a new `Uint16` type. See `NTi::TUint16Type` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TUint16Type* Uint16Raw();
+
+ /// Create a new `Uint32` type. See `NTi::TUint32Type` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TUint32Type* Uint32Raw();
+
+ /// Create a new `Uint64` type. See `NTi::TUint64Type` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TUint64Type* Uint64Raw();
+
+ /// Create a new `Float` type. See `NTi::TFloatType` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TFloatType* FloatRaw();
+
+ /// Create a new `Double` type. See `NTi::TDoubleType` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TDoubleType* DoubleRaw();
+
+ /// Create a new `String` type. See `NTi::TStringType` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TStringType* StringRaw();
+
+ /// Create a new `Utf8` type. See `NTi::TUtf8Type` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TUtf8Type* Utf8Raw();
+
+ /// Create a new `Date` type. See `NTi::TDateType` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TDateType* DateRaw();
+
+ /// Create a new `Datetime` type. See `NTi::TDatetimeType` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TDatetimeType* DatetimeRaw();
+
+ /// Create a new `Timestamp` type. See `NTi::TTimestampType` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TTimestampType* TimestampRaw();
+
+ /// Create a new `TzDate` type. See `NTi::TTzDateType` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TTzDateType* TzDateRaw();
+
+ /// Create a new `TzDatetime` type. See `NTi::TTzDatetimeType` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TTzDatetimeType* TzDatetimeRaw();
+
+ /// Create a new `TzTimestamp` type. See `NTi::TTzTimestampType` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TTzTimestampType* TzTimestampRaw();
+
+ /// Create a new `Interval` type. See `NTi::TIntervalType` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TIntervalType* IntervalRaw();
+
+ /// Create a new `Decimal` type. See `NTi::TDecimalType` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TDecimalType* DecimalRaw(ui8 precision, ui8 scale);
+
+ /// Create a new `Json` type. See `NTi::TJsonType` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TJsonType* JsonRaw();
+
+ /// Create a new `Yson` type. See `NTi::TYsonType` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TYsonType* YsonRaw();
+
+ /// Create a new `Uuid` type. See `NTi::TUuidType` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TUuidType* UuidRaw();
+
+ /// Create a new `Optional` type. See `NTi::TOptionalType` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TOptionalType* OptionalRaw(const TType* item);
+
+ /// Create a new `List` type. See `NTi::TListType` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TListType* ListRaw(const TType* item);
+
+ /// Create a new `Dict` type. See `NTi::TDictType` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TDictType* DictRaw(const TType* key, const TType* value);
+
+ /// Create a new `Struct` type. See `NTi::TStructType` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TStructType* StructRaw(TStructType::TMembers items);
+ const TStructType* StructRaw(TMaybe<TStringBuf> name, TStructType::TMembers items);
+
+ /// Create a new `Tuple` type. See `NTi::TTupleType` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TTupleType* TupleRaw(TTupleType::TElements items);
+ const TTupleType* TupleRaw(TMaybe<TStringBuf> name, TTupleType::TElements items);
+
+ /// Create a new `Variant` type. See `NTi::TVariantType` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TVariantType* VariantRaw(const TType* inner);
+ const TVariantType* VariantRaw(TMaybe<TStringBuf> name, const TType* inner);
+
+ /// Create a new `Tagged` type. See `NTi::TTaggedType` for more info.
+ /// The returned object will live for as long as this factory lives.
+ const TTaggedType* TaggedRaw(const TType* type, TStringBuf tag);
+
+ /// @}
+ };
+
+ namespace NPrivate {
+ ITypeFactory* GetDefaultHeapFactory();
+ }
+
+ /// Create a heap-based factory.
+ ///
+ /// This factory uses heap to allocate type instances, and reference counting to track individual type lifetimes.
+ ///
+ /// Choose this factory if you need safety and flexibility.
+ ITypeFactoryPtr HeapFactory();
+
+ /// Create a memory-pool-based factory.
+ ///
+ /// This factory uses a memory pool to allocate type instances. All allocated memory will be released when
+ /// the factory dies.
+ ///
+ /// Choose this factory if you need speed and memory efficiency. You have to understand how memory pool works.
+ /// We advise testing your code with msan when using this factory.
+ ///
+ /// If in doubt, use a heap-based factory instead.
+ ///
+ /// @param deduplicate
+ /// if enabled, factory will cache all types and avoid allocations when creating a new type that is
+ /// strictly-equal (see `type_equivalence.h`) to some previously created type.
+ ///
+ /// For example:
+ ///
+ /// ```
+ /// auto a = factory.Optional(factory.String());
+ /// auto b = factory.Optional(factory.String());
+ /// Y_ASSERT(a == b);
+ /// ```
+ ///
+ /// If `deduplicate` is disabled, this assert will fail. If, however, `deduplicate` is enabled,
+ /// the assert will not fail because `a` and `b` will point to the same type instance.
+ ///
+ /// This behaviour slows down creation process, but can save some memory when working with complex types.
+ ///
+ /// @param initial
+ /// number of bytes in the first page of the memory pool.
+ /// By default, we pre-allocate `64` bytes.
+ ///
+ /// @param grow
+ /// policy for calculating size for a next pool page.
+ /// By default, next page is twice as big as the previous one.
+ ///
+ /// @param alloc
+ /// underlying allocator that'll be used to allocate pool pages.
+ ///
+ /// @param options
+ /// other memory pool options.
+ IPoolTypeFactoryPtr PoolFactory(
+ bool deduplicate = true,
+ size_t initial = 64,
+ TMemoryPool::IGrowPolicy* grow = TMemoryPool::TExpGrow::Instance(),
+ IAllocator* alloc = TDefaultAllocator::Instance(),
+ TMemoryPool::TOptions options = {});
+}
diff --git a/library/cpp/type_info/type_info.cpp b/library/cpp/type_info/type_info.cpp
new file mode 100644
index 0000000000..8c14b158d0
--- /dev/null
+++ b/library/cpp/type_info/type_info.cpp
@@ -0,0 +1 @@
+#include "type_info.h"
diff --git a/library/cpp/type_info/type_info.h b/library/cpp/type_info/type_info.h
new file mode 100644
index 0000000000..5c6e249c11
--- /dev/null
+++ b/library/cpp/type_info/type_info.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include "builder.h"
+#include "error.h"
+#include "type.h"
+#include "type_constructors.h"
+#include "type_equivalence.h"
+#include "type_factory.h"
+#include "type_io.h"
+#include "type_list.h"
diff --git a/library/cpp/type_info/type_io.cpp b/library/cpp/type_info/type_io.cpp
new file mode 100644
index 0000000000..90b98c8baa
--- /dev/null
+++ b/library/cpp/type_info/type_io.cpp
@@ -0,0 +1,1186 @@
+#include "type_io.h"
+
+#include "builder.h"
+#include "type_constructors.h"
+#include "type_factory.h"
+
+#include <util/generic/overloaded.h>
+
+#include <library/cpp/yson_pull/read_ops.h>
+
+#include <util/stream/mem.h>
+#include <util/string/cast.h>
+#include <util/generic/vector.h>
+#include <util/generic/scope.h>
+
+namespace NTi::NIo {
+ namespace {
+ class TYsonDeserializer: private TNonCopyable {
+ public:
+ TYsonDeserializer(IPoolTypeFactory* factory, NYsonPull::TReader* reader)
+ : Factory_(factory)
+ , Reader_(reader)
+ {
+ }
+
+ public:
+ const TType* ReadType() {
+ if (++Depth_ > 100) {
+ ythrow TDeserializationException() << "types are nested too deep";
+ }
+
+ Y_DEFER {
+ Depth_--;
+ };
+
+ auto event = Reader_->NextEvent();
+
+ if (event.Type() == NYsonPull::EEventType::BeginStream) {
+ event = Reader_->NextEvent();
+ }
+
+ if (event.Type() == NYsonPull::EEventType::EndStream) {
+ if (Depth_ == 1) {
+ return nullptr;
+ } else {
+ ythrow TDeserializationException() << "unexpected end of stream";
+ }
+ }
+
+ if (event.Type() == NYsonPull::EEventType::Scalar && event.AsScalar().Type() == NYsonPull::EScalarType::String) {
+ return ReadTypeFromData(TypeNameStringToEnum(event.AsString()), std::monostate{});
+ } else if (event.Type() == NYsonPull::EEventType::BeginMap) {
+ return ReadTypeFromMap();
+ } else {
+ ythrow TDeserializationException() << "type must be either a string or a map";
+ }
+ }
+
+ private:
+ struct TDictData {
+ const TType *Key, *Value;
+ };
+ struct TDecimalData {
+ ui8 Precision, Scale;
+ };
+ using TTypeData = std::variant<
+ std::monostate,
+ TDictData,
+ TDecimalData,
+ TStructBuilderRaw,
+ TTupleBuilderRaw,
+ TTaggedBuilderRaw>;
+
+ const TType* ReadTypeFromMap() {
+ TMaybe<ETypeName> typeName;
+ TTypeData data;
+
+ while (true) {
+ auto event = Reader_->NextEvent();
+ if (event.Type() == NYsonPull::EEventType::Key) {
+ auto mapKey = event.AsString();
+
+ if (mapKey == "type_name") {
+ if (typeName.Defined()) {
+ ythrow TDeserializationException() << R"(duplicate key R"(type_name"))";
+ } else {
+ typeName = TypeNameStringToEnum(ReadString(R"("type_name")"));
+ }
+ } else if (mapKey == "item") {
+ if (std::holds_alternative<std::monostate>(data)) {
+ data = TTaggedBuilderRaw(*Factory_);
+ }
+ if (std::holds_alternative<TTaggedBuilderRaw>(data)) {
+ auto& builder = std::get<TTaggedBuilderRaw>(data);
+ if (!builder.HasItem()) {
+ builder.SetItem(ReadType());
+ } else {
+ ythrow TDeserializationException() << R"(duplicate key "item")";
+ }
+ } else {
+ ythrow TDeserializationException() << R"(unexpected key "item")";
+ }
+ } else if (mapKey == "key") {
+ if (std::holds_alternative<std::monostate>(data)) {
+ data = TDictData{nullptr, nullptr};
+ }
+ if (std::holds_alternative<TDictData>(data)) {
+ auto& dictData = std::get<TDictData>(data);
+ if (dictData.Key == nullptr) {
+ dictData.Key = ReadType();
+ } else {
+ ythrow TDeserializationException() << R"(duplicate key "key")";
+ }
+ } else {
+ ythrow TDeserializationException() << R"(unexpected key "key")";
+ }
+ } else if (mapKey == "value") {
+ if (std::holds_alternative<std::monostate>(data)) {
+ data = TDictData{nullptr, nullptr};
+ }
+ if (std::holds_alternative<TDictData>(data)) {
+ auto& dictData = std::get<TDictData>(data);
+ if (dictData.Value == nullptr) {
+ dictData.Value = ReadType();
+ } else {
+ ythrow TDeserializationException() << R"(duplicate key "value")";
+ }
+ } else {
+ ythrow TDeserializationException() << R"(unexpected key "value")";
+ }
+ } else if (mapKey == "members") {
+ if (std::holds_alternative<std::monostate>(data)) {
+ data = TStructBuilderRaw(*Factory_);
+ }
+ if (std::holds_alternative<TStructBuilderRaw>(data)) {
+ ReadMembers(std::get<TStructBuilderRaw>(data));
+ } else {
+ ythrow TDeserializationException() << R"(unexpected key "members")";
+ }
+ } else if (mapKey == "elements") {
+ if (std::holds_alternative<std::monostate>(data)) {
+ data = TTupleBuilderRaw(*Factory_);
+ }
+ if (std::holds_alternative<TTupleBuilderRaw>(data)) {
+ ReadElements(std::get<TTupleBuilderRaw>(data));
+ } else {
+ ythrow TDeserializationException() << R"(unexpected key "elements")";
+ }
+ } else if (mapKey == "tag") {
+ if (std::holds_alternative<std::monostate>(data)) {
+ data = TTaggedBuilderRaw(*Factory_);
+ }
+ if (std::holds_alternative<TTaggedBuilderRaw>(data)) {
+ auto& builder = std::get<TTaggedBuilderRaw>(data);
+ if (!builder.HasTag()) {
+ builder.SetTag(ReadString(R"("tag")"));
+ } else {
+ ythrow TDeserializationException() << R"(duplicate key "tag")";
+ }
+ } else {
+ ythrow TDeserializationException() << R"(unexpected key "tag")";
+ }
+ } else if (mapKey == "precision") {
+ if (std::holds_alternative<std::monostate>(data)) {
+ data = TDecimalData{ReadSmallInt(R"("precision")"), 0};
+ } else if (std::holds_alternative<TDecimalData>(data)) {
+ auto& decimalData = std::get<TDecimalData>(data);
+ if (decimalData.Precision == 0) {
+ decimalData.Precision = ReadSmallInt(R"("precision")");
+ } else {
+ ythrow TDeserializationException() << R"(duplicate key "precision")";
+ }
+ } else {
+ ythrow TDeserializationException() << R"(unexpected key "precision")";
+ }
+ } else if (mapKey == "scale") {
+ if (std::holds_alternative<std::monostate>(data)) {
+ data = TDecimalData{0, ReadSmallInt(R"("scale")")};
+ } else if (std::holds_alternative<TDecimalData>(data)) {
+ auto& decimalData = std::get<TDecimalData>(data);
+ if (decimalData.Scale == 0) {
+ decimalData.Scale = ReadSmallInt(R"("scale")");
+ } else {
+ ythrow TDeserializationException() << R"(duplicate key "scale")";
+ }
+ } else {
+ ythrow TDeserializationException() << R"(unexpected key "scale")";
+ }
+ } else {
+ NYsonPull::NReadOps::SkipValue(*Reader_);
+ }
+ } else if (event.Type() == NYsonPull::EEventType::EndMap) {
+ if (!typeName.Defined()) {
+ ythrow TDeserializationException() << R"(missing required key "type_name")";
+ }
+
+ return ReadTypeFromData(*typeName, std::move(data));
+ } else {
+ ythrow TDeserializationException() << "unexpected event " << event.Type();
+ }
+ }
+ }
+
+ const TType* ReadTypeFromData(ETypeName typeName, TTypeData data) {
+ const TType* type;
+
+ switch (typeName) {
+ case ETypeName::Bool:
+ type = TBoolType::InstanceRaw();
+ break;
+ case ETypeName::Int8:
+ type = TInt8Type::InstanceRaw();
+ break;
+ case ETypeName::Int16:
+ type = TInt16Type::InstanceRaw();
+ break;
+ case ETypeName::Int32:
+ type = TInt32Type::InstanceRaw();
+ break;
+ case ETypeName::Int64:
+ type = TInt64Type::InstanceRaw();
+ break;
+ case ETypeName::Uint8:
+ type = TUint8Type::InstanceRaw();
+ break;
+ case ETypeName::Uint16:
+ type = TUint16Type::InstanceRaw();
+ break;
+ case ETypeName::Uint32:
+ type = TUint32Type::InstanceRaw();
+ break;
+ case ETypeName::Uint64:
+ type = TUint64Type::InstanceRaw();
+ break;
+ case ETypeName::Float:
+ type = TFloatType::InstanceRaw();
+ break;
+ case ETypeName::Double:
+ type = TDoubleType::InstanceRaw();
+ break;
+ case ETypeName::String:
+ type = TStringType::InstanceRaw();
+ break;
+ case ETypeName::Utf8:
+ type = TUtf8Type::InstanceRaw();
+ break;
+ case ETypeName::Date:
+ type = TDateType::InstanceRaw();
+ break;
+ case ETypeName::Datetime:
+ type = TDatetimeType::InstanceRaw();
+ break;
+ case ETypeName::Timestamp:
+ type = TTimestampType::InstanceRaw();
+ break;
+ case ETypeName::TzDate:
+ type = TTzDateType::InstanceRaw();
+ break;
+ case ETypeName::TzDatetime:
+ type = TTzDatetimeType::InstanceRaw();
+ break;
+ case ETypeName::TzTimestamp:
+ type = TTzTimestampType::InstanceRaw();
+ break;
+ case ETypeName::Interval:
+ type = TIntervalType::InstanceRaw();
+ break;
+ case ETypeName::Decimal: {
+ if (!std::holds_alternative<TDecimalData>(data)) {
+ ythrow TDeserializationException() << R"(missing required keys "precision" and "scale" for type Decimal)";
+ }
+
+ auto& decimalData = std::get<TDecimalData>(data);
+
+ if (decimalData.Precision == 0) {
+ ythrow TDeserializationException() << R"(missing required key "precision" for type Decimal)";
+ }
+
+ if (decimalData.Scale == 0) {
+ ythrow TDeserializationException() << R"(missing required key "scale" for type Decimal)";
+ }
+
+ return Factory_->DecimalRaw(decimalData.Precision, decimalData.Scale);
+ }
+ case ETypeName::Json:
+ type = TJsonType::InstanceRaw();
+ break;
+ case ETypeName::Yson:
+ type = TYsonType::InstanceRaw();
+ break;
+ case ETypeName::Uuid:
+ type = TUuidType::InstanceRaw();
+ break;
+ case ETypeName::Void:
+ type = TVoidType::InstanceRaw();
+ break;
+ case ETypeName::Null:
+ type = TNullType::InstanceRaw();
+ break;
+ case ETypeName::Optional: {
+ if (!std::holds_alternative<TTaggedBuilderRaw>(data)) {
+ ythrow TDeserializationException() << R"(missing required key "item" for type Optional)";
+ }
+
+ auto& builder = std::get<TTaggedBuilderRaw>(data);
+
+ if (!builder.HasItem()) {
+ ythrow TDeserializationException() << R"(missing required key "item" for type Optional)";
+ }
+
+ return Factory_->OptionalRaw(*builder.GetItem());
+ }
+ case ETypeName::List: {
+ if (!std::holds_alternative<TTaggedBuilderRaw>(data)) {
+ ythrow TDeserializationException() << R"(missing required key "item" for type List)";
+ }
+
+ auto& builder = std::get<TTaggedBuilderRaw>(data);
+
+ if (!builder.HasItem()) {
+ ythrow TDeserializationException() << R"(missing required key "item" for type List)";
+ }
+
+ return Factory_->ListRaw(*builder.GetItem());
+ }
+ case ETypeName::Dict: {
+ if (!std::holds_alternative<TDictData>(data)) {
+ ythrow TDeserializationException() << R"(missing required keys "key" and "value" for type Dict)";
+ }
+
+ auto& dictData = std::get<TDictData>(data);
+
+ if (dictData.Key == nullptr) {
+ ythrow TDeserializationException() << R"(missing required key "key" for type Dict)";
+ }
+
+ if (dictData.Value == nullptr) {
+ ythrow TDeserializationException() << R"(missing required key "value" for type Dict)";
+ }
+
+ return Factory_->DictRaw(dictData.Key, dictData.Value);
+ }
+ case ETypeName::Struct: {
+ if (!std::holds_alternative<TStructBuilderRaw>(data)) {
+ ythrow TDeserializationException() << R"(missing required key "members" for type Struct)";
+ }
+
+ return std::get<TStructBuilderRaw>(data).BuildRaw();
+ }
+ case ETypeName::Tuple: {
+ if (!std::holds_alternative<TTupleBuilderRaw>(data)) {
+ ythrow TDeserializationException() << R"(missing required key "elements" for type Tuple)";
+ }
+
+ return std::get<TTupleBuilderRaw>(data).BuildRaw();
+ }
+ case ETypeName::Variant: {
+ if (std::holds_alternative<TStructBuilderRaw>(data)) {
+ return std::get<TStructBuilderRaw>(data).BuildVariantRaw();
+ } else if (std::holds_alternative<TTupleBuilderRaw>(data)) {
+ return std::get<TTupleBuilderRaw>(data).BuildVariantRaw();
+ } else {
+ ythrow TDeserializationException() << R"(missing both keys "members" and "elements" for type Variant)";
+ }
+ }
+ case ETypeName::Tagged: {
+ if (!std::holds_alternative<TTaggedBuilderRaw>(data)) {
+ ythrow TDeserializationException() << R"(missing required keys "tag" and "item" for type Tagged)";
+ }
+
+ auto& builder = std::get<TTaggedBuilderRaw>(data);
+
+ if (!builder.HasItem()) {
+ ythrow TDeserializationException() << R"(missing required key "item" for type Tagged)";
+ }
+
+ if (!builder.HasTag()) {
+ ythrow TDeserializationException() << R"(missing required key "tag" for type Tagged)";
+ }
+
+ return builder.BuildRaw();
+ }
+ }
+
+ if (!std::holds_alternative<std::monostate>(data)) {
+ ythrow TDeserializationException() << "unexpected key for type " << typeName;
+ }
+
+ return type;
+ }
+
+ TStringBuf ReadString(TStringBuf what) {
+ auto event = Reader_->NextEvent();
+
+ if (event.Type() != NYsonPull::EEventType::Scalar || event.AsScalar().Type() != NYsonPull::EScalarType::String) {
+ ythrow TDeserializationException() << what << " must contain a string";
+ }
+
+ return event.AsString();
+ }
+
+ ui8 ReadSmallInt(TStringBuf what) {
+ auto event = Reader_->NextEvent();
+
+ if (event.Type() != NYsonPull::EEventType::Scalar || event.AsScalar().Type() != NYsonPull::EScalarType::Int64) {
+ ythrow TDeserializationException() << what << " must contain a signed integer";
+ }
+
+ auto result = event.AsScalar().AsInt64();
+
+ if (result <= 0) {
+ ythrow TDeserializationException() << what << " must be greater than zero";
+ }
+
+ if (result > Max<ui8>()) {
+ ythrow TDeserializationException() << what << " is too big";
+ }
+
+ return static_cast<ui8>(result);
+ }
+
+ void ReadMembers(TStructBuilderRaw& builder) {
+ if (Reader_->NextEvent().Type() != NYsonPull::EEventType::BeginList) {
+ ythrow TDeserializationException() << R"("members" must contain a list)";
+ }
+
+ while (true) {
+ auto event = Reader_->NextEvent();
+
+ if (event.Type() == NYsonPull::EEventType::BeginMap) {
+ while (true) {
+ auto event = Reader_->NextEvent();
+
+ if (event.Type() == NYsonPull::EEventType::Key) {
+ auto mapKey = event.AsString();
+ if (mapKey == "name") {
+ if (builder.HasMemberName()) {
+ ythrow TDeserializationException() << R"(duplicate key "name")";
+ }
+
+ builder.AddMemberName(ReadString(R"("name")"));
+ } else if (mapKey == "type") {
+ if (builder.HasMemberType()) {
+ ythrow TDeserializationException() << R"(duplicate key "type")";
+ }
+
+ builder.AddMemberType(ReadType());
+ } else {
+ NYsonPull::NReadOps::SkipValue(*Reader_);
+ }
+ } else if (event.Type() == NYsonPull::EEventType::EndMap) {
+ if (!builder.HasMemberName()) {
+ ythrow TDeserializationException() << R"(missing required key "name")";
+ }
+ if (!builder.HasMemberType()) {
+ ythrow TDeserializationException() << R"(missing required key "type")";
+ }
+
+ builder.AddMember();
+ break;
+ } else {
+ ythrow TDeserializationException() << "unexpected event " << event.Type();
+ }
+ }
+ } else if (event.Type() == NYsonPull::EEventType::EndList) {
+ break;
+ } else {
+ ythrow TDeserializationException() << R"("members" must contain a list of maps)";
+ }
+ }
+ }
+
+ void ReadElements(TTupleBuilderRaw& builder) {
+ if (Reader_->NextEvent().Type() != NYsonPull::EEventType::BeginList) {
+ ythrow TDeserializationException() << R"("elements" must contain a list)";
+ }
+
+ while (true) {
+ auto event = Reader_->NextEvent();
+
+ if (event.Type() == NYsonPull::EEventType::BeginMap) {
+ while (true) {
+ auto event = Reader_->NextEvent();
+
+ if (event.Type() == NYsonPull::EEventType::Key) {
+ auto mapKey = event.AsString();
+ if (mapKey == "type") {
+ if (builder.HasElementType()) {
+ ythrow TDeserializationException() << R"(duplicate key "type")";
+ }
+
+ builder.AddElementType(ReadType());
+ } else {
+ NYsonPull::NReadOps::SkipValue(*Reader_);
+ }
+ } else if (event.Type() == NYsonPull::EEventType::EndMap) {
+ if (!builder.HasElementType()) {
+ ythrow TDeserializationException() << R"(missing required key "type")";
+ }
+
+ builder.AddElement();
+ break;
+ } else {
+ ythrow TDeserializationException() << "unexpected event " << event.Type();
+ }
+ }
+ } else if (event.Type() == NYsonPull::EEventType::EndList) {
+ break;
+ } else {
+ ythrow TDeserializationException() << R"("elements" must contain a list of maps)";
+ }
+ }
+ }
+
+ static ETypeName TypeNameStringToEnum(TStringBuf name) {
+ static const THashMap<TStringBuf, ETypeName> dispatch = {
+ {"void", ETypeName::Void},
+ {"null", ETypeName::Null},
+ {"bool", ETypeName::Bool},
+ {"int8", ETypeName::Int8},
+ {"int16", ETypeName::Int16},
+ {"int32", ETypeName::Int32},
+ {"int64", ETypeName::Int64},
+ {"uint8", ETypeName::Uint8},
+ {"uint16", ETypeName::Uint16},
+ {"uint32", ETypeName::Uint32},
+ {"uint64", ETypeName::Uint64},
+ {"float", ETypeName::Float},
+ {"double", ETypeName::Double},
+ {"string", ETypeName::String},
+ {"utf8", ETypeName::Utf8},
+ {"date", ETypeName::Date},
+ {"datetime", ETypeName::Datetime},
+ {"timestamp", ETypeName::Timestamp},
+ {"tz_date", ETypeName::TzDate},
+ {"tz_datetime", ETypeName::TzDatetime},
+ {"tz_timestamp", ETypeName::TzTimestamp},
+ {"interval", ETypeName::Interval},
+ {"json", ETypeName::Json},
+ {"yson", ETypeName::Yson},
+ {"uuid", ETypeName::Uuid},
+ {"decimal", ETypeName::Decimal},
+ {"optional", ETypeName::Optional},
+ {"list", ETypeName::List},
+ {"dict", ETypeName::Dict},
+ {"struct", ETypeName::Struct},
+ {"tuple", ETypeName::Tuple},
+ {"variant", ETypeName::Variant},
+ {"tagged", ETypeName::Tagged},
+ };
+
+ if (auto it = dispatch.find(name); it != dispatch.end()) {
+ return it->second;
+ } else {
+ ythrow TDeserializationException() << "unknown type " << TString{name}.Quote();
+ }
+ }
+
+ private:
+ IPoolTypeFactory* Factory_;
+ NYsonPull::TReader* Reader_;
+ size_t Depth_ = 0;
+ };
+ }
+
+ TTypePtr DeserializeYson(ITypeFactory& factory, NYsonPull::TReader& reader, bool deduplicate) {
+ auto pool = PoolFactory(deduplicate, 2048);
+ auto type = DeserializeYsonRaw(*pool, reader);
+ return factory.Adopt(type->AsPtr());
+ }
+
+ TTypePtr DeserializeYson(ITypeFactory& factory, TStringBuf data, bool deduplicate) {
+ auto reader = NYsonPull::TReader(NYsonPull::NInput::FromMemory(data), NYsonPull::EStreamType::Node);
+ return DeserializeYson(factory, reader, deduplicate);
+ }
+
+ TTypePtr DeserializeYson(ITypeFactory& factory, IInputStream& input, bool deduplicate) {
+ auto reader = NYsonPull::TReader(NYsonPull::NInput::FromInputStream(&input), NYsonPull::EStreamType::Node);
+ return DeserializeYson(factory, reader, deduplicate);
+ }
+
+ const TType* DeserializeYsonRaw(IPoolTypeFactory& factory, NYsonPull::TReader& reader) {
+ if (reader.LastEvent().Type() != NYsonPull::EEventType::BeginStream) {
+ ythrow TDeserializationException() << "stream contains extraneous data";
+ }
+
+ auto type = DeserializeYsonMultipleRaw(factory, reader);
+
+ if (type == nullptr) {
+ ythrow TDeserializationException() << "unexpected end of stream";
+ }
+
+ if (reader.NextEvent().Type() != NYsonPull::EEventType::EndStream) {
+ ythrow TDeserializationException() << "stream contains extraneous data";
+ }
+
+ return type;
+ }
+
+ const TType* DeserializeYsonRaw(IPoolTypeFactory& factory, TStringBuf data) {
+ auto reader = NYsonPull::TReader(NYsonPull::NInput::FromMemory(data), NYsonPull::EStreamType::Node);
+ return DeserializeYsonRaw(factory, reader);
+ }
+
+ const TType* DeserializeYsonRaw(IPoolTypeFactory& factory, IInputStream& input) {
+ auto reader = NYsonPull::TReader(NYsonPull::NInput::FromInputStream(&input), NYsonPull::EStreamType::Node);
+ return DeserializeYsonRaw(factory, reader);
+ }
+
+ const TType* DeserializeYsonMultipleRaw(IPoolTypeFactory& factory, NYsonPull::TReader& reader) {
+ return TYsonDeserializer(&factory, &reader).ReadType();
+ }
+
+ TString SerializeYson(const TType* type, bool humanReadable, bool includeTags) {
+ auto result = TString();
+ auto writer = humanReadable
+ ? NYsonPull::MakePrettyTextWriter(NYsonPull::NOutput::FromString(&result), NYsonPull::EStreamType::Node)
+ : NYsonPull::MakeBinaryWriter(NYsonPull::NOutput::FromString(&result), NYsonPull::EStreamType::Node);
+ SerializeYson(type, writer.GetConsumer(), includeTags);
+ return result;
+ }
+
+ void SerializeYson(const TType* type, NYsonPull::IConsumer& consumer, bool includeTags) {
+ consumer.OnBeginStream();
+ SerializeYsonMultiple(type, consumer, includeTags);
+ consumer.OnEndStream();
+ }
+
+ void SerializeYson(const TType* type, IOutputStream& stream, bool humanReadable, bool includeTags) {
+ auto writer = humanReadable
+ ? NYsonPull::MakePrettyTextWriter(NYsonPull::NOutput::FromOutputStream(&stream), NYsonPull::EStreamType::Node)
+ : NYsonPull::MakeBinaryWriter(NYsonPull::NOutput::FromOutputStream(&stream), NYsonPull::EStreamType::Node);
+ SerializeYson(type, writer.GetConsumer(), includeTags);
+ }
+
+ void SerializeYsonMultiple(const TType* type, NYsonPull::IConsumer& consumer, bool includeTags) {
+ type->VisitRaw(TOverloaded{
+ [&consumer](const TVoidType*) {
+ consumer.OnScalarString("void");
+ },
+ [&consumer](const TNullType*) {
+ consumer.OnScalarString("null");
+ },
+ [&consumer](const TBoolType*) {
+ consumer.OnScalarString("bool");
+ },
+ [&consumer](const TInt8Type*) {
+ consumer.OnScalarString("int8");
+ },
+ [&consumer](const TInt16Type*) {
+ consumer.OnScalarString("int16");
+ },
+ [&consumer](const TInt32Type*) {
+ consumer.OnScalarString("int32");
+ },
+ [&consumer](const TInt64Type*) {
+ consumer.OnScalarString("int64");
+ },
+ [&consumer](const TUint8Type*) {
+ consumer.OnScalarString("uint8");
+ },
+ [&consumer](const TUint16Type*) {
+ consumer.OnScalarString("uint16");
+ },
+ [&consumer](const TUint32Type*) {
+ consumer.OnScalarString("uint32");
+ },
+ [&consumer](const TUint64Type*) {
+ consumer.OnScalarString("uint64");
+ },
+ [&consumer](const TFloatType*) {
+ consumer.OnScalarString("float");
+ },
+ [&consumer](const TDoubleType*) {
+ consumer.OnScalarString("double");
+ },
+ [&consumer](const TStringType*) {
+ consumer.OnScalarString("string");
+ },
+ [&consumer](const TUtf8Type*) {
+ consumer.OnScalarString("utf8");
+ },
+ [&consumer](const TDateType*) {
+ consumer.OnScalarString("date");
+ },
+ [&consumer](const TDatetimeType*) {
+ consumer.OnScalarString("datetime");
+ },
+ [&consumer](const TTimestampType*) {
+ consumer.OnScalarString("timestamp");
+ },
+ [&consumer](const TTzDateType*) {
+ consumer.OnScalarString("tz_date");
+ },
+ [&consumer](const TTzDatetimeType*) {
+ consumer.OnScalarString("tz_datetime");
+ },
+ [&consumer](const TTzTimestampType*) {
+ consumer.OnScalarString("tz_timestamp");
+ },
+ [&consumer](const TIntervalType*) {
+ consumer.OnScalarString("interval");
+ },
+ [&consumer](const TJsonType*) {
+ consumer.OnScalarString("json");
+ },
+ [&consumer](const TYsonType*) {
+ consumer.OnScalarString("yson");
+ },
+ [&consumer](const TUuidType*) {
+ consumer.OnScalarString("uuid");
+ },
+ [&consumer](const TDecimalType* t) {
+ consumer.OnBeginMap();
+
+ consumer.OnKey("type_name");
+ consumer.OnScalarString("decimal");
+
+ consumer.OnKey("precision");
+ consumer.OnScalarInt64(t->GetPrecision());
+
+ consumer.OnKey("scale");
+ consumer.OnScalarInt64(t->GetScale());
+
+ consumer.OnEndMap();
+ },
+ [&consumer, includeTags](const TOptionalType* t) {
+ consumer.OnBeginMap();
+
+ consumer.OnKey("type_name");
+ consumer.OnScalarString("optional");
+
+ consumer.OnKey("item");
+ SerializeYsonMultiple(t->GetItemTypeRaw(), consumer, includeTags);
+
+ consumer.OnEndMap();
+ },
+ [&consumer, includeTags](const TListType* t) {
+ consumer.OnBeginMap();
+
+ consumer.OnKey("type_name");
+ consumer.OnScalarString("list");
+
+ consumer.OnKey("item");
+ SerializeYsonMultiple(t->GetItemTypeRaw(), consumer, includeTags);
+
+ consumer.OnEndMap();
+ },
+ [&consumer, includeTags](const TDictType* t) {
+ consumer.OnBeginMap();
+
+ consumer.OnKey("type_name");
+ consumer.OnScalarString("dict");
+
+ consumer.OnKey("key");
+ SerializeYsonMultiple(t->GetKeyTypeRaw(), consumer, includeTags);
+
+ consumer.OnKey("value");
+ SerializeYsonMultiple(t->GetValueTypeRaw(), consumer, includeTags);
+
+ consumer.OnEndMap();
+ },
+ [&consumer, includeTags](const TStructType* t) {
+ consumer.OnBeginMap();
+
+ consumer.OnKey("type_name");
+ consumer.OnScalarString("struct");
+
+ consumer.OnKey("members");
+ consumer.OnBeginList();
+ for (auto& item : t->GetMembers()) {
+ consumer.OnBeginMap();
+
+ consumer.OnKey("name");
+ consumer.OnScalarString(item.GetName());
+
+ consumer.OnKey("type");
+ SerializeYsonMultiple(item.GetTypeRaw(), consumer, includeTags);
+
+ consumer.OnEndMap();
+ }
+ consumer.OnEndList();
+
+ consumer.OnEndMap();
+ },
+ [&consumer, includeTags](const TTupleType* t) {
+ consumer.OnBeginMap();
+
+ consumer.OnKey("type_name");
+ consumer.OnScalarString("tuple");
+
+ consumer.OnKey("elements");
+ consumer.OnBeginList();
+ for (auto& item : t->GetElements()) {
+ consumer.OnBeginMap();
+
+ consumer.OnKey("type");
+ SerializeYsonMultiple(item.GetTypeRaw(), consumer, includeTags);
+
+ consumer.OnEndMap();
+ }
+ consumer.OnEndList();
+
+ consumer.OnEndMap();
+ },
+ [&consumer, includeTags](const TVariantType* t) {
+ consumer.OnBeginMap();
+
+ consumer.OnKey("type_name");
+ consumer.OnScalarString("variant");
+
+ t->VisitUnderlyingRaw(
+ TOverloaded{
+ [&consumer, includeTags](const TStructType* t) {
+ // Warning: we loose struct's name here.
+ // See https://ml.yandex-team.ru/thread/data-com-dev/171136785840079161/
+
+ consumer.OnKey("members");
+ consumer.OnBeginList();
+ for (auto& item : t->GetMembers()) {
+ consumer.OnBeginMap();
+
+ consumer.OnKey("name");
+ consumer.OnScalarString(item.GetName());
+
+ consumer.OnKey("type");
+ SerializeYsonMultiple(item.GetTypeRaw(), consumer, includeTags);
+
+ consumer.OnEndMap();
+ }
+ consumer.OnEndList();
+ },
+ [&consumer, includeTags](const TTupleType* t) {
+ // Warning: we loose tuple's name here.
+ // See https://ml.yandex-team.ru/thread/data-com-dev/171136785840079161/
+
+ consumer.OnKey("elements");
+ consumer.OnBeginList();
+ for (auto& item : t->GetElements()) {
+ consumer.OnBeginMap();
+
+ consumer.OnKey("type");
+ SerializeYsonMultiple(item.GetTypeRaw(), consumer, includeTags);
+
+ consumer.OnEndMap();
+ }
+ consumer.OnEndList();
+ }});
+
+ consumer.OnEndMap();
+ },
+ [&consumer, includeTags](const TTaggedType* t) {
+ if (includeTags) {
+ consumer.OnBeginMap();
+
+ consumer.OnKey("type_name");
+ consumer.OnScalarString("tagged");
+
+ consumer.OnKey("tag");
+ consumer.OnScalarString(t->GetTag());
+
+ consumer.OnKey("item");
+ SerializeYsonMultiple(t->GetItemTypeRaw(), consumer, includeTags);
+
+ consumer.OnEndMap();
+ } else {
+ SerializeYsonMultiple(t->GetItemTypeRaw(), consumer, includeTags);
+ }
+ },
+ });
+ }
+
+ namespace {
+ void WriteVoidType(NYsonPull::IConsumer& consumer) {
+ consumer.OnBeginList();
+ consumer.OnScalarString("VoidType");
+ consumer.OnEndList();
+ }
+
+ void WriteNullType(NYsonPull::IConsumer& consumer) {
+ consumer.OnBeginList();
+ consumer.OnScalarString("NullType");
+ consumer.OnEndList();
+ }
+
+ void WriteDataType(NYsonPull::IConsumer& consumer, EPrimitiveTypeName name) {
+ consumer.OnBeginList();
+ consumer.OnScalarString("DataType");
+ consumer.OnScalarString(ToString(name));
+ consumer.OnEndList();
+ }
+ }
+
+ void AsYqlType(const TType* type, NYsonPull::IConsumer& consumer, bool includeTags) {
+ type->VisitRaw(TOverloaded{
+ [&consumer](const TVoidType*) {
+ WriteVoidType(consumer);
+ },
+ [&consumer](const TNullType*) {
+ WriteNullType(consumer);
+ },
+ [&consumer](const TBoolType*) {
+ WriteDataType(consumer, EPrimitiveTypeName::Bool);
+ },
+ [&consumer](const TInt8Type*) {
+ WriteDataType(consumer, EPrimitiveTypeName::Int8);
+ },
+ [&consumer](const TInt16Type*) {
+ WriteDataType(consumer, EPrimitiveTypeName::Int16);
+ },
+ [&consumer](const TInt32Type*) {
+ WriteDataType(consumer, EPrimitiveTypeName::Int32);
+ },
+ [&consumer](const TInt64Type*) {
+ WriteDataType(consumer, EPrimitiveTypeName::Int64);
+ },
+ [&consumer](const TUint8Type*) {
+ WriteDataType(consumer, EPrimitiveTypeName::Uint8);
+ },
+ [&consumer](const TUint16Type*) {
+ WriteDataType(consumer, EPrimitiveTypeName::Uint16);
+ },
+ [&consumer](const TUint32Type*) {
+ WriteDataType(consumer, EPrimitiveTypeName::Uint32);
+ },
+ [&consumer](const TUint64Type*) {
+ WriteDataType(consumer, EPrimitiveTypeName::Uint64);
+ },
+ [&consumer](const TFloatType*) {
+ WriteDataType(consumer, EPrimitiveTypeName::Float);
+ },
+ [&consumer](const TDoubleType*) {
+ WriteDataType(consumer, EPrimitiveTypeName::Double);
+ },
+ [&consumer](const TStringType*) {
+ WriteDataType(consumer, EPrimitiveTypeName::String);
+ },
+ [&consumer](const TUtf8Type*) {
+ WriteDataType(consumer, EPrimitiveTypeName::Utf8);
+ },
+ [&consumer](const TDateType*) {
+ WriteDataType(consumer, EPrimitiveTypeName::Date);
+ },
+ [&consumer](const TDatetimeType*) {
+ WriteDataType(consumer, EPrimitiveTypeName::Datetime);
+ },
+ [&consumer](const TTimestampType*) {
+ WriteDataType(consumer, EPrimitiveTypeName::Timestamp);
+ },
+ [&consumer](const TTzDateType*) {
+ WriteDataType(consumer, EPrimitiveTypeName::TzDate);
+ },
+ [&consumer](const TTzDatetimeType*) {
+ WriteDataType(consumer, EPrimitiveTypeName::TzDatetime);
+ },
+ [&consumer](const TTzTimestampType*) {
+ WriteDataType(consumer, EPrimitiveTypeName::TzTimestamp);
+ },
+ [&consumer](const TIntervalType*) {
+ WriteDataType(consumer, EPrimitiveTypeName::Interval);
+ },
+ [&consumer](const TJsonType*) {
+ WriteDataType(consumer, EPrimitiveTypeName::Json);
+ },
+ [&consumer](const TYsonType*) {
+ WriteDataType(consumer, EPrimitiveTypeName::Yson);
+ },
+ [&consumer](const TUuidType*) {
+ WriteDataType(consumer, EPrimitiveTypeName::Uuid);
+ },
+ [&consumer](const TDecimalType* t) {
+ consumer.OnBeginList();
+ consumer.OnScalarString("DataType");
+ consumer.OnScalarString("Decimal");
+ consumer.OnScalarString(ToString(t->GetPrecision()));
+ consumer.OnScalarString(ToString(t->GetScale()));
+ consumer.OnEndList();
+ },
+ [&consumer, includeTags](const TOptionalType* t) {
+ consumer.OnBeginList();
+ consumer.OnScalarString("OptionalType");
+ AsYqlType(t->GetItemTypeRaw(), consumer, includeTags);
+ consumer.OnEndList();
+ },
+ [&consumer, includeTags](const TListType* t) {
+ consumer.OnBeginList();
+ consumer.OnScalarString("ListType");
+ AsYqlType(t->GetItemTypeRaw(), consumer, includeTags);
+ consumer.OnEndList();
+ },
+ [&consumer, includeTags](const TDictType* t) {
+ consumer.OnBeginList();
+ consumer.OnScalarString("DictType");
+ AsYqlType(t->GetKeyTypeRaw(), consumer, includeTags);
+ AsYqlType(t->GetValueTypeRaw(), consumer, includeTags);
+ consumer.OnEndList();
+ },
+ [&consumer, includeTags](const TStructType* t) {
+ consumer.OnBeginList();
+ consumer.OnScalarString("StructType");
+ {
+ consumer.OnBeginList();
+ for (auto& item : t->GetMembers()) {
+ consumer.OnBeginList();
+ consumer.OnScalarString(item.GetName());
+ AsYqlType(item.GetTypeRaw(), consumer, includeTags);
+ consumer.OnEndList();
+ }
+ consumer.OnEndList();
+ }
+ consumer.OnEndList();
+ },
+ [&consumer, includeTags](const TTupleType* t) {
+ consumer.OnBeginList();
+ consumer.OnScalarString("TupleType");
+ {
+ consumer.OnBeginList();
+ for (auto& item : t->GetElements()) {
+ AsYqlType(item.GetTypeRaw(), consumer, includeTags);
+ }
+ consumer.OnEndList();
+ }
+
+ consumer.OnEndList();
+ },
+ [&consumer, includeTags](const TVariantType* t) {
+ consumer.OnBeginList();
+ consumer.OnScalarString("VariantType");
+ AsYqlType(t->GetUnderlyingTypeRaw(), consumer, includeTags);
+ consumer.OnEndList();
+ },
+ [&consumer, includeTags](const TTaggedType* t) {
+ if (includeTags) {
+ consumer.OnBeginList();
+ consumer.OnScalarString("TaggedType");
+ consumer.OnScalarString(t->GetTag());
+ AsYqlType(t->GetItemTypeRaw(), consumer, includeTags);
+ consumer.OnEndList();
+ } else {
+ AsYqlType(t->GetItemTypeRaw(), consumer, includeTags);
+ }
+ },
+ });
+ }
+
+ TString AsYqlType(const NTi::TType* type, bool includeTags) {
+ auto result = TString();
+ auto writer = NYsonPull::MakePrettyTextWriter(NYsonPull::NOutput::FromString(&result), NYsonPull::EStreamType::Node);
+ writer.BeginStream();
+ AsYqlType(type, writer.GetConsumer(), includeTags);
+ writer.EndStream();
+ return result;
+ }
+
+ void AsYqlRowSpec(const TType* maybeTagged, NYsonPull::IConsumer& consumer, bool includeTags) {
+ auto* type = maybeTagged->StripTagsRaw();
+
+ if (!type->IsStruct()) {
+ ythrow TApiException() << "AsYqlRowSpec expected a struct type but got " << type->GetTypeName();
+ }
+
+ consumer.OnBeginMap();
+ consumer.OnKey("StrictSchema");
+ consumer.OnScalarBoolean(true);
+ consumer.OnKey("Type");
+ AsYqlType(type, consumer, includeTags);
+ consumer.OnEndMap();
+ }
+
+ TString AsYqlRowSpec(const NTi::TType* type, bool includeTags) {
+ auto result = TString();
+ auto writer = NYsonPull::MakePrettyTextWriter(NYsonPull::NOutput::FromString(&result), NYsonPull::EStreamType::Node);
+ writer.BeginStream();
+ AsYqlRowSpec(type, writer.GetConsumer(), includeTags);
+ writer.EndStream();
+ return result;
+ }
+
+ void AsYtSchema(const TType* maybeTagged, NYsonPull::IConsumer& consumer, bool failOnEmptyStruct) {
+ auto* type = maybeTagged->StripTagsRaw();
+
+ if (!type->IsStruct()) {
+ ythrow TApiException() << "AsYtSchema expected a struct type but got " << type->GetTypeName();
+ }
+
+ auto* structType = type->AsStructRaw();
+
+ if (structType->GetMembers().empty()) {
+ if (failOnEmptyStruct) {
+ ythrow TApiException() << "AsYtSchema expected a non-empty struct";
+ }
+
+ AsYtSchema(Struct({{"_yql_fake_column", Optional(Bool())}}).Get(), consumer);
+ return;
+ }
+
+ consumer.OnBeginAttributes();
+
+ consumer.OnKey("strict");
+ consumer.OnScalarBoolean(true);
+
+ consumer.OnKey("unique_keys");
+ consumer.OnScalarBoolean(false);
+
+ consumer.OnEndAttributes();
+
+ consumer.OnBeginList();
+ for (auto& item : structType->GetMembers()) {
+ auto* itemType = item.GetTypeRaw()->StripTagsRaw();
+
+ bool required = true;
+
+ if (itemType->IsOptional()) {
+ // toplevel optionals make non-required columns
+ itemType = itemType->AsOptionalRaw()->GetItemTypeRaw();
+ required = false;
+ }
+
+ TStringBuf typeString = itemType->VisitRaw(TOverloaded{
+ [](const TVoidType*) -> TStringBuf { return "any"; },
+ [](const TNullType*) -> TStringBuf { return "any"; },
+ [](const TBoolType*) -> TStringBuf { return "boolean"; },
+ [](const TInt8Type*) -> TStringBuf { return "int8"; },
+ [](const TInt16Type*) -> TStringBuf { return "int16"; },
+ [](const TInt32Type*) -> TStringBuf { return "int32"; },
+ [](const TInt64Type*) -> TStringBuf { return "int64"; },
+ [](const TUint8Type*) -> TStringBuf { return "uint8"; },
+ [](const TUint16Type*) -> TStringBuf { return "uint16"; },
+ [](const TUint32Type*) -> TStringBuf { return "uint32"; },
+ [](const TUint64Type*) -> TStringBuf { return "uint64"; },
+ [](const TFloatType*) -> TStringBuf { return "double"; },
+ [](const TDoubleType*) -> TStringBuf { return "double"; },
+ [](const TStringType*) -> TStringBuf { return "string"; },
+ [](const TUtf8Type*) -> TStringBuf { return "utf8"; },
+ [](const TDateType*) -> TStringBuf { return "uint16"; },
+ [](const TDatetimeType*) -> TStringBuf { return "uint32"; },
+ [](const TTimestampType*) -> TStringBuf { return "uint64"; },
+ [](const TTzDateType*) -> TStringBuf { return "string"; },
+ [](const TTzDatetimeType*) -> TStringBuf { return "string"; },
+ [](const TTzTimestampType*) -> TStringBuf { return "string"; },
+ [](const TIntervalType*) -> TStringBuf { return "int64"; },
+ [](const TJsonType*) -> TStringBuf { return "string"; },
+ [](const TYsonType*) -> TStringBuf { return "any"; },
+ [](const TUuidType*) -> TStringBuf { return "string"; },
+ [](const TDecimalType*) -> TStringBuf { return "string"; },
+ [](const TOptionalType*) -> TStringBuf { return "any"; },
+ [](const TListType*) -> TStringBuf { return "any"; },
+ [](const TDictType*) -> TStringBuf { return "any"; },
+ [](const TStructType*) -> TStringBuf { return "any"; },
+ [](const TTupleType*) -> TStringBuf { return "any"; },
+ [](const TVariantType*) -> TStringBuf { return "any"; },
+ [](const TTaggedType*) -> TStringBuf { return "any"; },
+ });
+
+ if (typeString == "any") {
+ // columns of type `any` cannot be required
+ required = false;
+ }
+
+ {
+ consumer.OnBeginMap();
+
+ consumer.OnKey("name");
+ consumer.OnScalarString(item.GetName());
+
+ consumer.OnKey("required");
+ consumer.OnScalarBoolean(required);
+
+ consumer.OnKey("type");
+ consumer.OnScalarString(typeString);
+
+ consumer.OnEndMap();
+ }
+ }
+ consumer.OnEndList();
+ }
+
+ TString AsYtSchema(const NTi::TType* type, bool failOnEmptyStruct) {
+ auto result = TString();
+ auto writer = NYsonPull::MakePrettyTextWriter(NYsonPull::NOutput::FromString(&result), NYsonPull::EStreamType::Node);
+ writer.BeginStream();
+ AsYtSchema(type, writer.GetConsumer(), failOnEmptyStruct);
+ writer.EndStream();
+ return result;
+ }
+}
diff --git a/library/cpp/type_info/type_io.h b/library/cpp/type_info/type_io.h
new file mode 100644
index 0000000000..e400414cff
--- /dev/null
+++ b/library/cpp/type_info/type_io.h
@@ -0,0 +1,115 @@
+#pragma once
+
+//! @file type_io.h
+//!
+//! Utilities for serializing and deserializing type instances.
+
+#include "type.h"
+
+#include <library/cpp/yson_pull/yson.h>
+
+namespace NTi::NIo {
+ /// Load type from a serialized representation.
+ ///
+ /// Serialization uses YSON (either binary or text). Contents are described in the [docs page].
+ ///
+ /// Throws `TTypeDeserializationException` if input is not valid.
+ ///
+ /// [docs page]: https://a.yandex-team.ru/arc/trunk/arcadia/library/cpp/type_info/docs/types_serialization.md
+ ///
+ /// @param factory factory that will be used to allocate new type. Technically, type will be deserialized into
+ /// a temporary pool factory and then adopted into a given one.
+ /// @param reader yson pull reader that'll be used to read types.
+ /// @param deduplicate use deduplication while creating new types. See `NTi::PoolFactory` function for more info.
+ /// @{
+ TTypePtr DeserializeYson(ITypeFactory& factory, NYsonPull::TReader& reader, bool deduplicate = true);
+ TTypePtr DeserializeYson(ITypeFactory& factory, TStringBuf data, bool deduplicate = true);
+ TTypePtr DeserializeYson(ITypeFactory& factory, IInputStream& input, bool deduplicate = true);
+ /// @}
+
+ /// Like `Deserialize`, but returns a raw pointer.
+ /// @{
+ const TType* DeserializeYsonRaw(IPoolTypeFactory& factory, NYsonPull::TReader& reader);
+ const TType* DeserializeYsonRaw(IPoolTypeFactory& factory, TStringBuf data);
+ const TType* DeserializeYsonRaw(IPoolTypeFactory& factory, IInputStream& input);
+ /// @}
+
+ /// Like `Deserialize`, but allows deserializing multiple types from the same reader.
+ ///
+ /// This function takes a reader created with `NYsonPull::EStreamType::ListFragment` mode. It reads a type,
+ /// but doesn't fails if there is no `BeginStream` event. If the reader is empty, it returns nullptr.
+ ///
+ /// This function mirrors `SerializeMultiple`. Call it multiple times on the same reader to read multiple types.
+ const TType* DeserializeYsonMultipleRaw(IPoolTypeFactory& factory, NYsonPull::TReader& reader);
+
+ /// Serialize this type info.
+ ///
+ /// Serialization uses YSON (either binary or text). Contents are described in the [RFC].
+ ///
+ /// [RFC]: https://a.yandex-team.ru/arc/trunk/arcadia/logfeller/mvp/docs/types_serialization.md
+ ///
+ /// @param humanReadable use pretty textual format instead of a binary one.
+ /// @param includeTags when disabled, tagged types will be removed from the result, only tagged type contents
+ /// will be dumped. For example, `Tagged<'Url', String>` will be rendered as just `String`.
+ /// This is useful if you only care about physical layout of a type and don't want to export
+ /// any semantical meaning.
+ /// @{
+ void SerializeYson(const TType* type, NYsonPull::IConsumer& consumer, bool includeTags = true);
+ void SerializeYson(const TType* type, IOutputStream& stream, bool humanReadable = false, bool includeTags = true);
+ TString SerializeYson(const TType* type, bool humanReadable = false, bool includeTags = true);
+ /// @}
+
+ /// Like `Serialize`, but allows serializing multiple types into the same consumer.
+ ///
+ /// This function takes a consumer created with `NYsonPull::EStreamType::ListFragment` mode. It writes type,
+ /// but doesn't emit the `BeginStream` and `EndStream` commands.
+ ///
+ /// Call this function multiple times on the same consumer to write multiple types. Note that you must emit
+ /// the `BeginStream` and `EndStream` commands to the consumer yourself.
+ void SerializeYsonMultiple(const TType* type, NYsonPull::IConsumer& consumer, bool includeTags = true);
+
+ /// Convert type to the lisp-like representation used in YQL row specs.
+ ///
+ /// TODO: move this code to yql/
+ ///
+ /// @param includeTags when disabled, tagged types will be removed from the result, only tagged type contents
+ /// will be dumped. For example, `Tagged<'Url', String>` will be rendered as just `String`.
+ /// This is useful if you only care about physical layout of a type and don't want to export
+ /// any semantic meaning.
+ /// @{
+ void AsYqlType(const TType* type, NYsonPull::IConsumer& consumer, bool includeTags = true);
+ TString AsYqlType(const TType* type, bool includeTags = true);
+ /// @}
+
+ /// Generate a strict YQL row spec. Toplevel tags will be ignored.
+ ///
+ /// Throws `TApiException` if the type is not a (possibly tagged) struct.
+ ///
+ /// TODO: move this code to yql/
+ ///
+ /// @param type type that'll be converted to YQL row spec.
+ /// @param consumer yson pull consumer. Attention: `OnBeginStream` and `OnEndStream` should be emitted manually
+ /// before and after calling this function.
+ /// @param includeTags same as in `TType::AsYqlType`.
+ /// @{
+ void AsYqlRowSpec(const TType* type, NYsonPull::IConsumer& consumer, bool includeTags = true);
+ TString AsYqlRowSpec(const TType* type, bool includeTags = true);
+ /// @}
+
+ /// Generate a strict YT schema (only types are exported, no index/sorting information).
+ ///
+ /// Throws `TApiException` if the type is not a (possibly tagged) struct.
+ ///
+ /// The schema is generated according to the translation rules of YQL types, i.e. container types translate
+ /// to `Any`.
+ ///
+ /// TODO: move this code to mapreduce/yt/
+ ///
+ /// @param failOnEmptyStruct if true, will throw `TApiException` if called on a struct with no fields;
+ /// if false, will emit a strict YT schema with a single column `'_yql_fake_column'`
+ /// of type `Optional<Bool>` (this is how YQL handles empty tables).
+ /// @{
+ void AsYtSchema(const TType* type, NYsonPull::IConsumer& consumer, bool failOnEmptyStruct = true);
+ TString AsYtSchema(const TType* type, bool failOnEmptyStruct = true);
+ /// @}
+}
diff --git a/library/cpp/type_info/type_list.cpp b/library/cpp/type_info/type_list.cpp
new file mode 100644
index 0000000000..b741b2858b
--- /dev/null
+++ b/library/cpp/type_info/type_list.cpp
@@ -0,0 +1 @@
+#include "type_list.h"
diff --git a/library/cpp/type_info/type_list.h b/library/cpp/type_info/type_list.h
new file mode 100644
index 0000000000..c87c51918d
--- /dev/null
+++ b/library/cpp/type_info/type_list.h
@@ -0,0 +1,183 @@
+#pragma once
+
+//! @file type_list.h
+//!
+//! Enum with all type names that are included in the Common Type System.
+//!
+//!
+//! # Primitive and non-primitive types
+//!
+//! Some systems only work with primitive types, so we've split the enum in two: the first contains primitive types,
+//! and the second contains all types, including the primitive ones. This way systems that are only interested
+//! in primitives can stop handling containers and make all their switch-cases exhaustive.
+//!
+//! Consequently, the class hierarchy follows the same division: there is `NTi::TPrimitiveType`,
+//! from which all primitives are derived.
+//!
+//!
+//! # Enumerator values
+//!
+//! Enumerator values are implementation detail and should not be relied upon. In particular, use `NTi::ToTypeName`
+//! and `NTi::ToPrimitiveTypeName` to safely cast between `NTi::EPrimitiveTypeName` and `NTi::ETypeName`. Also, don't
+//! use enumerator values for serialization and deserialization — convert enumerator values to strings
+//! it you need persistence.
+
+#include <util/system/types.h>
+#include <util/generic/variant.h>
+
+namespace NTi {
+ /// Enum with names of all primitive types.
+ ///
+ /// See the file-level documentation.
+ enum class EPrimitiveTypeName : i32 {
+ Bool,
+
+ Int8,
+ Int16,
+ Int32,
+ Int64,
+ Uint8,
+ Uint16,
+ Uint32,
+ Uint64,
+
+ Float,
+ Double,
+
+ String,
+ Utf8,
+
+ Date,
+ Datetime,
+ Timestamp,
+ TzDate,
+ TzDatetime,
+ TzTimestamp,
+ Interval,
+
+ Decimal,
+ Json,
+ Yson,
+ Uuid,
+ };
+
+ /// Enum with names of all types, including primitives.
+ ///
+ /// See the file-level documentation.
+ enum class ETypeName : i32 {
+ //
+ // # Primitive types
+
+ Bool,
+
+ Int8,
+ Int16,
+ Int32,
+ Int64,
+ Uint8,
+ Uint16,
+ Uint32,
+ Uint64,
+
+ Float,
+ Double,
+
+ String,
+ Utf8,
+
+ Date,
+ Datetime,
+ Timestamp,
+ TzDate,
+ TzDatetime,
+ TzTimestamp,
+ Interval,
+
+ Decimal,
+ Json,
+ Yson,
+ Uuid,
+
+ FIRST_PRIMITIVE = Bool,
+ LAST_PRIMITIVE = Uuid,
+
+ //
+ // # Singular types
+
+ Void,
+ Null,
+
+ FIRST_SINGULAR = Void,
+ LAST_SINGULAR = Null,
+
+ //
+ // # Containers
+
+ Optional,
+ List,
+ Dict,
+ Struct,
+ Tuple,
+ Variant,
+ Tagged,
+
+ FIRST_CONTAINER = Optional,
+ LAST_CONTAINER = Tagged,
+ };
+
+ /// Return true if the given type is a primitive one.
+ ///
+ /// Primitive type is a type that have no type parameters and is not a singular one.
+ inline constexpr bool IsPrimitive(ETypeName typeName) {
+ return ETypeName::FIRST_PRIMITIVE <= typeName && typeName <= ETypeName::LAST_PRIMITIVE;
+ }
+
+ /// Return true if the given type is one of singular types.
+ ///
+ /// Singular type is a type that has only one instance and therefore carries no information,
+ /// i.e. occupy zero-length memory buffer.
+ inline constexpr bool IsSingular(ETypeName typeName) {
+ return ETypeName::FIRST_SINGULAR <= typeName && typeName <= ETypeName::LAST_SINGULAR;
+ }
+
+ /// Return true if the given type is one of containers.
+ ///
+ /// Container type is a type that has type parameters.
+ inline constexpr bool IsContainer(ETypeName typeName) {
+ return ETypeName::FIRST_CONTAINER <= typeName && typeName <= ETypeName::LAST_CONTAINER;
+ }
+
+ /// Return true if the given type has any type parameters.
+ inline constexpr bool HasTypeParameters(ETypeName typeName) {
+ return IsContainer(typeName);
+ }
+
+ /// Return true if the given type has any non-type parameters.
+ inline constexpr bool HasNonTypeParameters(ETypeName typeName) {
+ return typeName == ETypeName::Decimal;
+ }
+
+ /// Return true if the given type has any type or non-type parameters.
+ inline constexpr bool HasParameters(ETypeName typeName) {
+ return HasTypeParameters(typeName) || HasNonTypeParameters(typeName);
+ }
+
+ /// Safely cast `NTi::EPrimitiveTypeName` to `NTi::ETypeName`.
+ ///
+ /// Enumerator values should not relied upon, therefore users should not cast `NTi::EPrimitiveTypeName`
+ /// to `NTi::ETypeName` using `static_cast`.
+ inline constexpr ETypeName ToTypeName(EPrimitiveTypeName primitiveTypeName) {
+ // Note: there's a test in ut/type_list.cpp that checks this is a safe conversion
+ return static_cast<ETypeName>(primitiveTypeName);
+ }
+
+ /// Cast `NTi::ETypeName` to `NTi::EPrimitiveTypeName`, panic if the given type is not a primitive one.
+ ///
+ /// Enumerator values should not relied upon, therefore users should not cast `NTi::ETypeName`
+ /// to `NTi::EPrimitiveTypeName` using `static_cast`.
+ inline constexpr EPrimitiveTypeName ToPrimitiveTypeName(ETypeName typeName) {
+ Y_VERIFY(IsPrimitive(typeName));
+ // Note: there's a test in ut/type_list.cpp that checks this is a safe conversion
+ return static_cast<EPrimitiveTypeName>(typeName);
+ }
+}
diff --git a/library/cpp/type_info/ut/builder.cpp b/library/cpp/type_info/ut/builder.cpp
new file mode 100644
index 0000000000..43b7bb8c9b
--- /dev/null
+++ b/library/cpp/type_info/ut/builder.cpp
@@ -0,0 +1,125 @@
+#include <library/cpp/testing/unittest/gtest.h>
+
+#include <library/cpp/type_info/type_info.h>
+
+#include "utils.h"
+
+class Builder: public NTesting::TTest {
+public:
+ void SetUp() override {
+ F = NTi::PoolFactory(false);
+ }
+
+ void TearDown() override {
+ F.Reset();
+ }
+
+ NTi::IPoolTypeFactoryPtr F;
+};
+
+TEST_F(Builder, TaggedBuilder) {
+ auto builder = NTi::TTaggedBuilderRaw(*F);
+
+ ASSERT_FALSE(builder.CanBuild());
+ ASSERT_FALSE(builder.HasTag());
+ ASSERT_FALSE(builder.HasItem());
+
+ UNIT_ASSERT_EQUAL(builder.GetTag(), Nothing());
+ UNIT_ASSERT_EQUAL(builder.GetItem(), Nothing());
+
+ {
+ auto tag = TString("Url");
+ builder.SetTag(tag);
+ }
+
+ ASSERT_FALSE(builder.CanBuild());
+ ASSERT_TRUE(builder.HasTag());
+ ASSERT_FALSE(builder.HasItem());
+
+ UNIT_ASSERT_EQUAL(builder.GetTag(), MakeMaybe<TStringBuf>("Url"));
+ UNIT_ASSERT_EQUAL(builder.GetItem(), Nothing());
+
+ {
+ auto ty = NTi::String();
+ builder.SetItem(ty);
+ }
+
+ ASSERT_TRUE(builder.CanBuild());
+ ASSERT_TRUE(builder.HasTag());
+ ASSERT_TRUE(builder.HasItem());
+
+ UNIT_ASSERT_EQUAL(builder.GetTag(), MakeMaybe<TStringBuf>("Url"));
+ UNIT_ASSERT_EQUAL(builder.GetItem(), MakeMaybe<const NTi::TType*>(NTi::TStringType::InstanceRaw()));
+
+ {
+ auto tagged = builder.Build();
+ ASSERT_STRICT_EQ(tagged, NTi::Tagged(NTi::String(), "Url"));
+ }
+
+ ASSERT_TRUE(builder.CanBuild());
+
+ builder.Reset();
+
+ ASSERT_FALSE(builder.CanBuild());
+ ASSERT_FALSE(builder.HasTag());
+ ASSERT_FALSE(builder.HasItem());
+
+ UNIT_ASSERT_EQUAL(builder.GetTag(), Nothing());
+ UNIT_ASSERT_EQUAL(builder.GetItem(), Nothing());
+
+ builder.SetTag("T");
+ builder.SetItem(NTi::String());
+
+ ASSERT_TRUE(builder.CanBuild());
+ ASSERT_TRUE(builder.HasTag());
+ ASSERT_TRUE(builder.HasItem());
+
+ UNIT_ASSERT_EQUAL(builder.GetTag(), MakeMaybe<TStringBuf>("T"));
+ UNIT_ASSERT_EQUAL(builder.GetItem(), MakeMaybe<const NTi::TType*>(NTi::TStringType::InstanceRaw()));
+
+ builder.DiscardItem();
+
+ ASSERT_FALSE(builder.CanBuild());
+ ASSERT_TRUE(builder.HasTag());
+ ASSERT_FALSE(builder.HasItem());
+
+ UNIT_ASSERT_EQUAL(builder.GetTag(), MakeMaybe<TStringBuf>("T"));
+ UNIT_ASSERT_EQUAL(builder.GetItem(), Nothing());
+
+ builder.DiscardTag();
+
+ builder.SetTag("T");
+ builder.SetItem(NTi::String());
+
+ ASSERT_TRUE(builder.CanBuild());
+ ASSERT_TRUE(builder.HasTag());
+ ASSERT_TRUE(builder.HasItem());
+}
+
+TEST_F(Builder, TaggedBuilderChaining) {
+ auto builder = NTi::TTaggedBuilderRaw(*F)
+ .SetTag("Uuu")
+ .SetItem(NTi::Optional(NTi::String()));
+
+ ASSERT_STRICT_EQ(builder.Build(), NTi::Tagged(NTi::Optional(NTi::String()), "Uuu"));
+
+ builder = std::move(builder)
+ .DiscardTag()
+ .SetTag("Urls");
+
+ ASSERT_STRICT_EQ(builder.Build(), NTi::Tagged(NTi::Optional(NTi::String()), "Urls"));
+
+ builder = std::move(builder)
+ .DiscardItem()
+ .SetItem(F->ListRaw(F->StringRaw()));
+
+ ASSERT_STRICT_EQ(builder.Build(), NTi::Tagged(NTi::List(NTi::String()), "Urls"));
+
+ auto type = std::move(builder)
+ .Reset()
+ .SetTag("Url")
+ .SetItem(F->StringRaw())
+ .Build();
+
+ ASSERT_STRICT_EQ(type, NTi::Tagged(NTi::String(), "Url"));
+}
diff --git a/library/cpp/type_info/ut/test_data.cpp b/library/cpp/type_info/ut/test_data.cpp
new file mode 100644
index 0000000000..36944e7bc4
--- /dev/null
+++ b/library/cpp/type_info/ut/test_data.cpp
@@ -0,0 +1,91 @@
+#include <library/cpp/testing/unittest/gtest.h>
+#include <library/cpp/resource/resource.h>
+
+#include <library/cpp/type_info/type_info.h>
+#include <library/cpp/type_info/type_io.h>
+
+#include <util/string/strip.h>
+#include <util/string/split.h>
+
+using namespace NTi;
+
+std::vector<std::vector<TString>> ParseData(TStringBuf data, int expectedFieldsCount) {
+ TString noComments;
+ {
+ TMemoryInput in(data);
+ TString line;
+ while (in.ReadLine(line)) {
+ if (StripString(line).StartsWith('#')) {
+ continue;
+ }
+ noComments += line;
+ }
+ }
+
+ std::vector<std::vector<TString>> result;
+ for (TStringBuf record : StringSplitter(noComments).SplitByString(";;")) {
+ record = StripString(record);
+ if (record.Empty()) {
+ continue;
+ }
+ std::vector<TString> fields;
+ for (TStringBuf field : StringSplitter(record).SplitByString("::")) {
+ fields.emplace_back(StripString(field));
+ }
+ if (static_cast<int>(fields.size()) != expectedFieldsCount) {
+ ythrow yexception() << "Unexpected field count expected: " << expectedFieldsCount << " actual: " << fields.size();
+ }
+ result.push_back(fields);
+ }
+ return result;
+}
+
+TEST(TestData, GoodTypes) {
+ auto records = ParseData(NResource::Find("/good"), 2);
+
+ for (const auto& record : records) {
+ const auto& typeYson = record.at(0);
+ const auto& typeText = record.at(1);
+ TString context = TStringBuilder()
+ << "text: " << typeText << Endl
+ << "yson: " << typeYson << Endl;
+ auto wrapError = [&] (const std::exception& ex) {
+ return yexception() << "Unexpected error: " << ex.what() << '\n' << context;
+ };
+
+ TTypePtr type;
+ try {
+ type = NIo::DeserializeYson(*HeapFactory(), typeYson);
+ } catch (const std::exception& ex) {
+ ythrow wrapError(ex);
+ }
+ UNIT_ASSERT_VALUES_EQUAL_C(ToString(*type), typeText, context);
+
+ TTypePtr type2;
+ try {
+ auto yson2 = NIo::SerializeYson(type.Get(), true);
+ type2 = NIo::DeserializeYson(*HeapFactory(), yson2);
+ } catch (const std::exception& ex) {
+ ythrow wrapError(ex);
+ }
+ UNIT_ASSERT_VALUES_EQUAL_C(*type, *type2, context);
+ }
+}
+
+TEST(TestData, BadTypes) {
+ auto records = ParseData(NResource::Find("/bad"), 3);
+
+ for (const auto& record : records) {
+ const auto& typeYson = record.at(0);
+ const auto& exceptionMessage = record.at(1);
+
+ TString context = TStringBuilder()
+ << "exception: " << exceptionMessage << Endl
+ << "yson: " << typeYson << Endl;
+ UNIT_ASSERT_EXCEPTION_CONTAINS_C(
+ NIo::DeserializeYson(*HeapFactory(), typeYson),
+ yexception,
+ exceptionMessage,
+ context);
+ }
+} \ No newline at end of file
diff --git a/library/cpp/type_info/ut/type_basics.cpp b/library/cpp/type_info/ut/type_basics.cpp
new file mode 100644
index 0000000000..83996d63d3
--- /dev/null
+++ b/library/cpp/type_info/ut/type_basics.cpp
@@ -0,0 +1,381 @@
+#include <library/cpp/testing/unittest/gtest.h>
+
+#include <library/cpp/type_info/type_info.h>
+
+#include "utils.h"
+
+TEST_TF(TypeBasics, Void) {
+ auto t = f.Void();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Void);
+ ASSERT_TRUE(t->IsVoid());
+}
+
+TEST_TF(TypeBasics, Bool) {
+ auto t = f.Bool();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Bool);
+ ASSERT_TRUE(t->IsBool());
+}
+
+TEST_TF(TypeBasics, Int8) {
+ auto t = f.Int8();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Int8);
+ ASSERT_TRUE(t->IsInt8());
+}
+
+TEST_TF(TypeBasics, Int16) {
+ auto t = f.Int16();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Int16);
+ ASSERT_TRUE(t->IsInt16());
+}
+
+TEST_TF(TypeBasics, Int32) {
+ auto t = f.Int32();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Int32);
+ ASSERT_TRUE(t->IsInt32());
+}
+
+TEST_TF(TypeBasics, Int64) {
+ auto t = f.Int64();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Int64);
+ ASSERT_TRUE(t->IsInt64());
+}
+
+TEST_TF(TypeBasics, Uint8) {
+ auto t = f.Uint8();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Uint8);
+ ASSERT_TRUE(t->IsUint8());
+}
+
+TEST_TF(TypeBasics, Uint16) {
+ auto t = f.Uint16();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Uint16);
+ ASSERT_TRUE(t->IsUint16());
+}
+
+TEST_TF(TypeBasics, Uint32) {
+ auto t = f.Uint32();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Uint32);
+ ASSERT_TRUE(t->IsUint32());
+}
+
+TEST_TF(TypeBasics, Uint64) {
+ auto t = f.Uint64();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Uint64);
+ ASSERT_TRUE(t->IsUint64());
+}
+
+TEST_TF(TypeBasics, Float) {
+ auto t = f.Float();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Float);
+ ASSERT_TRUE(t->IsFloat());
+}
+
+TEST_TF(TypeBasics, Double) {
+ auto t = f.Double();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Double);
+ ASSERT_TRUE(t->IsDouble());
+}
+
+TEST_TF(TypeBasics, String) {
+ auto t = f.String();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::String);
+ ASSERT_TRUE(t->IsString());
+}
+
+TEST_TF(TypeBasics, Utf8) {
+ auto t = f.Utf8();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Utf8);
+ ASSERT_TRUE(t->IsUtf8());
+}
+
+TEST_TF(TypeBasics, Date) {
+ auto t = f.Date();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Date);
+ ASSERT_TRUE(t->IsDate());
+}
+
+TEST_TF(TypeBasics, Datetime) {
+ auto t = f.Datetime();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Datetime);
+ ASSERT_TRUE(t->IsDatetime());
+}
+
+TEST_TF(TypeBasics, Timestamp) {
+ auto t = f.Timestamp();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Timestamp);
+ ASSERT_TRUE(t->IsTimestamp());
+}
+
+TEST_TF(TypeBasics, TzDate) {
+ auto t = f.TzDate();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::TzDate);
+ ASSERT_TRUE(t->IsTzDate());
+}
+
+TEST_TF(TypeBasics, TzDatetime) {
+ auto t = f.TzDatetime();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::TzDatetime);
+ ASSERT_TRUE(t->IsTzDatetime());
+}
+
+TEST_TF(TypeBasics, TzTimestamp) {
+ auto t = f.TzTimestamp();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::TzTimestamp);
+ ASSERT_TRUE(t->IsTzTimestamp());
+}
+
+TEST_TF(TypeBasics, Interval) {
+ auto t = f.Interval();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Interval);
+ ASSERT_TRUE(t->IsInterval());
+}
+
+TEST_TF(TypeBasics, Decimal) {
+ auto t = f.Decimal(20, 10);
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Decimal);
+ ASSERT_TRUE(t->IsDecimal());
+ ASSERT_EQ(t->GetPrecision(), 20);
+ ASSERT_EQ(t->GetScale(), 10);
+}
+
+TEST_TF(TypeBasics, Json) {
+ auto t = f.Json();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Json);
+ ASSERT_TRUE(t->IsJson());
+}
+
+TEST_TF(TypeBasics, Yson) {
+ auto t = f.Yson();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Yson);
+ ASSERT_TRUE(t->IsYson());
+}
+
+TEST_TF(TypeBasics, Uuid) {
+ auto t = f.Uuid();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Uuid);
+ ASSERT_TRUE(t->IsUuid());
+}
+
+TEST_TF(TypeBasics, Optional) {
+ auto t = f.Optional(f.Void());
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Optional);
+ ASSERT_TRUE(t->IsOptional());
+ ASSERT_TRUE(t->GetItemType()->IsVoid());
+ ASSERT_EQ(t->GetItemType().Get(), t->GetItemTypeRaw());
+}
+
+TEST_TF(TypeBasics, List) {
+ auto t = f.List(f.Void());
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::List);
+ ASSERT_TRUE(t->IsList());
+ ASSERT_TRUE(t->GetItemType()->IsVoid());
+ ASSERT_EQ(t->GetItemType().Get(), t->GetItemTypeRaw());
+}
+
+TEST_TF(TypeBasics, Dict) {
+ auto t = f.Dict(f.Void(), f.String());
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Dict);
+ ASSERT_TRUE(t->IsDict());
+ ASSERT_TRUE(t->GetKeyType()->IsVoid());
+ ASSERT_EQ(t->GetKeyType().Get(), t->GetKeyTypeRaw());
+ ASSERT_TRUE(t->GetValueType()->IsString());
+ ASSERT_EQ(t->GetValueType().Get(), t->GetValueTypeRaw());
+}
+
+TEST_TF(TypeBasics, EmptyStruct) {
+ auto t = f.Struct({});
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Struct);
+ ASSERT_TRUE(t->IsStruct());
+ ASSERT_FALSE(t->GetName().Defined());
+ ASSERT_EQ(t->GetMembers().size(), 0);
+}
+
+TEST_TF(TypeBasics, NamedEmptyStruct) {
+ auto t = f.Struct("S", {});
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Struct);
+ ASSERT_TRUE(t->IsStruct());
+ ASSERT_TRUE(t->GetName().Defined());
+ ASSERT_EQ(t->GetName().GetRef(), "S");
+ ASSERT_EQ(t->GetMembers().size(), 0);
+}
+
+TEST_TF(TypeBasics, Struct) {
+ auto t = f.Struct({{"a", f.Int64()}, {"b", f.Int8()}});
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Struct);
+ ASSERT_TRUE(t->IsStruct());
+ ASSERT_FALSE(t->GetName().Defined());
+ ASSERT_EQ(t->GetMembers().size(), 2);
+ ASSERT_EQ(t->GetMembers()[0].GetName(), "a");
+ ASSERT_TRUE(t->GetMembers()[0].GetTypeRaw()->IsInt64());
+ ASSERT_EQ(t->GetMembers()[0].GetType().Get(), t->GetMembers()[0].GetTypeRaw());
+ ASSERT_EQ(t->GetMembers()[1].GetName(), "b");
+ ASSERT_TRUE(t->GetMembers()[1].GetTypeRaw()->IsInt8());
+ ASSERT_EQ(t->GetMembers()[1].GetType().Get(), t->GetMembers()[1].GetTypeRaw());
+}
+
+TEST_TF(TypeBasics, NamedStruct) {
+ auto t = f.Struct("S", {{"a", f.Int64()}, {"b", f.Int8()}});
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Struct);
+ ASSERT_TRUE(t->IsStruct());
+ ASSERT_TRUE(t->GetName().Defined());
+ ASSERT_EQ(t->GetName().GetRef(), "S");
+ ASSERT_EQ(t->GetMembers().size(), 2);
+ ASSERT_EQ(t->GetMembers()[0].GetName(), "a");
+ ASSERT_TRUE(t->GetMembers()[0].GetTypeRaw()->IsInt64());
+ ASSERT_EQ(t->GetMembers()[0].GetType().Get(), t->GetMembers()[0].GetTypeRaw());
+ ASSERT_EQ(t->GetMembers()[1].GetName(), "b");
+ ASSERT_TRUE(t->GetMembers()[1].GetTypeRaw()->IsInt8());
+ ASSERT_EQ(t->GetMembers()[1].GetType().Get(), t->GetMembers()[1].GetTypeRaw());
+}
+
+TEST_TF(TypeBasics, EmptyTuple) {
+ auto t = f.Tuple({});
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Tuple);
+ ASSERT_TRUE(t->IsTuple());
+ ASSERT_FALSE(t->GetName().Defined());
+ ASSERT_EQ(t->GetElements().size(), 0);
+}
+
+TEST_TF(TypeBasics, NamedEmptyTuple) {
+ auto t = f.Tuple("T", {});
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Tuple);
+ ASSERT_TRUE(t->IsTuple());
+ ASSERT_TRUE(t->GetName().Defined());
+ ASSERT_EQ(t->GetName().GetRef(), "T");
+ ASSERT_EQ(t->GetElements().size(), 0);
+}
+
+TEST_TF(TypeBasics, Tuple) {
+ auto t = f.Tuple({{f.Int64()}, {f.Int8()}});
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Tuple);
+ ASSERT_TRUE(t->IsTuple());
+ ASSERT_FALSE(t->GetName().Defined());
+ ASSERT_EQ(t->GetElements().size(), 2);
+ ASSERT_TRUE(t->GetElements()[0].GetTypeRaw()->IsInt64());
+ ASSERT_EQ(t->GetElements()[0].GetType().Get(), t->GetElements()[0].GetTypeRaw());
+ ASSERT_TRUE(t->GetElements()[1].GetTypeRaw()->IsInt8());
+ ASSERT_EQ(t->GetElements()[1].GetType().Get(), t->GetElements()[1].GetTypeRaw());
+}
+
+TEST_TF(TypeBasics, NamedTuple) {
+ auto t = f.Tuple("T", {{f.Int64()}, {f.Int8()}});
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Tuple);
+ ASSERT_TRUE(t->IsTuple());
+ ASSERT_TRUE(t->GetName().Defined());
+ ASSERT_EQ(t->GetName().GetRef(), "T");
+ ASSERT_EQ(t->GetElements().size(), 2);
+ ASSERT_TRUE(t->GetElements()[0].GetTypeRaw()->IsInt64());
+ ASSERT_EQ(t->GetElements()[0].GetType().Get(), t->GetElements()[0].GetTypeRaw());
+ ASSERT_TRUE(t->GetElements()[1].GetTypeRaw()->IsInt8());
+ ASSERT_EQ(t->GetElements()[1].GetType().Get(), t->GetElements()[1].GetTypeRaw());
+}
+
+TEST_TF(TypeBasics, VariantOverStruct) {
+ auto t = f.Variant(f.Struct("Inner", {{"x", f.Void()}}));
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Variant);
+ ASSERT_TRUE(t->IsVariant());
+ ASSERT_FALSE(t->GetName().Defined());
+ ASSERT_TRUE(t->IsVariantOverStruct());
+ ASSERT_FALSE(t->IsVariantOverTuple());
+ ASSERT_EQ(t->GetUnderlyingType()->AsStruct()->GetName().GetRef(), "Inner");
+ ASSERT_EQ(t->GetUnderlyingType().Get(), t->GetUnderlyingTypeRaw());
+}
+
+TEST_TF(TypeBasics, NamedVariantOverStruct) {
+ auto t = f.Variant("V", f.Struct("Inner", {{"x", f.Void()}}));
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Variant);
+ ASSERT_TRUE(t->IsVariant());
+ ASSERT_TRUE(t->GetName().Defined());
+ ASSERT_EQ(t->GetName().GetRef(), "V");
+ ASSERT_TRUE(t->IsVariantOverStruct());
+ ASSERT_FALSE(t->IsVariantOverTuple());
+ ASSERT_EQ(t->GetUnderlyingType()->AsStruct()->GetName().GetRef(), "Inner");
+ ASSERT_EQ(t->GetUnderlyingType().Get(), t->GetUnderlyingTypeRaw());
+}
+
+TEST_TF(TypeBasics, VariantOverTuple) {
+ auto t = f.Variant(f.Tuple("Inner", {{f.Void()}}));
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Variant);
+ ASSERT_TRUE(t->IsVariant());
+ ASSERT_FALSE(t->GetName().Defined());
+ ASSERT_FALSE(t->IsVariantOverStruct());
+ ASSERT_TRUE(t->IsVariantOverTuple());
+ ASSERT_EQ(t->GetUnderlyingType()->AsTuple()->GetName().GetRef(), "Inner");
+ ASSERT_EQ(t->GetUnderlyingType().Get(), t->GetUnderlyingTypeRaw());
+}
+
+TEST_TF(TypeBasics, NamedVariantOverTuple) {
+ auto t = f.Variant("V", f.Tuple("Inner", {{f.Void()}}));
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Variant);
+ ASSERT_TRUE(t->IsVariant());
+ ASSERT_TRUE(t->GetName().Defined());
+ ASSERT_EQ(t->GetName().GetRef(), "V");
+ ASSERT_FALSE(t->IsVariantOverStruct());
+ ASSERT_TRUE(t->IsVariantOverTuple());
+ ASSERT_EQ(t->GetUnderlyingType()->AsTuple()->GetName().GetRef(), "Inner");
+ ASSERT_EQ(t->GetUnderlyingType().Get(), t->GetUnderlyingTypeRaw());
+}
+
+TEST_TF(TypeBasics, Tagged) {
+ auto t = f.Tagged(f.Void(), "T");
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Tagged);
+ ASSERT_TRUE(t->IsTagged());
+ ASSERT_EQ(t->GetTag(), "T");
+ ASSERT_TRUE(t->GetItemType()->IsVoid());
+ ASSERT_EQ(t->GetItemType().Get(), t->GetItemTypeRaw());
+}
+
+TEST_TF(TypeBasics, EmptyStructLookupByName) {
+ auto s = f.Struct({});
+
+ ASSERT_FALSE(s->HasMember("item"));
+ ASSERT_EQ(s->GetMemberIndex("item"), -1);
+
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ [=]() {
+ s->GetMember("item");
+ }(),
+ NTi::TItemNotFound, "no item named 'item'");
+}
+
+TEST_TF(TypeBasics, StructLookupByName) {
+ auto s = f.Struct({
+ {"a", f.Void()},
+ {"field2", f.String()},
+ {"field1", f.Utf8()},
+ {"", f.Null()},
+ });
+
+ ASSERT_TRUE(s->HasMember("a"));
+ ASSERT_EQ(s->GetMemberIndex("a"), 0);
+
+ ASSERT_TRUE(s->HasMember("field2"));
+ ASSERT_EQ(s->GetMemberIndex("field2"), 1);
+
+ ASSERT_TRUE(s->HasMember("field1"));
+ ASSERT_EQ(s->GetMemberIndex("field1"), 2);
+
+ ASSERT_TRUE(s->HasMember(""));
+ ASSERT_EQ(s->GetMemberIndex(""), 3);
+
+ ASSERT_FALSE(s->HasMember("b"));
+ ASSERT_EQ(s->GetMemberIndex("b"), -1);
+
+ ASSERT_EQ(s->GetMember("a").GetName(), "a");
+ ASSERT_TRUE(s->GetMember("a").GetTypeRaw()->IsVoid());
+
+ ASSERT_EQ(s->GetMember("field2").GetName(), "field2");
+ ASSERT_TRUE(s->GetMember("field2").GetTypeRaw()->IsString());
+
+ ASSERT_EQ(s->GetMember("field1").GetName(), "field1");
+ ASSERT_TRUE(s->GetMember("field1").GetTypeRaw()->IsUtf8());
+
+ ASSERT_EQ(s->GetMember("").GetName(), "");
+ ASSERT_TRUE(s->GetMember("").GetTypeRaw()->IsNull());
+
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ [=]() {
+ s->GetMember("b");
+ }(),
+ NTi::TItemNotFound, "no item named 'b'");
+}
diff --git a/library/cpp/type_info/ut/type_complexity_ut.cpp b/library/cpp/type_info/ut/type_complexity_ut.cpp
new file mode 100644
index 0000000000..3b4f6e5372
--- /dev/null
+++ b/library/cpp/type_info/ut/type_complexity_ut.cpp
@@ -0,0 +1,33 @@
+#include <library/cpp/testing/unittest/gtest.h>
+
+#include <library/cpp/type_info/type_complexity.h>
+#include <library/cpp/type_info/type_constructors.h>
+
+using namespace NTi;
+
+TEST(TypeComplexity, Test)
+{
+ EXPECT_EQ(ComputeTypeComplexity(Int64()), 1);
+ EXPECT_EQ(ComputeTypeComplexity(String()), 1);
+ EXPECT_EQ(ComputeTypeComplexity(Null()), 1);
+ EXPECT_EQ(ComputeTypeComplexity(Decimal(4, 2)), 1);
+
+ EXPECT_EQ(ComputeTypeComplexity(Optional(Utf8())), 2);
+ EXPECT_EQ(ComputeTypeComplexity(List(Json())), 2);
+ EXPECT_EQ(ComputeTypeComplexity(Tagged(String(), "jpeg")), 2);
+ EXPECT_EQ(ComputeTypeComplexity(Dict(String(), Optional(Int64()))), 4);
+ EXPECT_EQ(ComputeTypeComplexity(Struct({
+ {"a", String()},
+ {"b", List(Optional(Int64()))},
+ })), 5);
+ EXPECT_EQ(ComputeTypeComplexity(Tuple({{Float()}, {Float()}})), 3);
+
+ EXPECT_EQ(ComputeTypeComplexity(Variant(Struct({
+ {"a", String()},
+ {"b", Int64()},
+ }))), 3);
+ EXPECT_EQ(ComputeTypeComplexity(Tuple({
+ {String()},
+ {Int64()},
+ })), 3);
+} \ No newline at end of file
diff --git a/library/cpp/type_info/ut/type_constraints.cpp b/library/cpp/type_info/ut/type_constraints.cpp
new file mode 100644
index 0000000000..0f56bd89b0
--- /dev/null
+++ b/library/cpp/type_info/ut/type_constraints.cpp
@@ -0,0 +1,53 @@
+#include <library/cpp/testing/unittest/gtest.h>
+
+#include <library/cpp/type_info/type_info.h>
+
+#include "utils.h"
+
+TEST_TF(TypeConstraints, DecimalScale) {
+ UNIT_ASSERT_EXCEPTION_CONTAINS([&f]() {
+ f.Decimal(20, 21);
+ }(),
+ NTi::TIllegalTypeException, "decimal scale 21 should be no greater than decimal precision 20");
+}
+
+TEST_TF(TypeConstraints, StructDuplicateItem) {
+ UNIT_ASSERT_EXCEPTION_CONTAINS([&f]() {
+ f.Struct({{"a", f.Void()}, {"a", f.String()}});
+ }(),
+ NTi::TIllegalTypeException, "duplicate struct item 'a'");
+
+ UNIT_ASSERT_EXCEPTION_CONTAINS([&f]() {
+ f.Struct({{"a", f.Void()}, {"b", f.Bool()}, {"a", f.String()}});
+ }(),
+ NTi::TIllegalTypeException, "duplicate struct item 'a'");
+}
+
+TEST_TF(TypeConstraints, StructEmpty) {
+ f.Struct({}); // empty structs are ok, this should not fail
+}
+
+TEST_TF(TypeConstraints, TupleEmpty) {
+ f.Tuple({}); // empty tuples are ok, this should not fail
+}
+
+TEST_TF(TypeConstraints, VariantStructEmpty) {
+ UNIT_ASSERT_EXCEPTION_CONTAINS([&f]() {
+ f.Variant(f.Struct({}));
+ }(),
+ NTi::TIllegalTypeException, "variant should contain at least one alternative");
+}
+
+TEST_TF(TypeConstraints, VariantTupleEmpty) {
+ UNIT_ASSERT_EXCEPTION_CONTAINS([&f]() {
+ f.Variant(f.Tuple({}));
+ }(),
+ NTi::TIllegalTypeException, "variant should contain at least one alternative");
+}
+
+TEST_TF(TypeConstraints, VariantWrongInnerType) {
+ UNIT_ASSERT_EXCEPTION_CONTAINS([&f]() {
+ f.Variant(f.String());
+ }(),
+ NTi::TIllegalTypeException, "variants can only contain structs and tuples, got String instead");
+}
diff --git a/library/cpp/type_info/ut/type_deserialize.cpp b/library/cpp/type_info/ut/type_deserialize.cpp
new file mode 100644
index 0000000000..9e93a26bee
--- /dev/null
+++ b/library/cpp/type_info/ut/type_deserialize.cpp
@@ -0,0 +1,528 @@
+#include <library/cpp/testing/unittest/gtest.h>
+
+#include <library/cpp/type_info/type_info.h>
+
+#include <library/cpp/yson_pull/yson.h>
+
+#include "utils.h"
+
+TEST(TypeDeserialize, Void) {
+ ASSERT_DESERIALIZED_EQ(NTi::Void(), R"(void)");
+ ASSERT_DESERIALIZED_EQ(NTi::Void(), R"({type_name=void})");
+}
+
+TEST(TypeDeserialize, Null) {
+ ASSERT_DESERIALIZED_EQ(NTi::Null(), R"(null)");
+ ASSERT_DESERIALIZED_EQ(NTi::Null(), R"({type_name=null})");
+}
+
+TEST(TypeDeserialize, Bool) {
+ ASSERT_DESERIALIZED_EQ(NTi::Bool(), R"(bool)");
+ ASSERT_DESERIALIZED_EQ(NTi::Bool(), R"({type_name=bool})");
+}
+
+TEST(TypeDeserialize, Int8) {
+ ASSERT_DESERIALIZED_EQ(NTi::Int8(), R"(int8)");
+ ASSERT_DESERIALIZED_EQ(NTi::Int8(), R"({type_name=int8})");
+}
+
+TEST(TypeDeserialize, Int16) {
+ ASSERT_DESERIALIZED_EQ(NTi::Int16(), R"(int16)");
+ ASSERT_DESERIALIZED_EQ(NTi::Int16(), R"({type_name=int16})");
+}
+
+TEST(TypeDeserialize, Int32) {
+ ASSERT_DESERIALIZED_EQ(NTi::Int32(), R"(int32)");
+ ASSERT_DESERIALIZED_EQ(NTi::Int32(), R"({type_name=int32})");
+}
+
+TEST(TypeDeserialize, Int64) {
+ ASSERT_DESERIALIZED_EQ(NTi::Int64(), R"(int64)");
+ ASSERT_DESERIALIZED_EQ(NTi::Int64(), R"({type_name=int64})");
+}
+
+TEST(TypeDeserialize, Uint8) {
+ ASSERT_DESERIALIZED_EQ(NTi::Uint8(), R"(uint8)");
+ ASSERT_DESERIALIZED_EQ(NTi::Uint8(), R"({type_name=uint8})");
+}
+
+TEST(TypeDeserialize, Uint16) {
+ ASSERT_DESERIALIZED_EQ(NTi::Uint16(), R"(uint16)");
+ ASSERT_DESERIALIZED_EQ(NTi::Uint16(), R"({type_name=uint16})");
+}
+
+TEST(TypeDeserialize, Uint32) {
+ ASSERT_DESERIALIZED_EQ(NTi::Uint32(), R"(uint32)");
+ ASSERT_DESERIALIZED_EQ(NTi::Uint32(), R"({type_name=uint32})");
+}
+
+TEST(TypeDeserialize, Uint64) {
+ ASSERT_DESERIALIZED_EQ(NTi::Uint64(), R"(uint64)");
+ ASSERT_DESERIALIZED_EQ(NTi::Uint64(), R"({type_name=uint64})");
+}
+
+TEST(TypeDeserialize, Float) {
+ ASSERT_DESERIALIZED_EQ(NTi::Float(), R"(float)");
+ ASSERT_DESERIALIZED_EQ(NTi::Float(), R"({type_name=float})");
+}
+
+TEST(TypeDeserialize, Double) {
+ ASSERT_DESERIALIZED_EQ(NTi::Double(), R"(double)");
+ ASSERT_DESERIALIZED_EQ(NTi::Double(), R"({type_name=double})");
+}
+
+TEST(TypeDeserialize, String) {
+ ASSERT_DESERIALIZED_EQ(NTi::String(), R"(string)");
+ ASSERT_DESERIALIZED_EQ(NTi::String(), R"({type_name=string})");
+}
+
+TEST(TypeDeserialize, Utf8) {
+ ASSERT_DESERIALIZED_EQ(NTi::Utf8(), R"(utf8)");
+ ASSERT_DESERIALIZED_EQ(NTi::Utf8(), R"({type_name=utf8})");
+}
+
+TEST(TypeDeserialize, Date) {
+ ASSERT_DESERIALIZED_EQ(NTi::Date(), R"(date)");
+ ASSERT_DESERIALIZED_EQ(NTi::Date(), R"({type_name=date})");
+}
+
+TEST(TypeDeserialize, Datetime) {
+ ASSERT_DESERIALIZED_EQ(NTi::Datetime(), R"(datetime)");
+ ASSERT_DESERIALIZED_EQ(NTi::Datetime(), R"({type_name=datetime})");
+}
+
+TEST(TypeDeserialize, Timestamp) {
+ ASSERT_DESERIALIZED_EQ(NTi::Timestamp(), R"(timestamp)");
+ ASSERT_DESERIALIZED_EQ(NTi::Timestamp(), R"({type_name=timestamp})");
+}
+
+TEST(TypeDeserialize, TzDate) {
+ ASSERT_DESERIALIZED_EQ(NTi::TzDate(), R"(tz_date)");
+ ASSERT_DESERIALIZED_EQ(NTi::TzDate(), R"({type_name=tz_date})");
+}
+
+TEST(TypeDeserialize, TzDatetime) {
+ ASSERT_DESERIALIZED_EQ(NTi::TzDatetime(), R"(tz_datetime)");
+ ASSERT_DESERIALIZED_EQ(NTi::TzDatetime(), R"({type_name=tz_datetime})");
+}
+
+TEST(TypeDeserialize, TzTimestamp) {
+ ASSERT_DESERIALIZED_EQ(NTi::TzTimestamp(), R"(tz_timestamp)");
+ ASSERT_DESERIALIZED_EQ(NTi::TzTimestamp(), R"({type_name=tz_timestamp})");
+}
+
+TEST(TypeDeserialize, Interval) {
+ ASSERT_DESERIALIZED_EQ(NTi::Interval(), R"(interval)");
+ ASSERT_DESERIALIZED_EQ(NTi::Interval(), R"({type_name=interval})");
+}
+
+TEST(TypeDeserialize, Decimal) {
+ ASSERT_DESERIALIZED_EQ(NTi::Decimal(20, 10), R"({type_name=decimal; precision=20; scale=10})");
+ ASSERT_DESERIALIZED_EQ(NTi::Decimal(20, 10), R"({scale=10; type_name=decimal; precision=20})");
+ ASSERT_DESERIALIZED_EQ(NTi::Decimal(10, 10), R"({type_name=decimal; precision=10; scale=10})");
+}
+
+TEST(TypeDeserialize, DecimalMissingTypeParameters) {
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({precision=20; scale=10})");
+ }(),
+ NTi::TDeserializationException, R"(missing required key "type_name")");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({type_name=decimal; scale=10})");
+ }(),
+ NTi::TDeserializationException, R"(missing required key "precision")");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({type_name=decimal; precision=20})");
+ }(),
+ NTi::TDeserializationException, R"(missing required key "scale")");
+}
+
+TEST(TypeDeserialize, Json) {
+ ASSERT_DESERIALIZED_EQ(NTi::Json(), R"(json)");
+ ASSERT_DESERIALIZED_EQ(NTi::Json(), R"({type_name=json})");
+}
+
+TEST(TypeDeserialize, Yson) {
+ ASSERT_DESERIALIZED_EQ(NTi::Yson(), R"(yson)");
+ ASSERT_DESERIALIZED_EQ(NTi::Yson(), R"({type_name=yson})");
+}
+
+TEST(TypeDeserialize, Uuid) {
+ ASSERT_DESERIALIZED_EQ(NTi::Uuid(), R"(uuid)");
+ ASSERT_DESERIALIZED_EQ(NTi::Uuid(), R"({type_name=uuid})");
+}
+
+TEST(TypeDeserialize, Optional) {
+ ASSERT_DESERIALIZED_EQ(NTi::Optional(NTi::Void()), R"({type_name=optional; item=void})");
+ ASSERT_DESERIALIZED_EQ(NTi::Optional(NTi::String()), R"({type_name=optional; item=string})");
+ ASSERT_DESERIALIZED_EQ(NTi::Optional(NTi::String()), R"({item=string; type_name=optional})");
+}
+
+TEST(TypeDeserialize, OptionalMissingTypeParameters) {
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({item=string})");
+ }(),
+ NTi::TDeserializationException, R"(missing required key "type_name")");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({type_name=optional})");
+ }(),
+ NTi::TDeserializationException, R"(missing required key "item")");
+}
+
+TEST(TypeDeserialize, List) {
+ ASSERT_DESERIALIZED_EQ(NTi::List(NTi::Void()), R"({type_name=list; item=void})");
+ ASSERT_DESERIALIZED_EQ(NTi::List(NTi::String()), R"({type_name=list; item=string})");
+ ASSERT_DESERIALIZED_EQ(NTi::List(NTi::String()), R"({item=string; type_name=list})");
+}
+
+TEST(TypeDeserialize, ListMissingTypeParameters) {
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({item=string})");
+ }(),
+ NTi::TDeserializationException, R"(missing required key "type_name")");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({type_name=list})");
+ }(),
+ NTi::TDeserializationException, R"(missing required key "item")");
+}
+
+TEST(TypeDeserialize, Dict) {
+ ASSERT_DESERIALIZED_EQ(NTi::Dict(NTi::Void(), NTi::Void()), R"({type_name=dict; key=void; value=void})");
+ ASSERT_DESERIALIZED_EQ(NTi::Dict(NTi::Int32(), NTi::String()), R"({type_name=dict; key=int32; value=string})");
+ ASSERT_DESERIALIZED_EQ(NTi::Dict(NTi::Int32(), NTi::String()), R"({key=int32; value=string; type_name=dict})");
+ ASSERT_DESERIALIZED_EQ(NTi::Dict(NTi::Int32(), NTi::String()), R"({value=string; key=int32; type_name=dict})");
+}
+
+TEST(TypeDeserialize, DictMissingTypeParameters) {
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({key=string; value=string})");
+ }(),
+ NTi::TDeserializationException, R"(missing required key "type_name")");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({type_name=dict})");
+ }(),
+ NTi::TDeserializationException, R"(missing required keys "key" and "value")");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({type_name=dict; value=string})");
+ }(),
+ NTi::TDeserializationException, R"(missing required key "key")");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({type_name=dict; key=string})");
+ }(),
+ NTi::TDeserializationException, R"(missing required key "value")");
+}
+
+TEST(TypeDeserialize, StructEmpty) {
+ ASSERT_DESERIALIZED_EQ(
+ NTi::Struct({}),
+ R"({type_name=struct; members=[]})");
+ ASSERT_DESERIALIZED_EQ(
+ NTi::Struct({}),
+ R"({members=[]; type_name=struct})");
+}
+
+TEST(TypeDeserialize, Struct) {
+ auto ty = NTi::Struct({{"ItemB", NTi::String()}, {"ItemA", NTi::List(NTi::Int64())}});
+
+ ASSERT_DESERIALIZED_EQ(
+ ty,
+ R"({type_name=struct; members=[{name=ItemB; type=string}; {name=ItemA; type={type_name=list; item=int64}}]})");
+ ASSERT_DESERIALIZED_EQ(
+ ty,
+ R"({members=[{name=ItemB; type=string}; {name=ItemA; type={type_name=list; item=int64}}]; type_name=struct})");
+ ASSERT_DESERIALIZED_EQ(
+ ty,
+ R"({type_name=struct; members=[{type=string; name=ItemB}; {name=ItemA; type={item=int64; type_name=list}}]})");
+ ASSERT_DESERIALIZED_EQ(
+ ty,
+ R"({type_name=struct; members=[{name=ItemB; type=string}; {name=ItemA; type={type_name=list; item=int64}}]; name=#})");
+ ASSERT_DESERIALIZED_EQ(
+ ty,
+ R"({type_name=struct; name=#; members=[{name=ItemB; type=string}; {name=ItemA; type={type_name=list; item=int64}}]})");
+}
+
+TEST(TypeDeserialize, StructMissingTypeParameters) {
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({members=[]})");
+ }(),
+ NTi::TDeserializationException, R"(missing required key "type_name")");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({type_name=struct})");
+ }(),
+ NTi::TDeserializationException, R"(missing required key "members")");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({type_name=struct; name=S})");
+ }(),
+ NTi::TDeserializationException, R"(missing required key "members")");
+}
+
+TEST(TypeDeserialize, TupleEmpty) {
+ ASSERT_DESERIALIZED_EQ(
+ NTi::Tuple({}),
+ R"({type_name=tuple; elements=[]})");
+
+ ASSERT_DESERIALIZED_EQ(
+ NTi::Tuple({}),
+ R"({elements=[]; type_name=tuple})");
+}
+
+TEST(TypeDeserialize, Tuple) {
+ auto ty = NTi::Tuple({{NTi::String()}, {NTi::List(NTi::Int64())}});
+
+ ASSERT_DESERIALIZED_EQ(
+ ty,
+ R"({type_name=tuple; elements=[{type=string}; {type={type_name=list; item=int64}}]})");
+ ASSERT_DESERIALIZED_EQ(
+ ty,
+ R"({elements=[{type=string}; {type={item=int64; type_name=list}}]; type_name=tuple})");
+ ASSERT_DESERIALIZED_EQ(
+ ty,
+ R"({type_name=tuple; name=#; elements=[{type=string}; {type={type_name=list; item=int64}}]})");
+ ASSERT_DESERIALIZED_EQ(
+ ty,
+ R"({type_name=tuple; elements=[{type=string}; {type={type_name=list; item=int64}}]; name=#})");
+}
+
+TEST(TypeDeserialize, TupleMissingTypeParameters) {
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({elements=[]})");
+ }(),
+ NTi::TDeserializationException, R"(missing required key "type_name")");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({type_name=tuple})");
+ }(),
+ NTi::TDeserializationException, R"(missing required key "elements")");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({type_name=tuple; name=T})");
+ }(),
+ NTi::TDeserializationException, R"(missing required key "elements")");
+}
+
+TEST(TypeDeserialize, VariantStruct) {
+ auto ty = NTi::Variant(NTi::Struct({{"ItemB", NTi::String()}, {"ItemA", NTi::List(NTi::Int64())}}));
+
+ ASSERT_DESERIALIZED_EQ(
+ ty,
+ R"({type_name=variant; members=[{name=ItemB; type=string}; {name=ItemA; type={type_name=list; item=int64}}]})");
+ ASSERT_DESERIALIZED_EQ(
+ ty,
+ R"({members=[{name=ItemB; type=string}; {name=ItemA; type={type_name=list; item=int64}}]; type_name=variant})");
+ ASSERT_DESERIALIZED_EQ(
+ ty,
+ R"({type_name=variant; name=#; members=[{name=ItemB; type=string}; {name=ItemA; type={type_name=list; item=int64}}]})");
+ ASSERT_DESERIALIZED_EQ(
+ ty,
+ R"({type_name=variant; members=[{name=ItemB; type=string}; {name=ItemA; type={type_name=list; item=int64}}]; name=#})");
+}
+
+TEST(TypeDeserialize, VariantTuple) {
+ auto ty = NTi::Variant(NTi::Tuple({{NTi::String()}, {NTi::List(NTi::Int64())}}));
+
+ ASSERT_DESERIALIZED_EQ(
+ ty,
+ R"({type_name=variant; elements=[{type=string}; {type={type_name=list; item=int64}}]})");
+ ASSERT_DESERIALIZED_EQ(
+ ty,
+ R"({elements=[{type=string}; {type={type_name=list; item=int64}}]; type_name=variant})");
+ ASSERT_DESERIALIZED_EQ(
+ ty,
+ R"({type_name=variant; name=#; elements=[{type=string}; {type={type_name=list; item=int64}}]})");
+ ASSERT_DESERIALIZED_EQ(
+ ty,
+ R"({type_name=variant; elements=[{type=string}; {type={type_name=list; item=int64}}]; name=#})");
+}
+
+TEST(TypeDeserialize, VariantMissingTypeParameters) {
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({elements=[]})");
+ }(),
+ NTi::TDeserializationException, R"(missing required key "type_name")");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({name=X})");
+ }(),
+ NTi::TDeserializationException, R"(missing required key "type_name")");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({type_name=variant})");
+ }(),
+ NTi::TDeserializationException, R"(missing both keys "members" and "elements")");
+}
+
+TEST(TypeDeserialize, Tagged) {
+ ASSERT_DESERIALIZED_EQ(
+ NTi::Tagged(NTi::String(), "Url"),
+ R"({type_name=tagged; tag=Url; item=string})");
+ ASSERT_DESERIALIZED_EQ(
+ NTi::Tagged(NTi::String(), "Url"),
+ R"({type_name=tagged; item=string; tag=Url})");
+ ASSERT_DESERIALIZED_EQ(
+ NTi::Tagged(NTi::String(), "Url"),
+ R"({item=string; tag=Url; type_name=tagged})");
+}
+
+TEST(TypeDeserialize, TaggedMissingTypeParameters) {
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({tag=T; item=string})");
+ }(),
+ NTi::TDeserializationException, R"(missing required key "type_name")");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({tag=T})");
+ }(),
+ NTi::TDeserializationException, R"(missing required key "type_name")");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({type_name=tagged; tag=T})");
+ }(),
+ NTi::TDeserializationException, R"(missing required key "item")");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({type_name=tagged; item=string})");
+ }(),
+ NTi::TDeserializationException, R"(missing required key "tag")");
+}
+
+TEST(TypeDeserialize, ComplexTypeAsString) {
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"(decimal)");
+ }(),
+ NTi::TDeserializationException, R"(missing required keys "precision" and "scale")");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"(optional)");
+ }(),
+ NTi::TDeserializationException, R"(missing required key "item")");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"(list)");
+ }(),
+ NTi::TDeserializationException, R"(missing required key "item")");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"(dict)");
+ }(),
+ NTi::TDeserializationException, R"(missing required keys "key" and "value")");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"(struct)");
+ }(),
+ NTi::TDeserializationException, R"(missing required key "members")");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"(tuple)");
+ }(),
+ NTi::TDeserializationException, R"(missing required key "elements")");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"(variant)");
+ }(),
+ NTi::TDeserializationException, R"(missing both keys "members" and "elements")");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"(tagged)");
+ }(),
+ NTi::TDeserializationException, R"(missing required keys "tag" and "item")");
+}
+
+TEST(TypeDeserialize, MissingTypeName) {
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({})");
+ }(),
+ NTi::TDeserializationException, R"(missing required key "type_name")");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({item=string})");
+ }(),
+ NTi::TDeserializationException, R"(missing required key "type_name")");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({tag=Url})");
+ }(),
+ NTi::TDeserializationException, R"(missing required key "type_name")");
+ UNIT_ASSERT_EXCEPTION_CONTAINS(
+ []() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), R"({key=string; value=int32})");
+ }(),
+ NTi::TDeserializationException, R"(missing required key "type_name")");
+}
+
+TEST(TypeDeserialize, UnknownKeys) {
+ auto tupleType = NTi::Tuple({{NTi::String()}, {NTi::List(NTi::Int64())}});
+
+ ASSERT_DESERIALIZED_EQ(
+ tupleType,
+ R"({type_name=tuple; unknown_key=<>0; elements=[{type=string}; {type={type_name=list; item=int64}}]})");
+
+ ASSERT_DESERIALIZED_EQ(
+ tupleType,
+ R"({type_name=tuple; elements=[{unknown_key={foo=<>0}; type=string}; {type={type_name=list; item=int64}}]})");
+
+ auto structType = NTi::Struct({{"ItemB", NTi::String()}, {"ItemA", NTi::List(NTi::Int64())}});
+ ASSERT_DESERIALIZED_EQ(
+ structType,
+ R"({type_name=struct; unknown_key=[]; members=[{name=ItemB; type=string}; {name=ItemA; type={type_name=list; item=int64}}]})");
+ ASSERT_DESERIALIZED_EQ(
+ structType,
+ R"({type_name=struct; members=[{name=ItemB; unknown_key=foo; type=string}; {name=ItemA; type={type_name=list; item=int64}}]})");
+
+ auto utf8Type = NTi::Utf8();
+ ASSERT_DESERIALIZED_EQ(
+ utf8Type,
+ R"({type_name=utf8; unknown_key=[];})");
+}
+
+
+TEST(TypeDeserialize, DeepType) {
+ auto ty = TStringBuilder();
+ for (size_t i = 0; i < 100; ++i)
+ ty << "{type_name=optional; item=";
+ ty << "string";
+ for (size_t i = 0; i < 100; ++i)
+ ty << "}";
+
+ UNIT_ASSERT_EXCEPTION_CONTAINS([&ty]() {
+ NTi::NIo::DeserializeYson(*NTi::HeapFactory(), ty);
+ }(),
+ NTi::TDeserializationException, R"(too deep)");
+}
+
+TEST(TypeDeserialize, MultipleTypes) {
+ auto ty = "{type_name=optional; item=string}; {type_name=list; item=utf8}";
+
+ auto reader = NYsonPull::TReader(NYsonPull::NInput::FromMemory(ty), NYsonPull::EStreamType::ListFragment);
+ auto factory = NTi::PoolFactory();
+
+ {
+ auto ty1 = NTi::Optional(NTi::String());
+ ASSERT_STRICT_EQ(NTi::NIo::DeserializeYsonMultipleRaw(*factory, reader), ty1.Get());
+ }
+
+ {
+ auto ty2 = NTi::List(NTi::Utf8());
+ ASSERT_STRICT_EQ(NTi::NIo::DeserializeYsonMultipleRaw(*factory, reader), ty2.Get());
+ }
+
+ ASSERT_EQ(NTi::NIo::DeserializeYsonMultipleRaw(*factory, reader), nullptr);
+}
diff --git a/library/cpp/type_info/ut/type_equivalence.cpp b/library/cpp/type_info/ut/type_equivalence.cpp
new file mode 100644
index 0000000000..ca697620ad
--- /dev/null
+++ b/library/cpp/type_info/ut/type_equivalence.cpp
@@ -0,0 +1,394 @@
+#include <library/cpp/testing/unittest/gtest.h>
+
+#include <library/cpp/type_info/type_info.h>
+
+#include "utils.h"
+
+TEST_TF(TypeEquivalence, StrictEqSelf) {
+ ASSERT_STRICT_EQ(
+ f.Void(),
+ f.Void());
+ ASSERT_STRICT_EQ(
+ f.Bool(),
+ f.Bool());
+ ASSERT_STRICT_EQ(
+ f.Int8(),
+ f.Int8());
+ ASSERT_STRICT_EQ(
+ f.Int16(),
+ f.Int16());
+ ASSERT_STRICT_EQ(
+ f.Int32(),
+ f.Int32());
+ ASSERT_STRICT_EQ(
+ f.Int64(),
+ f.Int64());
+ ASSERT_STRICT_EQ(
+ f.Uint8(),
+ f.Uint8());
+ ASSERT_STRICT_EQ(
+ f.Uint16(),
+ f.Uint16());
+ ASSERT_STRICT_EQ(
+ f.Uint32(),
+ f.Uint32());
+ ASSERT_STRICT_EQ(
+ f.Uint64(),
+ f.Uint64());
+ ASSERT_STRICT_EQ(
+ f.Float(),
+ f.Float());
+ ASSERT_STRICT_EQ(
+ f.Double(),
+ f.Double());
+ ASSERT_STRICT_EQ(
+ f.String(),
+ f.String());
+ ASSERT_STRICT_EQ(
+ f.Utf8(),
+ f.Utf8());
+ ASSERT_STRICT_EQ(
+ f.Date(),
+ f.Date());
+ ASSERT_STRICT_EQ(
+ f.Datetime(),
+ f.Datetime());
+ ASSERT_STRICT_EQ(
+ f.Timestamp(),
+ f.Timestamp());
+ ASSERT_STRICT_EQ(
+ f.TzDate(),
+ f.TzDate());
+ ASSERT_STRICT_EQ(
+ f.TzDatetime(),
+ f.TzDatetime());
+ ASSERT_STRICT_EQ(
+ f.TzTimestamp(),
+ f.TzTimestamp());
+ ASSERT_STRICT_EQ(
+ f.Interval(),
+ f.Interval());
+ ASSERT_STRICT_EQ(
+ f.Decimal(20, 10),
+ f.Decimal(20, 10));
+ ASSERT_STRICT_EQ(
+ f.Json(),
+ f.Json());
+ ASSERT_STRICT_EQ(
+ f.Yson(),
+ f.Yson());
+ ASSERT_STRICT_EQ(
+ f.Uuid(),
+ f.Uuid());
+ ASSERT_STRICT_EQ(
+ f.Optional(f.Void()),
+ f.Optional(f.Void()));
+ ASSERT_STRICT_EQ(
+ f.List(f.Void()),
+ f.List(f.Void()));
+ ASSERT_STRICT_EQ(
+ f.Dict(f.Void(), f.String()),
+ f.Dict(f.Void(), f.String()));
+ ASSERT_STRICT_EQ(
+ f.Struct({}),
+ f.Struct({}));
+ ASSERT_STRICT_EQ(
+ f.Struct("S", {}),
+ f.Struct("S", {}));
+ ASSERT_STRICT_EQ(
+ f.Struct({{"a", f.Int64()}, {"b", f.Int8()}}),
+ f.Struct({{"a", f.Int64()}, {"b", f.Int8()}}));
+ ASSERT_STRICT_EQ(
+ f.Struct("S", {{"a", f.Int64()}, {"b", f.Int8()}}),
+ f.Struct("S", {{"a", f.Int64()}, {"b", f.Int8()}}));
+ ASSERT_STRICT_EQ(
+ f.Tuple({}),
+ f.Tuple({}));
+ ASSERT_STRICT_EQ(
+ f.Tuple("T", {}),
+ f.Tuple("T", {}));
+ ASSERT_STRICT_EQ(
+ f.Tuple({{f.Int64()}, {f.Int8()}}),
+ f.Tuple({{f.Int64()}, {f.Int8()}}));
+ ASSERT_STRICT_EQ(
+ f.Tuple("T", {{f.Int64()}, {f.Int8()}}),
+ f.Tuple("T", {{f.Int64()}, {f.Int8()}}));
+ ASSERT_STRICT_EQ(
+ f.Variant(f.Struct("Inner", {{"x", f.Void()}})),
+ f.Variant(f.Struct("Inner", {{"x", f.Void()}})));
+ ASSERT_STRICT_EQ(
+ f.Variant("V", f.Struct("Inner", {{"x", f.Void()}})),
+ f.Variant("V", f.Struct("Inner", {{"x", f.Void()}})));
+ ASSERT_STRICT_EQ(
+ f.Variant(f.Tuple("Inner", {{f.Void()}})),
+ f.Variant(f.Tuple("Inner", {{f.Void()}})));
+ ASSERT_STRICT_EQ(
+ f.Variant("V", f.Tuple("Inner", {{f.Void()}})),
+ f.Variant("V", f.Tuple("Inner", {{f.Void()}})));
+ ASSERT_STRICT_EQ(
+ f.Tagged(f.Void(), "T"),
+ f.Tagged(f.Void(), "T"));
+}
+
+TEST_TF(TypeEquivalence, StrictNeOtherType) {
+ ASSERT_STRICT_NE(
+ f.Void(),
+ f.Bool());
+ ASSERT_STRICT_NE(
+ f.Bool(),
+ f.Int8());
+ ASSERT_STRICT_NE(
+ f.Int8(),
+ f.Int16());
+ ASSERT_STRICT_NE(
+ f.Int16(),
+ f.Int32());
+ ASSERT_STRICT_NE(
+ f.Int32(),
+ f.Int64());
+ ASSERT_STRICT_NE(
+ f.Int64(),
+ f.Uint8());
+ ASSERT_STRICT_NE(
+ f.Uint8(),
+ f.Uint16());
+ ASSERT_STRICT_NE(
+ f.Uint16(),
+ f.Uint32());
+ ASSERT_STRICT_NE(
+ f.Uint32(),
+ f.Uint64());
+ ASSERT_STRICT_NE(
+ f.Uint64(),
+ f.Float());
+ ASSERT_STRICT_NE(
+ f.Float(),
+ f.Double());
+ ASSERT_STRICT_NE(
+ f.Double(),
+ f.String());
+ ASSERT_STRICT_NE(
+ f.String(),
+ f.Utf8());
+ ASSERT_STRICT_NE(
+ f.Utf8(),
+ f.Date());
+ ASSERT_STRICT_NE(
+ f.Date(),
+ f.Datetime());
+ ASSERT_STRICT_NE(
+ f.Datetime(),
+ f.Timestamp());
+ ASSERT_STRICT_NE(
+ f.Timestamp(),
+ f.TzDate());
+ ASSERT_STRICT_NE(
+ f.TzDate(),
+ f.TzDatetime());
+ ASSERT_STRICT_NE(
+ f.TzDatetime(),
+ f.TzTimestamp());
+ ASSERT_STRICT_NE(
+ f.TzTimestamp(),
+ f.Interval());
+ ASSERT_STRICT_NE(
+ f.Interval(),
+ f.Decimal(20, 10));
+ ASSERT_STRICT_NE(
+ f.Decimal(20, 10),
+ f.Json());
+ ASSERT_STRICT_NE(
+ f.Json(),
+ f.Yson());
+ ASSERT_STRICT_NE(
+ f.Yson(),
+ f.Uuid());
+ ASSERT_STRICT_NE(
+ f.Uuid(),
+ f.Optional(f.Void()));
+ ASSERT_STRICT_NE(
+ f.Optional(f.Void()),
+ f.List(f.Void()));
+ ASSERT_STRICT_NE(
+ f.List(f.Void()),
+ f.Dict(f.Void(), f.String()));
+ ASSERT_STRICT_NE(
+ f.Dict(f.Void(), f.String()),
+ f.Struct({}));
+ ASSERT_STRICT_NE(
+ f.Struct({}),
+ f.Struct("S", {}));
+ ASSERT_STRICT_NE(
+ f.Struct("S", {}),
+ f.Struct({{"a", f.Int64()}, {"b", f.Int8()}}));
+ ASSERT_STRICT_NE(
+ f.Struct({{"a", f.Int64()}, {"b", f.Int8()}}),
+ f.Struct("S", {{"a", f.Int64()}, {"b", f.Int8()}}));
+ ASSERT_STRICT_NE(
+ f.Struct("S", {{"a", f.Int64()}, {"b", f.Int8()}}),
+ f.Tuple({}));
+ ASSERT_STRICT_NE(
+ f.Tuple({}),
+ f.Tuple("T", {}));
+ ASSERT_STRICT_NE(
+ f.Tuple("T", {}),
+ f.Tuple({{f.Int64()}, {f.Int8()}}));
+ ASSERT_STRICT_NE(
+ f.Tuple({{f.Int64()}, {f.Int8()}}),
+ f.Tuple("T", {{f.Int64()}, {f.Int8()}}));
+ ASSERT_STRICT_NE(
+ f.Tuple("T", {{f.Int64()}, {f.Int8()}}),
+ f.Variant(f.Struct("Inner", {{"x", f.Void()}})));
+ ASSERT_STRICT_NE(
+ f.Variant(f.Struct("Inner", {{"x", f.Void()}})),
+ f.Variant("V", f.Struct("Inner", {{"x", f.Void()}})));
+ ASSERT_STRICT_NE(
+ f.Variant("V", f.Struct("Inner", {{"x", f.Void()}})),
+ f.Variant(f.Tuple("Inner", {{f.Void()}})));
+ ASSERT_STRICT_NE(
+ f.Variant(f.Tuple("Inner", {{f.Void()}})),
+ f.Variant("V", f.Tuple("Inner", {{f.Void()}})));
+ ASSERT_STRICT_NE(
+ f.Variant("V", f.Tuple("Inner", {{f.Void()}})),
+ f.Tagged(f.Void(), "T"));
+ ASSERT_STRICT_NE(
+ f.Tagged(f.Void(), "T"),
+ f.Void());
+}
+
+TEST_TF(TypeEquivalence, StrictNeDecimal) {
+ ASSERT_STRICT_NE(
+ f.Decimal(20, 10),
+ f.Decimal(21, 10));
+ ASSERT_STRICT_NE(
+ f.Decimal(20, 10),
+ f.Decimal(20, 11));
+}
+
+TEST_TF(TypeEquivalence, StrictNeStruct) {
+ ASSERT_STRICT_NE(
+ f.Struct({}),
+ f.Struct("", {}));
+ ASSERT_STRICT_NE(
+ f.Struct("name", {}),
+ f.Struct("other name", {}));
+ ASSERT_STRICT_NE(
+ f.Struct({}),
+ f.Struct({{"x", f.Void()}}));
+ ASSERT_STRICT_NE(
+ f.Struct({{"x", f.Void()}}),
+ f.Struct({{"y", f.Void()}}));
+ ASSERT_STRICT_NE(
+ f.Struct({{"x", f.Void()}}),
+ f.Struct({{"x", f.String()}}));
+ ASSERT_STRICT_NE(
+ f.Struct("name", {}),
+ f.Struct("name", {{"x", f.Void()}}));
+ ASSERT_STRICT_NE(
+ f.Struct("name", {{"x", f.Void()}}),
+ f.Struct("name", {{"y", f.Void()}}));
+ ASSERT_STRICT_NE(
+ f.Struct("name", {{"x", f.Void()}}),
+ f.Struct("name", {{"x", f.String()}}));
+ ASSERT_STRICT_NE(
+ f.Struct({{"x", f.Void()}, {"y", f.String()}}),
+ f.Struct({{"x", f.String()}, {"y", f.Void()}}));
+ ASSERT_STRICT_NE(
+ f.Struct({{"x", f.Void()}, {"y", f.Void()}}),
+ f.Struct({{"y", f.Void()}, {"x", f.Void()}}));
+ ASSERT_STRICT_NE(
+ f.Struct({{"x", f.Void()}, {"y", f.Void()}}),
+ f.Struct({{"x", f.Void()}, {"y", f.Void()}, {"z", f.Void()}}));
+}
+
+TEST_TF(TypeEquivalence, StrictNeTuple) {
+ ASSERT_STRICT_NE(
+ f.Tuple({}),
+ f.Tuple("", {}));
+ ASSERT_STRICT_NE(
+ f.Tuple("name", {}),
+ f.Tuple("other name", {}));
+ ASSERT_STRICT_NE(
+ f.Tuple({}),
+ f.Tuple({{f.Void()}}));
+ ASSERT_STRICT_NE(
+ f.Tuple({{f.Void()}}),
+ f.Tuple({{f.String()}}));
+ ASSERT_STRICT_NE(
+ f.Tuple("name", {}),
+ f.Tuple("name", {{f.Void()}}));
+ ASSERT_STRICT_NE(
+ f.Tuple("name", {{f.Void()}}),
+ f.Tuple("name", {{f.String()}}));
+ ASSERT_STRICT_NE(
+ f.Tuple({{f.String()}, {f.Void()}}),
+ f.Tuple({{f.Void()}, {f.String()}}));
+ ASSERT_STRICT_NE(
+ f.Tuple({{f.Void()}, {f.Void()}}),
+ f.Tuple({{f.Void()}, {f.Void()}, {f.Void()}}));
+}
+
+TEST_TF(TypeEquivalence, StrictNeVariant) {
+ ASSERT_STRICT_NE(
+ f.Variant(f.Tuple({{f.Void()}})),
+ f.Variant("", f.Tuple({{f.Void()}})));
+ ASSERT_STRICT_NE(
+ f.Variant("", f.Tuple({{f.Void()}})),
+ f.Variant("X", f.Tuple({{f.Void()}})));
+ ASSERT_STRICT_NE(
+ f.Variant(f.Tuple({{f.Void()}})),
+ f.Variant(f.Tuple({{f.String()}})));
+ ASSERT_STRICT_NE(
+ f.Variant("X", f.Tuple({{f.Utf8()}})),
+ f.Variant("X", f.Tuple({{f.String()}})));
+ ASSERT_STRICT_NE(
+ f.Variant(f.Tuple({{f.Utf8()}})),
+ f.Variant(f.Struct({{"_", f.Utf8()}})));
+ ASSERT_STRICT_NE(
+ f.Variant(f.Struct({{"item1", f.String()}})),
+ f.Variant(f.Struct({{"item2", f.String()}})));
+ ASSERT_STRICT_NE(
+ f.Variant("X", f.Struct({{"item2", f.String()}})),
+ f.Variant("X", f.Struct({{"item1", f.String()}})));
+}
+
+TEST_TF(TypeEquivalence, StrictNeTagged) {
+ ASSERT_STRICT_NE(
+ f.Tagged(f.String(), "Tag"),
+ f.Tagged(f.String(), "Other tag"));
+ ASSERT_STRICT_NE(
+ f.Tagged(f.String(), "Tag"),
+ f.Tagged(f.Utf8(), "Tag"));
+}
+
+TEST_TF(TypeEquivalence, StrictNeDeep) {
+ auto t1 = f.Struct({
+ {"i", f.Optional(f.String())},
+ {"don't", f.Utf8()},
+ {"have", f.Tuple("GeoCoordinates", {{f.Float()}, {f.Float()}})},
+ {"enough", f.List(f.Optional(f.String()))},
+ {"fantasy", f.Dict(f.String(), f.List(f.Utf8()))}, // < difference
+ {"to", f.Yson()},
+ {"think", f.Optional(f.Json())},
+ {"of", f.Optional(f.String())},
+ {"a", f.List(f.Tuple({{f.String()}, {f.Tuple("GeoCoordinates", {{f.Float()}, {f.Float()}})}}))},
+ {"meaningful", f.Optional(f.String())},
+ {"example", f.Dict(f.Optional(f.Decimal(10, 5)), f.List(f.Dict(f.Int32(), f.Float())))},
+ });
+
+ auto t2 = f.Struct({
+ {"i", f.Optional(f.String())},
+ {"don't", f.Utf8()},
+ {"have", f.Tuple("GeoCoordinates", {{f.Float()}, {f.Float()}})},
+ {"enough", f.List(f.Optional(f.String()))},
+ {"fantasy", f.Dict(f.String(), f.List(f.String()))}, // < difference
+ {"to", f.Yson()},
+ {"think", f.Optional(f.Json())},
+ {"of", f.Optional(f.String())},
+ {"a", f.List(f.Tuple({{f.String()}, {f.Tuple("GeoCoordinates", {{f.Float()}, {f.Float()}})}}))},
+ {"meaningful", f.Optional(f.String())},
+ {"example", f.Dict(f.Optional(f.Decimal(10, 5)), f.List(f.Dict(f.Int32(), f.Float())))},
+ });
+
+ ASSERT_STRICT_NE(t1, t2);
+}
diff --git a/library/cpp/type_info/ut/type_factory.cpp b/library/cpp/type_info/ut/type_factory.cpp
new file mode 100644
index 0000000000..0ab44129ad
--- /dev/null
+++ b/library/cpp/type_info/ut/type_factory.cpp
@@ -0,0 +1,121 @@
+#include <library/cpp/testing/unittest/gtest.h>
+
+#include <library/cpp/type_info/type_info.h>
+
+TEST(TypeFactory, AdoptPoolToPool) {
+ auto f1 = NTi::PoolFactory();
+ auto f2 = NTi::PoolFactory();
+
+ auto t = f1->Optional(f1->List(f1->String()));
+ auto ta = f2->Adopt(t);
+
+ ASSERT_NE(t.Get(), ta.Get());
+ ASSERT_NE(t->GetItemTypeRaw(), ta->GetItemTypeRaw());
+ ASSERT_EQ(t->GetItemTypeRaw()->AsListRaw()->GetItemTypeRaw(), ta->GetItemTypeRaw()->AsListRaw()->GetItemTypeRaw());
+
+ f1 = nullptr;
+ f2 = nullptr;
+ t = nullptr;
+
+ // `ta` is still alive
+
+ ASSERT_TRUE(ta->IsOptional());
+ ASSERT_TRUE(ta->GetItemTypeRaw()->IsList());
+}
+
+TEST(TypeFactory, AdoptPoolToSamePool) {
+ auto f = NTi::PoolFactory();
+
+ auto t = f->Optional(f->List(f->String()));
+ auto ta = f->Adopt(t);
+
+ ASSERT_EQ(t.Get(), ta.Get());
+}
+
+TEST(TypeFactory, AdoptHeapToHeap) {
+ auto f = NTi::HeapFactory();
+
+ auto t = f->Optional(f->List(f->String()));
+ auto ta = f->Adopt(t);
+
+ ASSERT_EQ(t.Get(), ta.Get());
+}
+
+TEST(TypeFactory, AdoptHeapToPool) {
+ auto f1 = NTi::HeapFactory();
+ auto f2 = NTi::PoolFactory();
+
+ auto t = f1->Optional(f1->List(f1->String()));
+ auto ta = f2->Adopt(t);
+
+ ASSERT_NE(t.Get(), ta.Get());
+ ASSERT_NE(t->GetItemTypeRaw(), ta->GetItemTypeRaw());
+ ASSERT_EQ(t->GetItemTypeRaw()->AsListRaw()->GetItemTypeRaw(), ta->GetItemTypeRaw()->AsListRaw()->GetItemTypeRaw());
+
+ f1 = nullptr;
+ f2 = nullptr;
+ t = nullptr;
+
+ // `ta` is still alive
+
+ ASSERT_TRUE(ta->IsOptional());
+ ASSERT_TRUE(ta->GetItemTypeRaw()->IsList());
+}
+
+TEST(TypeFactory, AdoptPoolToHeap) {
+ auto f1 = NTi::PoolFactory();
+ auto f2 = NTi::HeapFactory();
+
+ auto t = f1->Optional(f1->List(f1->String()));
+ auto ta = f2->Adopt(t);
+
+ ASSERT_NE(t.Get(), ta.Get());
+ ASSERT_NE(t->GetItemTypeRaw(), ta->GetItemTypeRaw());
+ ASSERT_EQ(t->GetItemTypeRaw()->AsListRaw()->GetItemTypeRaw(), ta->GetItemTypeRaw()->AsListRaw()->GetItemTypeRaw());
+
+ f1 = nullptr;
+ f2 = nullptr;
+ t = nullptr;
+
+ // `ta` is still alive
+
+ ASSERT_TRUE(ta->IsOptional());
+ ASSERT_TRUE(ta->GetItemTypeRaw()->IsList());
+}
+
+TEST(TypeFactory, AdoptStaticToPool) {
+ auto f = NTi::PoolFactory();
+
+ auto t = NTi::Void();
+ auto ta = f->Adopt(t);
+
+ ASSERT_EQ(t.Get(), ta.Get());
+}
+
+TEST(TypeFactory, AdoptStaticToHeap) {
+ auto f = NTi::HeapFactory();
+
+ auto t = NTi::Void();
+ auto ta = f->Adopt(t);
+ ASSERT_EQ(t.Get(), ta.Get());
+}
+
+TEST(TypeFactory, Dedup) {
+ {
+ auto f = NTi::PoolFactory(/* deduplicate = */ false);
+
+ auto a = f->OptionalRaw(f->StringRaw());
+ auto b = f->OptionalRaw(f->StringRaw());
+
+ ASSERT_NE(a, b);
+ }
+
+ {
+ auto f = NTi::PoolFactory(/* deduplicate = */ true);
+
+ auto a = f->OptionalRaw(f->StringRaw());
+ auto b = f->OptionalRaw(f->StringRaw());
+
+ ASSERT_EQ(a, b);
+ }
+}
diff --git a/library/cpp/type_info/ut/type_factory_raw.cpp b/library/cpp/type_info/ut/type_factory_raw.cpp
new file mode 100644
index 0000000000..37f5d71aa7
--- /dev/null
+++ b/library/cpp/type_info/ut/type_factory_raw.cpp
@@ -0,0 +1,365 @@
+#include <library/cpp/testing/unittest/gtest.h>
+
+#include <library/cpp/type_info/type_info.h>
+
+TEST(TypeFactoryRaw, Void) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->VoidRaw();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Void);
+ ASSERT_TRUE(t->IsVoid());
+}
+
+TEST(TypeFactoryRaw, Bool) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->BoolRaw();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Bool);
+ ASSERT_TRUE(t->IsBool());
+}
+
+TEST(TypeFactoryRaw, Int8) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->Int8Raw();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Int8);
+ ASSERT_TRUE(t->IsInt8());
+}
+
+TEST(TypeFactoryRaw, Int16) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->Int16Raw();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Int16);
+ ASSERT_TRUE(t->IsInt16());
+}
+
+TEST(TypeFactoryRaw, Int32) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->Int32Raw();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Int32);
+ ASSERT_TRUE(t->IsInt32());
+}
+
+TEST(TypeFactoryRaw, Int64) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->Int64Raw();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Int64);
+ ASSERT_TRUE(t->IsInt64());
+}
+
+TEST(TypeFactoryRaw, Uint8) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->Uint8Raw();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Uint8);
+ ASSERT_TRUE(t->IsUint8());
+}
+
+TEST(TypeFactoryRaw, Uint16) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->Uint16Raw();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Uint16);
+ ASSERT_TRUE(t->IsUint16());
+}
+
+TEST(TypeFactoryRaw, Uint32) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->Uint32Raw();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Uint32);
+ ASSERT_TRUE(t->IsUint32());
+}
+
+TEST(TypeFactoryRaw, Uint64) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->Uint64Raw();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Uint64);
+ ASSERT_TRUE(t->IsUint64());
+}
+
+TEST(TypeFactoryRaw, Float) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->FloatRaw();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Float);
+ ASSERT_TRUE(t->IsFloat());
+}
+
+TEST(TypeFactoryRaw, Double) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->DoubleRaw();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Double);
+ ASSERT_TRUE(t->IsDouble());
+}
+
+TEST(TypeFactoryRaw, String) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->StringRaw();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::String);
+ ASSERT_TRUE(t->IsString());
+}
+
+TEST(TypeFactoryRaw, Utf8) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->Utf8Raw();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Utf8);
+ ASSERT_TRUE(t->IsUtf8());
+}
+
+TEST(TypeFactoryRaw, Date) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->DateRaw();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Date);
+ ASSERT_TRUE(t->IsDate());
+}
+
+TEST(TypeFactoryRaw, Datetime) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->DatetimeRaw();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Datetime);
+ ASSERT_TRUE(t->IsDatetime());
+}
+
+TEST(TypeFactoryRaw, Timestamp) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->TimestampRaw();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Timestamp);
+ ASSERT_TRUE(t->IsTimestamp());
+}
+
+TEST(TypeFactoryRaw, TzDate) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->TzDateRaw();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::TzDate);
+ ASSERT_TRUE(t->IsTzDate());
+}
+
+TEST(TypeFactoryRaw, TzDatetime) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->TzDatetimeRaw();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::TzDatetime);
+ ASSERT_TRUE(t->IsTzDatetime());
+}
+
+TEST(TypeFactoryRaw, TzTimestamp) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->TzTimestampRaw();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::TzTimestamp);
+ ASSERT_TRUE(t->IsTzTimestamp());
+}
+
+TEST(TypeFactoryRaw, Interval) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->IntervalRaw();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Interval);
+ ASSERT_TRUE(t->IsInterval());
+}
+
+TEST(TypeFactoryRaw, Decimal) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->DecimalRaw(20, 10);
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Decimal);
+ ASSERT_TRUE(t->IsDecimal());
+ ASSERT_EQ(t->GetPrecision(), 20);
+ ASSERT_EQ(t->GetScale(), 10);
+}
+
+TEST(TypeFactoryRaw, Json) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->JsonRaw();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Json);
+ ASSERT_TRUE(t->IsJson());
+}
+
+TEST(TypeFactoryRaw, Yson) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->YsonRaw();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Yson);
+ ASSERT_TRUE(t->IsYson());
+}
+
+TEST(TypeFactoryRaw, Uuid) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->UuidRaw();
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Uuid);
+ ASSERT_TRUE(t->IsUuid());
+}
+
+TEST(TypeFactoryRaw, Optional) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->OptionalRaw(f->VoidRaw());
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Optional);
+ ASSERT_TRUE(t->IsOptional());
+ ASSERT_TRUE(t->GetItemType()->IsVoid());
+ ASSERT_EQ(t->GetItemType().Get(), t->GetItemTypeRaw());
+}
+
+TEST(TypeFactoryRaw, List) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->ListRaw(f->VoidRaw());
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::List);
+ ASSERT_TRUE(t->IsList());
+ ASSERT_TRUE(t->GetItemType()->IsVoid());
+ ASSERT_EQ(t->GetItemType().Get(), t->GetItemTypeRaw());
+}
+
+TEST(TypeFactoryRaw, Dict) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->DictRaw(f->VoidRaw(), f->StringRaw());
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Dict);
+ ASSERT_TRUE(t->IsDict());
+ ASSERT_TRUE(t->GetKeyType()->IsVoid());
+ ASSERT_EQ(t->GetKeyType().Get(), t->GetKeyTypeRaw());
+ ASSERT_TRUE(t->GetValueType()->IsString());
+ ASSERT_EQ(t->GetValueType().Get(), t->GetValueTypeRaw());
+}
+
+TEST(TypeFactoryRaw, EmptyStruct) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->StructRaw({});
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Struct);
+ ASSERT_TRUE(t->IsStruct());
+ ASSERT_FALSE(t->GetName().Defined());
+ ASSERT_EQ(t->GetMembers().size(), 0);
+}
+
+TEST(TypeFactoryRaw, NamedEmptyStruct) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->StructRaw("S", {});
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Struct);
+ ASSERT_TRUE(t->IsStruct());
+ ASSERT_TRUE(t->GetName().Defined());
+ ASSERT_EQ(t->GetName().GetRef(), "S");
+ ASSERT_EQ(t->GetMembers().size(), 0);
+}
+
+TEST(TypeFactoryRaw, Struct) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->StructRaw({{"a", f->Int64Raw()}, {"b", f->Int8Raw()}});
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Struct);
+ ASSERT_TRUE(t->IsStruct());
+ ASSERT_FALSE(t->GetName().Defined());
+ ASSERT_EQ(t->GetMembers().size(), 2);
+ ASSERT_EQ(t->GetMembers()[0].GetName(), "a");
+ ASSERT_TRUE(t->GetMembers()[0].GetTypeRaw()->IsInt64());
+ ASSERT_EQ(t->GetMembers()[0].GetType().Get(), t->GetMembers()[0].GetTypeRaw());
+ ASSERT_EQ(t->GetMembers()[1].GetName(), "b");
+ ASSERT_TRUE(t->GetMembers()[1].GetTypeRaw()->IsInt8());
+ ASSERT_EQ(t->GetMembers()[1].GetType().Get(), t->GetMembers()[1].GetTypeRaw());
+}
+
+TEST(TypeFactoryRaw, NamedStruct) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->StructRaw("S", {{"a", f->Int64Raw()}, {"b", f->Int8Raw()}});
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Struct);
+ ASSERT_TRUE(t->IsStruct());
+ ASSERT_TRUE(t->GetName().Defined());
+ ASSERT_EQ(t->GetName().GetRef(), "S");
+ ASSERT_EQ(t->GetMembers().size(), 2);
+ ASSERT_EQ(t->GetMembers()[0].GetName(), "a");
+ ASSERT_TRUE(t->GetMembers()[0].GetTypeRaw()->IsInt64());
+ ASSERT_EQ(t->GetMembers()[0].GetType().Get(), t->GetMembers()[0].GetTypeRaw());
+ ASSERT_EQ(t->GetMembers()[1].GetName(), "b");
+ ASSERT_TRUE(t->GetMembers()[1].GetTypeRaw()->IsInt8());
+ ASSERT_EQ(t->GetMembers()[1].GetType().Get(), t->GetMembers()[1].GetTypeRaw());
+}
+
+TEST(TypeFactoryRaw, EmptyTuple) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->TupleRaw({});
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Tuple);
+ ASSERT_TRUE(t->IsTuple());
+ ASSERT_FALSE(t->GetName().Defined());
+ ASSERT_EQ(t->GetElements().size(), 0);
+}
+
+TEST(TypeFactoryRaw, NamedEmptyTuple) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->TupleRaw("T", {});
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Tuple);
+ ASSERT_TRUE(t->IsTuple());
+ ASSERT_TRUE(t->GetName().Defined());
+ ASSERT_EQ(t->GetName().GetRef(), "T");
+ ASSERT_EQ(t->GetElements().size(), 0);
+}
+
+TEST(TypeFactoryRaw, Tuple) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->TupleRaw({{f->Int64Raw()}, {f->Int8Raw()}});
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Tuple);
+ ASSERT_TRUE(t->IsTuple());
+ ASSERT_FALSE(t->GetName().Defined());
+ ASSERT_EQ(t->GetElements().size(), 2);
+ ASSERT_TRUE(t->GetElements()[0].GetTypeRaw()->IsInt64());
+ ASSERT_EQ(t->GetElements()[0].GetType().Get(), t->GetElements()[0].GetTypeRaw());
+ ASSERT_TRUE(t->GetElements()[1].GetTypeRaw()->IsInt8());
+ ASSERT_EQ(t->GetElements()[1].GetType().Get(), t->GetElements()[1].GetTypeRaw());
+}
+
+TEST(TypeFactoryRaw, NamedTuple) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->TupleRaw("T", {{f->Int64Raw()}, {f->Int8Raw()}});
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Tuple);
+ ASSERT_TRUE(t->IsTuple());
+ ASSERT_TRUE(t->GetName().Defined());
+ ASSERT_EQ(t->GetName().GetRef(), "T");
+ ASSERT_EQ(t->GetElements().size(), 2);
+ ASSERT_TRUE(t->GetElements()[0].GetTypeRaw()->IsInt64());
+ ASSERT_EQ(t->GetElements()[0].GetType().Get(), t->GetElements()[0].GetTypeRaw());
+ ASSERT_TRUE(t->GetElements()[1].GetTypeRaw()->IsInt8());
+ ASSERT_EQ(t->GetElements()[1].GetType().Get(), t->GetElements()[1].GetTypeRaw());
+}
+
+TEST(TypeFactoryRaw, VariantOverStruct) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->VariantRaw(f->StructRaw("Inner", {{"x", f->VoidRaw()}}));
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Variant);
+ ASSERT_TRUE(t->IsVariant());
+ ASSERT_FALSE(t->GetName().Defined());
+ ASSERT_TRUE(t->IsVariantOverStruct());
+ ASSERT_FALSE(t->IsVariantOverTuple());
+ ASSERT_EQ(t->GetUnderlyingType()->AsStruct()->GetName().GetRef(), "Inner");
+ ASSERT_EQ(t->GetUnderlyingType().Get(), t->GetUnderlyingTypeRaw());
+}
+
+TEST(TypeFactoryRaw, NamedVariantOverStruct) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->VariantRaw("V", f->StructRaw("Inner", {{"x", f->VoidRaw()}}));
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Variant);
+ ASSERT_TRUE(t->IsVariant());
+ ASSERT_TRUE(t->GetName().Defined());
+ ASSERT_EQ(t->GetName().GetRef(), "V");
+ ASSERT_TRUE(t->IsVariantOverStruct());
+ ASSERT_FALSE(t->IsVariantOverTuple());
+ ASSERT_EQ(t->GetUnderlyingType()->AsStruct()->GetName().GetRef(), "Inner");
+ ASSERT_EQ(t->GetUnderlyingType().Get(), t->GetUnderlyingTypeRaw());
+}
+
+TEST(TypeFactoryRaw, VariantOverTuple) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->VariantRaw(f->TupleRaw("Inner", {{f->VoidRaw()}}));
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Variant);
+ ASSERT_TRUE(t->IsVariant());
+ ASSERT_FALSE(t->GetName().Defined());
+ ASSERT_FALSE(t->IsVariantOverStruct());
+ ASSERT_TRUE(t->IsVariantOverTuple());
+ ASSERT_EQ(t->GetUnderlyingType()->AsTuple()->GetName().GetRef(), "Inner");
+ ASSERT_EQ(t->GetUnderlyingType().Get(), t->GetUnderlyingTypeRaw());
+}
+
+TEST(TypeFactoryRaw, NamedVariantOverTuple) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->VariantRaw("V", f->TupleRaw("Inner", {{f->VoidRaw()}}));
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Variant);
+ ASSERT_TRUE(t->IsVariant());
+ ASSERT_TRUE(t->GetName().Defined());
+ ASSERT_EQ(t->GetName().GetRef(), "V");
+ ASSERT_FALSE(t->IsVariantOverStruct());
+ ASSERT_TRUE(t->IsVariantOverTuple());
+ ASSERT_EQ(t->GetUnderlyingType()->AsTuple()->GetName().GetRef(), "Inner");
+ ASSERT_EQ(t->GetUnderlyingType().Get(), t->GetUnderlyingTypeRaw());
+}
+
+TEST(TypeFactoryRaw, Tagged) {
+ auto f = NTi::PoolFactory(false);
+ auto t = f->TaggedRaw(f->VoidRaw(), "T");
+ ASSERT_EQ(t->GetTypeName(), NTi::ETypeName::Tagged);
+ ASSERT_TRUE(t->IsTagged());
+ ASSERT_EQ(t->GetTag(), "T");
+ ASSERT_TRUE(t->GetItemType()->IsVoid());
+ ASSERT_EQ(t->GetItemType().Get(), t->GetItemTypeRaw());
+}
diff --git a/library/cpp/type_info/ut/type_io.cpp b/library/cpp/type_info/ut/type_io.cpp
new file mode 100644
index 0000000000..4feb9b7d83
--- /dev/null
+++ b/library/cpp/type_info/ut/type_io.cpp
@@ -0,0 +1,535 @@
+#include <library/cpp/testing/unittest/gtest.h>
+
+#include <library/cpp/type_info/type_info.h>
+
+#include "utils.h"
+
+TEST_TF(TypeIO, AsYqlType) {
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Void().Get()),
+ "[VoidType]");
+
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Null().Get()),
+ "[NullType]");
+
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Bool().Get()),
+ "[DataType; Bool]");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Int8().Get()),
+ "[DataType; Int8]");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Int16().Get()),
+ "[DataType; Int16]");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Int32().Get()),
+ "[DataType; Int32]");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Int64().Get()),
+ "[DataType; Int64]");
+
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Uint8().Get()),
+ "[DataType; Uint8]");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Uint16().Get()),
+ "[DataType; Uint16]");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Uint32().Get()),
+ "[DataType; Uint32]");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Uint64().Get()),
+ "[DataType; Uint64]");
+
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Float().Get()),
+ "[DataType; Float]");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Double().Get()),
+ "[DataType; Double]");
+
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.String().Get()),
+ "[DataType; String]");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Utf8().Get()),
+ "[DataType; Utf8]");
+
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Date().Get()),
+ "[DataType; Date]");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Datetime().Get()),
+ "[DataType; Datetime]");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Timestamp().Get()),
+ "[DataType; Timestamp]");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.TzDate().Get()),
+ "[DataType; TzDate]");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.TzDatetime().Get()),
+ "[DataType; TzDatetime]");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.TzTimestamp().Get()),
+ "[DataType; TzTimestamp]");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Interval().Get()),
+ "[DataType; Interval]");
+
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Json().Get()),
+ "[DataType; Json]");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Yson().Get()),
+ "[DataType; Yson]");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Uuid().Get()),
+ "[DataType; Uuid]");
+
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Decimal(20, 10).Get()),
+ "[DataType; Decimal; \"20\"; \"10\"]");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Decimal(35, 35).Get()),
+ "[DataType; Decimal; \"35\"; \"35\"]");
+
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(Optional(f.Bool()).Get()),
+ "[OptionalType; [DataType; Bool]]");
+
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.List(f.Bool()).Get()),
+ "[ListType; [DataType; Bool]]");
+
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Dict(f.Bool(), f.Int32()).Get()),
+ "[DictType; [DataType; Bool]; [DataType; Int32]]");
+
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Struct({}).Get()),
+ "[StructType; []]");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Struct({{"a", f.Bool()}}).Get()),
+ "[StructType; [[a; [DataType; Bool]]]]");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Struct({{"a", f.Yson()}, {"b", f.Bool()}}).Get()),
+ "[StructType; [[a; [DataType; Yson]]; [b; [DataType; Bool]]]]");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Struct({{"a", f.Int32()}, {"b", f.Int32()}, {"c", f.Int64()}}).Get()),
+ "[StructType; [[a; [DataType; Int32]]; [b; [DataType; Int32]]; [c; [DataType; Int64]]]]");
+
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Tuple({}).Get()),
+ "[TupleType; []]");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Tuple({{f.Bool()}}).Get()),
+ "[TupleType; [[DataType; Bool]]]");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Tuple({{f.Yson()}, {f.Bool()}}).Get()),
+ "[TupleType; [[DataType; Yson]; [DataType; Bool]]]");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Tuple({{f.Int32()}, {f.Int32()}, {f.Int64()}}).Get()),
+ "[TupleType; [[DataType; Int32]; [DataType; Int32]; [DataType; Int64]]]");
+
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Variant(f.Struct({{"a", f.Bool()}})).Get()),
+ "[VariantType; [StructType; [[a; [DataType; Bool]]]]]");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Variant(f.Struct({{"a", f.Yson()}, {"b", f.Bool()}})).Get()),
+ "[VariantType; [StructType; [[a; [DataType; Yson]]; [b; [DataType; Bool]]]]]");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Variant(f.Struct({{"a", f.Int32()}, {"b", f.Int32()}, {"c", f.Int64()}})).Get()),
+ "[VariantType; [StructType; [[a; [DataType; Int32]]; [b; [DataType; Int32]]; [c; [DataType; Int64]]]]]");
+
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Variant(f.Tuple({{f.Bool()}})).Get()),
+ "[VariantType; [TupleType; [[DataType; Bool]]]]");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Variant(f.Tuple({{f.Yson()}, {f.Bool()}})).Get()),
+ "[VariantType; [TupleType; [[DataType; Yson]; [DataType; Bool]]]]");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Variant(f.Tuple({{f.Int32()}, {f.Int32()}, {f.Int32()}})).Get()),
+ "[VariantType; [TupleType; [[DataType; Int32]; [DataType; Int32]; [DataType; Int32]]]]");
+
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Tagged(f.String(), "Url").Get()),
+ "[TaggedType; Url; [DataType; String]]");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlType(f.Tagged(f.String(), "Url").Get(), /* includeTags = */ false),
+ "[DataType; String]");
+}
+
+TEST_TF(TypeIO, AsYqlRowSpec) {
+ {
+ auto type = f.Struct({});
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlRowSpec(type.Get()),
+ "{StrictSchema=%true; Type=[StructType; []]}");
+ }
+ {
+ auto type = f.Tagged(f.Struct({}), "Event");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlRowSpec(type.Get()),
+ "{StrictSchema=%true; Type=[StructType; []]}");
+ }
+ {
+ auto type = f.Struct({{"x", f.Tagged(f.String(), "Url")}});
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlRowSpec(type.Get()),
+ "{StrictSchema=%true; Type=[StructType; [[x; [TaggedType; Url; [DataType; String]]]]]}");
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlRowSpec(type.Get(), /* includeTags = */ false),
+ "{StrictSchema=%true; Type=[StructType; [[x; [DataType; String]]]]}");
+ }
+ {
+ auto type = f.Struct({{"a", f.Bool()}});
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlRowSpec(type.Get()),
+ "{StrictSchema=%true; Type=[StructType; [[a; [DataType; Bool]]]]}");
+ }
+ {
+ auto type = f.Struct({{"a", f.Yson()}, {"b", f.Bool()}});
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlRowSpec(type.Get()),
+ "{StrictSchema=%true; Type=[StructType; [[a; [DataType; Yson]]; [b; [DataType; Bool]]]]}");
+ }
+ {
+ auto type = f.Struct({{"a", f.Int32()}, {"b", f.Int32()}, {"c", f.Int32()}});
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYqlRowSpec(type.Get()),
+ "{StrictSchema=%true; Type=[StructType; [[a; [DataType; Int32]]; [b; [DataType; Int32]]; [c; [DataType; Int32]]]]}");
+ }
+
+ UNIT_ASSERT_EXCEPTION_CONTAINS([&f]() {
+ NTi::NIo::AsYqlRowSpec(f.Void().Get());
+ }(),
+ NTi::TApiException, "expected a struct type");
+
+ UNIT_ASSERT_EXCEPTION_CONTAINS([&f]() {
+ NTi::NIo::AsYqlRowSpec(Optional(f.Struct({})).Get());
+ }(),
+ NTi::TApiException, "expected a struct type");
+}
+
+TEST_TF(TypeIO, AsYtSchema) {
+ {
+ auto type = f.Struct({
+ {"void", f.Void()},
+ {"null", f.Null()},
+ {"bool", f.Bool()},
+ {"int8", f.Int8()},
+ {"int16", f.Int16()},
+ {"int32", f.Int32()},
+ {"int64", f.Int64()},
+ {"uint8", f.Uint8()},
+ {"uint16", f.Uint16()},
+ {"uint32", f.Uint32()},
+ {"uint64", f.Uint64()},
+ {"float", f.Float()},
+ {"double", f.Double()},
+ {"string", f.String()},
+ {"utf8", f.Utf8()},
+ {"date", f.Date()},
+ {"datetime", f.Datetime()},
+ {"timestamp", f.Timestamp()},
+ {"tzdate", f.TzDate()},
+ {"tzdatetime", f.TzDatetime()},
+ {"tztimestamp", f.TzTimestamp()},
+ {"interval", f.Interval()},
+ {"decimal", f.Decimal(20, 10)},
+ {"json", f.Json()},
+ {"yson", f.Yson()},
+ {"uuid", f.Uuid()},
+ {"list", f.List(f.Bool())},
+ {"dict", f.Dict(f.Bool(), f.Bool())},
+ {"struct", f.Struct({})},
+ {"tuple", f.Tuple({})},
+ {"variant", f.Variant(f.Struct({{"x", f.Bool()}}))},
+ });
+
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYtSchema(type.Get()),
+ R"(
+ <strict=%true; unique_keys=%false>
+ [
+ {name=void; required=%false; type=any };
+ {name=null; required=%false; type=any };
+ {name=bool; required=%true; type=boolean };
+ {name=int8; required=%true; type=int8 };
+ {name=int16; required=%true; type=int16 };
+ {name=int32; required=%true; type=int32 };
+ {name=int64; required=%true; type=int64 };
+ {name=uint8; required=%true; type=uint8 };
+ {name=uint16; required=%true; type=uint16 };
+ {name=uint32; required=%true; type=uint32 };
+ {name=uint64; required=%true; type=uint64 };
+ {name=float; required=%true; type=double };
+ {name=double; required=%true; type=double };
+ {name=string; required=%true; type=string };
+ {name=utf8; required=%true; type=utf8 };
+ {name=date; required=%true; type=uint16 };
+ {name=datetime; required=%true; type=uint32 };
+ {name=timestamp; required=%true; type=uint64 };
+ {name=tzdate; required=%true; type=string };
+ {name=tzdatetime; required=%true; type=string };
+ {name=tztimestamp; required=%true; type=string };
+ {name=interval; required=%true; type=int64 };
+ {name=decimal; required=%true; type=string };
+ {name=json; required=%true; type=string };
+ {name=yson; required=%false; type=any };
+ {name=uuid; required=%true; type=string };
+ {name=list; required=%false; type=any };
+ {name=dict; required=%false; type=any };
+ {name=struct; required=%false; type=any };
+ {name=tuple; required=%false; type=any };
+ {name=variant; required=%false; type=any };
+ ]
+ )");
+ }
+
+ {
+ auto type = f.Struct({
+ {"void", f.Tagged(f.Void(), "Tag")},
+ {"null", f.Tagged(f.Null(), "Tag")},
+ {"bool", f.Tagged(f.Bool(), "Tag")},
+ {"int8", f.Tagged(f.Int8(), "Tag")},
+ {"int16", f.Tagged(f.Int16(), "Tag")},
+ {"int32", f.Tagged(f.Int32(), "Tag")},
+ {"int64", f.Tagged(f.Int64(), "Tag")},
+ {"uint8", f.Tagged(f.Uint8(), "Tag")},
+ {"uint16", f.Tagged(f.Uint16(), "Tag")},
+ {"uint32", f.Tagged(f.Uint32(), "Tag")},
+ {"uint64", f.Tagged(f.Uint64(), "Tag")},
+ {"float", f.Tagged(f.Float(), "Tag")},
+ {"double", f.Tagged(f.Double(), "Tag")},
+ {"string", f.Tagged(f.String(), "Tag")},
+ {"utf8", f.Tagged(f.Utf8(), "Tag")},
+ {"date", f.Tagged(f.Date(), "Tag")},
+ {"datetime", f.Tagged(f.Datetime(), "Tag")},
+ {"timestamp", f.Tagged(f.Timestamp(), "Tag")},
+ {"tzdate", f.Tagged(f.TzDate(), "Tag")},
+ {"tzdatetime", f.Tagged(f.TzDatetime(), "Tag")},
+ {"tztimestamp", f.Tagged(f.TzTimestamp(), "Tag")},
+ {"interval", f.Tagged(f.Interval(), "Tag")},
+ {"decimal", f.Tagged(f.Decimal(20, 10), "Tag")},
+ {"json", f.Tagged(f.Json(), "Tag")},
+ {"yson", f.Tagged(f.Yson(), "Tag")},
+ {"uuid", f.Tagged(f.Uuid(), "Tag")},
+ {"list", f.Tagged(f.List(f.Bool()), "Tag")},
+ {"dict", f.Tagged(f.Dict(f.Bool(), f.Bool()), "Tag")},
+ {"struct", f.Tagged(f.Struct({}), "Tag")},
+ {"tuple", f.Tagged(f.Tuple({}), "Tag")},
+ {"variant", f.Tagged(f.Variant(f.Struct({{"x", f.Bool()}})), "Tag")},
+ });
+
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYtSchema(type.Get()),
+ R"(
+ <strict=%true; unique_keys=%false>
+ [
+ {name=void; required=%false; type=any };
+ {name=null; required=%false; type=any };
+ {name=bool; required=%true; type=boolean };
+ {name=int8; required=%true; type=int8 };
+ {name=int16; required=%true; type=int16 };
+ {name=int32; required=%true; type=int32 };
+ {name=int64; required=%true; type=int64 };
+ {name=uint8; required=%true; type=uint8 };
+ {name=uint16; required=%true; type=uint16 };
+ {name=uint32; required=%true; type=uint32 };
+ {name=uint64; required=%true; type=uint64 };
+ {name=float; required=%true; type=double };
+ {name=double; required=%true; type=double };
+ {name=string; required=%true; type=string };
+ {name=utf8; required=%true; type=utf8 };
+ {name=date; required=%true; type=uint16 };
+ {name=datetime; required=%true; type=uint32 };
+ {name=timestamp; required=%true; type=uint64 };
+ {name=tzdate; required=%true; type=string };
+ {name=tzdatetime; required=%true; type=string };
+ {name=tztimestamp; required=%true; type=string };
+ {name=interval; required=%true; type=int64 };
+ {name=decimal; required=%true; type=string };
+ {name=json; required=%true; type=string };
+ {name=yson; required=%false; type=any };
+ {name=uuid; required=%true; type=string };
+ {name=list; required=%false; type=any };
+ {name=dict; required=%false; type=any };
+ {name=struct; required=%false; type=any };
+ {name=tuple; required=%false; type=any };
+ {name=variant; required=%false; type=any };
+ ]
+ )");
+ }
+
+ {
+ auto type = f.Struct({
+ {"void", Optional(f.Void())},
+ {"null", Optional(f.Null())},
+ {"bool", Optional(f.Bool())},
+ {"int8", Optional(f.Int8())},
+ {"int16", Optional(f.Int16())},
+ {"int32", Optional(f.Int32())},
+ {"int64", Optional(f.Int64())},
+ {"uint8", Optional(f.Uint8())},
+ {"uint16", Optional(f.Uint16())},
+ {"uint32", Optional(f.Uint32())},
+ {"uint64", Optional(f.Uint64())},
+ {"float", Optional(f.Float())},
+ {"double", Optional(f.Double())},
+ {"string", Optional(f.String())},
+ {"utf8", Optional(f.Utf8())},
+ {"date", Optional(f.Date())},
+ {"datetime", Optional(f.Datetime())},
+ {"timestamp", Optional(f.Timestamp())},
+ {"tzdate", Optional(f.TzDate())},
+ {"tzdatetime", Optional(f.TzDatetime())},
+ {"tztimestamp", Optional(f.TzTimestamp())},
+ {"interval", Optional(f.Interval())},
+ {"decimal", Optional(f.Decimal(20, 10))},
+ {"json", Optional(f.Json())},
+ {"yson", Optional(f.Yson())},
+ {"uuid", Optional(f.Uuid())},
+ {"list", Optional(f.List(f.Bool()))},
+ {"dict", Optional(f.Dict(f.Bool(), f.Bool()))},
+ {"struct", Optional(f.Struct({}))},
+ {"tuple", Optional(f.Tuple({}))},
+ {"variant", Optional(f.Variant(f.Struct({{"x", f.Bool()}})))},
+ });
+
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYtSchema(type.Get()),
+ R"(
+ <strict=%true; unique_keys=%false>
+ [
+ {name=void; required=%false; type=any };
+ {name=null; required=%false; type=any };
+ {name=bool; required=%false; type=boolean };
+ {name=int8; required=%false; type=int8 };
+ {name=int16; required=%false; type=int16 };
+ {name=int32; required=%false; type=int32 };
+ {name=int64; required=%false; type=int64 };
+ {name=uint8; required=%false; type=uint8 };
+ {name=uint16; required=%false; type=uint16 };
+ {name=uint32; required=%false; type=uint32 };
+ {name=uint64; required=%false; type=uint64 };
+ {name=float; required=%false; type=double };
+ {name=double; required=%false; type=double };
+ {name=string; required=%false; type=string };
+ {name=utf8; required=%false; type=utf8 };
+ {name=date; required=%false; type=uint16 };
+ {name=datetime; required=%false; type=uint32 };
+ {name=timestamp; required=%false; type=uint64 };
+ {name=tzdate; required=%false; type=string };
+ {name=tzdatetime; required=%false; type=string };
+ {name=tztimestamp; required=%false; type=string };
+ {name=interval; required=%false; type=int64 };
+ {name=decimal; required=%false; type=string };
+ {name=json; required=%false; type=string };
+ {name=yson; required=%false; type=any };
+ {name=uuid; required=%false; type=string };
+ {name=list; required=%false; type=any };
+ {name=dict; required=%false; type=any };
+ {name=struct; required=%false; type=any };
+ {name=tuple; required=%false; type=any };
+ {name=variant; required=%false; type=any };
+ ]
+ )");
+ }
+
+ {
+ auto type = f.Struct({
+ {"void", Optional(Optional(f.Void()))},
+ {"null", Optional(Optional(f.Null()))},
+ {"bool", Optional(Optional(f.Bool()))},
+ {"int8", Optional(Optional(f.Int8()))},
+ {"int16", Optional(Optional(f.Int16()))},
+ {"int32", Optional(Optional(f.Int32()))},
+ {"int64", Optional(Optional(f.Int64()))},
+ {"uint8", Optional(Optional(f.Uint8()))},
+ {"uint16", Optional(Optional(f.Uint16()))},
+ {"uint32", Optional(Optional(f.Uint32()))},
+ {"uint64", Optional(Optional(f.Uint64()))},
+ {"float", Optional(Optional(f.Float()))},
+ {"double", Optional(Optional(f.Double()))},
+ {"string", Optional(Optional(f.String()))},
+ {"utf8", Optional(Optional(f.Utf8()))},
+ {"date", Optional(Optional(f.Date()))},
+ {"datetime", Optional(Optional(f.Datetime()))},
+ {"timestamp", Optional(Optional(f.Timestamp()))},
+ {"tzdate", Optional(Optional(f.TzDate()))},
+ {"tzdatetime", Optional(Optional(f.TzDatetime()))},
+ {"tztimestamp", Optional(Optional(f.TzTimestamp()))},
+ {"interval", Optional(Optional(f.Interval()))},
+ {"decimal", Optional(Optional(f.Decimal(20, 10)))},
+ {"json", Optional(Optional(f.Json()))},
+ {"yson", Optional(Optional(f.Yson()))},
+ {"uuid", Optional(Optional(f.Uuid()))},
+ {"list", Optional(Optional(f.List(f.Bool())))},
+ {"dict", Optional(Optional(f.Dict(f.Bool(), f.Bool())))},
+ {"struct", Optional(Optional(f.Struct({})))},
+ {"tuple", Optional(Optional(f.Tuple({})))},
+ {"variant", Optional(Optional(f.Variant(f.Struct({{"x", f.Bool()}}))))},
+ });
+
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYtSchema(type.Get()),
+ R"(
+ <strict=%true; unique_keys=%false>
+ [
+ {name=void; required=%false; type=any };
+ {name=null; required=%false; type=any };
+ {name=bool; required=%false; type=any };
+ {name=int8; required=%false; type=any };
+ {name=int16; required=%false; type=any };
+ {name=int32; required=%false; type=any };
+ {name=int64; required=%false; type=any };
+ {name=uint8; required=%false; type=any };
+ {name=uint16; required=%false; type=any };
+ {name=uint32; required=%false; type=any };
+ {name=uint64; required=%false; type=any };
+ {name=float; required=%false; type=any };
+ {name=double; required=%false; type=any };
+ {name=string; required=%false; type=any };
+ {name=utf8; required=%false; type=any };
+ {name=date; required=%false; type=any };
+ {name=datetime; required=%false; type=any };
+ {name=timestamp; required=%false; type=any };
+ {name=tzdate; required=%false; type=any };
+ {name=tzdatetime; required=%false; type=any };
+ {name=tztimestamp; required=%false; type=any };
+ {name=interval; required=%false; type=any };
+ {name=decimal; required=%false; type=any };
+ {name=json; required=%false; type=any };
+ {name=yson; required=%false; type=any };
+ {name=uuid; required=%false; type=any };
+ {name=list; required=%false; type=any };
+ {name=dict; required=%false; type=any };
+ {name=struct; required=%false; type=any };
+ {name=tuple; required=%false; type=any };
+ {name=variant; required=%false; type=any };
+ ]
+ )");
+ }
+
+ UNIT_ASSERT_EXCEPTION_CONTAINS([&f]() {
+ NTi::NIo::AsYtSchema(f.Void().Get());
+ }(),
+ NTi::TApiException, "expected a struct type");
+
+ UNIT_ASSERT_EXCEPTION_CONTAINS([&f]() {
+ NTi::NIo::AsYtSchema(Optional(f.Struct({})).Get());
+ }(),
+ NTi::TApiException, "expected a struct type");
+
+ UNIT_ASSERT_EXCEPTION_CONTAINS([&f]() {
+ NTi::NIo::AsYtSchema(f.Struct({}).Get());
+ }(),
+ NTi::TApiException, "expected a non-empty struct");
+
+ ASSERT_YSON_EQ(
+ NTi::NIo::AsYtSchema(f.Struct({}).Get(), /* failOnEmptyf.Struct = */ false),
+ "<strict=%true; unique_keys=%false>[{name=_yql_fake_column; required=%false; type=boolean}]");
+}
diff --git a/library/cpp/type_info/ut/type_list.cpp b/library/cpp/type_info/ut/type_list.cpp
new file mode 100644
index 0000000000..56d9e16061
--- /dev/null
+++ b/library/cpp/type_info/ut/type_list.cpp
@@ -0,0 +1,64 @@
+#include <library/cpp/testing/unittest/gtest.h>
+
+#include <library/cpp/type_info/type_info.h>
+
+#include <util/generic/serialized_enum.h>
+
+TEST(TypeList, PrimitiveTypeNameSequence) {
+ auto primitiveNames = GetEnumAllValues<NTi::EPrimitiveTypeName>();
+
+ ASSERT_GT(primitiveNames.size(), 0);
+
+ ASSERT_EQ(static_cast<i32>(primitiveNames[0]), 0);
+
+ for (size_t i = 0; i < primitiveNames.size() - 1; ++i) {
+ ASSERT_EQ(static_cast<i32>(primitiveNames[i]) + 1, static_cast<i32>(primitiveNames[i + 1]));
+ }
+}
+
+TEST(TypeList, PrimitiveTypeGroup) {
+ auto primitiveNames = GetEnumAllValues<NTi::EPrimitiveTypeName>();
+
+ for (auto typeName : primitiveNames) {
+ ASSERT_TRUE(NTi::IsPrimitive(static_cast<NTi::ETypeName>(typeName)));
+ }
+}
+
+TEST(TypeList, TypeNameInExactlyOneGroup) {
+ auto allNames = GetEnumAllValues<NTi::ETypeName>();
+
+ for (auto typeName : allNames) {
+ int groups = 0;
+ groups += NTi::IsPrimitive(typeName);
+ groups += NTi::IsSingular(typeName);
+ groups += NTi::IsContainer(typeName);
+ ASSERT_EQ(groups, 1);
+ }
+}
+
+TEST(TypeList, EnumCoherence) {
+ auto primitiveNames = GetEnumAllValues<NTi::EPrimitiveTypeName>();
+ auto allNames = GetEnumAllValues<NTi::ETypeName>();
+
+ ASSERT_GT(allNames.size(), primitiveNames.size());
+
+ size_t i = 0;
+
+ for (; i < primitiveNames.size(); ++i) {
+ ASSERT_EQ(static_cast<i32>(primitiveNames[i]), static_cast<i32>(allNames[i]));
+ ASSERT_EQ(ToString(primitiveNames[i]), ToString(allNames[i]));
+ ASSERT_TRUE(NTi::IsPrimitive(allNames[i]));
+ }
+
+ for (; i < allNames.size(); ++i) {
+ ASSERT_FALSE(NTi::IsPrimitive(allNames[i]));
+ }
+}
+
+TEST(TypeList, Cast) {
+ auto primitiveNames = GetEnumAllValues<NTi::EPrimitiveTypeName>();
+
+ for (auto typeName : primitiveNames) {
+ ASSERT_EQ(typeName, NTi::ToPrimitiveTypeName(NTi::ToTypeName(typeName)));
+ }
+}
diff --git a/library/cpp/type_info/ut/type_serialize.cpp b/library/cpp/type_info/ut/type_serialize.cpp
new file mode 100644
index 0000000000..0d7e184eca
--- /dev/null
+++ b/library/cpp/type_info/ut/type_serialize.cpp
@@ -0,0 +1,251 @@
+#include <library/cpp/testing/unittest/gtest.h>
+
+#include <library/cpp/type_info/type_info.h>
+
+#include "utils.h"
+
+TEST(TypeSerialize, Void) {
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::Void().Get()), R"(void)");
+}
+
+TEST(TypeSerialize, Null) {
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::Null().Get()), R"(null)");
+}
+
+TEST(TypeSerialize, Bool) {
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::Bool().Get()), R"(bool)");
+}
+
+TEST(TypeSerialize, Int8) {
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::Int8().Get()), R"(int8)");
+}
+
+TEST(TypeSerialize, Int16) {
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::Int16().Get()), R"(int16)");
+}
+
+TEST(TypeSerialize, Int32) {
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::Int32().Get()), R"(int32)");
+}
+
+TEST(TypeSerialize, Int64) {
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::Int64().Get()), R"(int64)");
+}
+
+TEST(TypeSerialize, Uint8) {
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::Uint8().Get()), R"(uint8)");
+}
+
+TEST(TypeSerialize, Uint16) {
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::Uint16().Get()), R"(uint16)");
+}
+
+TEST(TypeSerialize, Uint32) {
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::Uint32().Get()), R"(uint32)");
+}
+
+TEST(TypeSerialize, Uint64) {
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::Uint64().Get()), R"(uint64)");
+}
+
+TEST(TypeSerialize, Float) {
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::Float().Get()), R"(float)");
+}
+
+TEST(TypeSerialize, Double) {
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::Double().Get()), R"(double)");
+}
+
+TEST(TypeSerialize, String) {
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::String().Get()), R"(string)");
+}
+
+TEST(TypeSerialize, Utf8) {
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::Utf8().Get()), R"(utf8)");
+}
+
+TEST(TypeSerialize, Date) {
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::Date().Get()), R"(date)");
+}
+
+TEST(TypeSerialize, Datetime) {
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::Datetime().Get()), R"(datetime)");
+}
+
+TEST(TypeSerialize, Timestamp) {
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::Timestamp().Get()), R"(timestamp)");
+}
+
+TEST(TypeSerialize, TzDate) {
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::TzDate().Get()), R"(tz_date)");
+}
+
+TEST(TypeSerialize, TzDatetime) {
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::TzDatetime().Get()), R"(tz_datetime)");
+}
+
+TEST(TypeSerialize, TzTimestamp) {
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::TzTimestamp().Get()), R"(tz_timestamp)");
+}
+
+TEST(TypeSerialize, Interval) {
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::Interval().Get()), R"(interval)");
+}
+
+TEST(TypeSerialize, Decimal) {
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::Decimal(20, 10).Get()), R"({type_name=decimal; precision=20; scale=10})");
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::Decimal(10, 10).Get()), R"({type_name=decimal; precision=10; scale=10})");
+}
+
+TEST(TypeSerialize, Json) {
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::Json().Get()), R"(json)");
+}
+
+TEST(TypeSerialize, Yson) {
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::Yson().Get()), R"(yson)");
+}
+
+TEST(TypeSerialize, Uuid) {
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::Uuid().Get()), R"(uuid)");
+}
+
+TEST(TypeSerialize, Optional) {
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::Optional(NTi::Void()).Get()), R"({type_name=optional; item=void})");
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::Optional(NTi::String()).Get()), R"({type_name=optional; item=string})");
+}
+
+TEST(TypeSerialize, List) {
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::List(NTi::Void()).Get()), R"({type_name=list; item=void})");
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::List(NTi::String()).Get()), R"({type_name=list; item=string})");
+}
+
+TEST(TypeSerialize, Dict) {
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::Dict(NTi::Void(), NTi::Void()).Get()), R"({type_name=dict; key=void; value=void})");
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(NTi::Dict(NTi::Int32(), NTi::String()).Get()), R"({type_name=dict; key=int32; value=string})");
+}
+
+TEST(TypeSerialize, StructEmpty) {
+ ASSERT_YSON_EQ(
+ NTi::NIo::SerializeYson(
+ NTi::Struct({}).Get()),
+ R"({type_name=struct; members=[]})");
+}
+
+TEST(TypeSerialize, Struct) {
+ ASSERT_YSON_EQ(
+ NTi::NIo::SerializeYson(
+ NTi::Struct({{"ItemB", NTi::String()}, {"ItemA", NTi::List(NTi::Int64())}}).Get()),
+ R"({type_name=struct; members=[{name=ItemB; type=string}; {name=ItemA; type={type_name=list; item=int64}}]})");
+}
+
+TEST(TypeSerialize, StructNamedEmpty) {
+ ASSERT_YSON_EQ(
+ NTi::NIo::SerializeYson(
+ NTi::Struct("S", {}).Get()),
+ R"({type_name=struct; members=[]})");
+}
+
+TEST(TypeSerialize, StructNamedEmptyNoNames) {
+ ASSERT_YSON_EQ(
+ NTi::NIo::SerializeYson(
+ NTi::Struct("S", {}).Get(),
+ /* binary = */ false,
+ /* includeTags = */ true),
+ R"({type_name=struct; members=[]})");
+}
+
+TEST(TypeSerialize, StructNamed) {
+ ASSERT_YSON_EQ(
+ NTi::NIo::SerializeYson(
+ NTi::Struct("S", {{"ItemB", NTi::String()}, {"ItemA", NTi::List(NTi::Int64())}}).Get()),
+ R"({type_name=struct; members=[{name=ItemB; type=string}; {name=ItemA; type={type_name=list; item=int64}}]})");
+}
+
+TEST(TypeSerialize, TupleEmpty) {
+ ASSERT_YSON_EQ(
+ NTi::NIo::SerializeYson(
+ NTi::Tuple({}).Get()),
+ R"({type_name=tuple; elements=[]})");
+}
+
+TEST(TypeSerialize, Tuple) {
+ ASSERT_YSON_EQ(
+ NTi::NIo::SerializeYson(
+ NTi::Tuple({{NTi::String()}, {NTi::List(NTi::Int64())}}).Get()),
+ R"({type_name=tuple; elements=[{type=string}; {type={type_name=list; item=int64}}]})");
+}
+
+TEST(TypeSerialize, TupleNamedEmpty) {
+ ASSERT_YSON_EQ(
+ NTi::NIo::SerializeYson(
+ NTi::Tuple("S", {}).Get()),
+ R"({type_name=tuple; elements=[]})");
+}
+
+TEST(TypeSerialize, TupleNamedEmptyNoNames) {
+ ASSERT_YSON_EQ(
+ NTi::NIo::SerializeYson(
+ NTi::Tuple("S", {}).Get(),
+ /* binary = */ false),
+ R"({type_name=tuple; elements=[]})");
+}
+
+TEST(TypeSerialize, VariantStruct) {
+ ASSERT_YSON_EQ(
+ NTi::NIo::SerializeYson(
+ NTi::Variant(
+ NTi::Struct({{"ItemB", NTi::String()}, {"ItemA", NTi::List(NTi::Int64())}}))
+ .Get()),
+ R"({type_name=variant; members=[{name=ItemB; type=string}; {name=ItemA; type={type_name=list; item=int64}}]})");
+}
+
+TEST(TypeSerialize, VariantTuple) {
+ ASSERT_YSON_EQ(
+ NTi::NIo::SerializeYson(
+ NTi::Variant(
+ NTi::Tuple({
+ {NTi::String()},
+ {NTi::List(NTi::Int64())},
+ }))
+ .Get()),
+ R"({type_name=variant; elements=[{type=string}; {type={type_name=list; item=int64}}]})");
+}
+
+TEST(TypeSerialize, Tagged) {
+ ASSERT_YSON_EQ(
+ NTi::NIo::SerializeYson(
+ NTi::Tagged(NTi::String(), "Url").Get()),
+ R"({type_name=tagged; tag=Url; item=string})");
+}
+
+TEST(TypeSerialize, TaggedNoTags) {
+ ASSERT_YSON_EQ(
+ NTi::NIo::SerializeYson(
+ NTi::Tagged(NTi::String(), "Url").Get(),
+ /* binary = */ false,
+ /* includeTags = */ false),
+ R"(string)");
+}
+
+TEST(TypeSerialize, MultipleType) {
+ auto result = TString();
+
+ {
+ auto writer = NYsonPull::MakeTextWriter(NYsonPull::NOutput::FromString(&result), NYsonPull::EStreamType::ListFragment);
+
+ writer.BeginStream();
+ NTi::NIo::SerializeYsonMultiple(NTi::Optional(NTi::String()).Get(), writer.GetConsumer());
+ NTi::NIo::SerializeYsonMultiple(NTi::List(NTi::Utf8()).Get(), writer.GetConsumer());
+ writer.EndStream();
+ }
+
+ ASSERT_YSON_EQ("[" + result + "]", R"([{type_name=optional; item=string}; {type_name=list; item=utf8}])");
+}
+
+TEST(TypeSerialize, Binary) {
+ ASSERT_YSON_EQ(
+ NTi::NIo::SerializeYson(
+ NTi::Tagged(NTi::String(), "Url").Get(),
+ /* binary = */ true),
+ R"({type_name=tagged; tag=Url; item=string})");
+}
diff --git a/library/cpp/type_info/ut/type_show.cpp b/library/cpp/type_info/ut/type_show.cpp
new file mode 100644
index 0000000000..cde0d9371a
--- /dev/null
+++ b/library/cpp/type_info/ut/type_show.cpp
@@ -0,0 +1,199 @@
+#include <library/cpp/testing/unittest/gtest.h>
+
+#include <library/cpp/type_info/type_info.h>
+
+#include "utils.h"
+
+TEST_TF(TypeShow, Void) {
+ ASSERT_EQ(ToString(*f.Void()), "Void");
+}
+
+TEST_TF(TypeShow, Null) {
+ ASSERT_EQ(ToString(*f.Null()), "Null");
+}
+
+TEST_TF(TypeShow, Bool) {
+ ASSERT_EQ(ToString(*f.Bool()), "Bool");
+}
+
+TEST_TF(TypeShow, Int8) {
+ ASSERT_EQ(ToString(*f.Int8()), "Int8");
+}
+
+TEST_TF(TypeShow, Int16) {
+ ASSERT_EQ(ToString(*f.Int16()), "Int16");
+}
+
+TEST_TF(TypeShow, Int32) {
+ ASSERT_EQ(ToString(*f.Int32()), "Int32");
+}
+
+TEST_TF(TypeShow, Int64) {
+ ASSERT_EQ(ToString(*f.Int64()), "Int64");
+}
+
+TEST_TF(TypeShow, Uint8) {
+ ASSERT_EQ(ToString(*f.Uint8()), "Uint8");
+}
+
+TEST_TF(TypeShow, Uint16) {
+ ASSERT_EQ(ToString(*f.Uint16()), "Uint16");
+}
+
+TEST_TF(TypeShow, Uint32) {
+ ASSERT_EQ(ToString(*f.Uint32()), "Uint32");
+}
+
+TEST_TF(TypeShow, Uint64) {
+ ASSERT_EQ(ToString(*f.Uint64()), "Uint64");
+}
+
+TEST_TF(TypeShow, Float) {
+ ASSERT_EQ(ToString(*f.Float()), "Float");
+}
+
+TEST_TF(TypeShow, Double) {
+ ASSERT_EQ(ToString(*f.Double()), "Double");
+}
+
+TEST_TF(TypeShow, String) {
+ ASSERT_EQ(ToString(*f.String()), "String");
+}
+
+TEST_TF(TypeShow, Utf8) {
+ ASSERT_EQ(ToString(*f.Utf8()), "Utf8");
+}
+
+TEST_TF(TypeShow, Date) {
+ ASSERT_EQ(ToString(*f.Date()), "Date");
+}
+
+TEST_TF(TypeShow, Datetime) {
+ ASSERT_EQ(ToString(*f.Datetime()), "Datetime");
+}
+
+TEST_TF(TypeShow, Timestamp) {
+ ASSERT_EQ(ToString(*f.Timestamp()), "Timestamp");
+}
+
+TEST_TF(TypeShow, TzDate) {
+ ASSERT_EQ(ToString(*f.TzDate()), "TzDate");
+}
+
+TEST_TF(TypeShow, TzDatetime) {
+ ASSERT_EQ(ToString(*f.TzDatetime()), "TzDatetime");
+}
+
+TEST_TF(TypeShow, TzTimestamp) {
+ ASSERT_EQ(ToString(*f.TzTimestamp()), "TzTimestamp");
+}
+
+TEST_TF(TypeShow, Interval) {
+ ASSERT_EQ(ToString(*f.Interval()), "Interval");
+}
+
+TEST_TF(TypeShow, Decimal) {
+ ASSERT_EQ(ToString(*f.Decimal(20, 10)), "Decimal(20, 10)");
+}
+
+TEST_TF(TypeShow, Json) {
+ ASSERT_EQ(ToString(*f.Json()), "Json");
+}
+
+TEST_TF(TypeShow, Yson) {
+ ASSERT_EQ(ToString(*f.Yson()), "Yson");
+}
+
+TEST_TF(TypeShow, Uuid) {
+ ASSERT_EQ(ToString(*f.Uuid()), "Uuid");
+}
+
+TEST_TF(TypeShow, Optional) {
+ ASSERT_EQ(ToString(*f.Optional(f.String())), "Optional<String>");
+}
+
+TEST_TF(TypeShow, List) {
+ ASSERT_EQ(ToString(*f.List(f.String())), "List<String>");
+}
+
+TEST_TF(TypeShow, Dict) {
+ ASSERT_EQ(ToString(*f.Dict(f.String(), f.Int32())), "Dict<String, Int32>");
+}
+
+TEST_TF(TypeShow, Struct) {
+ ASSERT_EQ(
+ ToString(*f.Struct({})),
+ "Struct<>");
+ ASSERT_EQ(
+ ToString(*f.Struct({{"x1", f.Void()}})),
+ "Struct<'x1': Void>");
+ ASSERT_EQ(
+ ToString(*f.Struct({{"x1", f.Void()}, {"x2", f.String()}})),
+ "Struct<'x1': Void, 'x2': String>");
+ ASSERT_EQ(
+ ToString(*f.Struct("Name", {})),
+ "Struct['Name']<>");
+ ASSERT_EQ(
+ ToString(*f.Struct("Name", {{"x1", f.Void()}})),
+ "Struct['Name']<'x1': Void>");
+ ASSERT_EQ(
+ ToString(*f.Struct("Name", {{"x1", f.Void()}, {"x2", f.String()}})),
+ "Struct['Name']<'x1': Void, 'x2': String>");
+}
+
+TEST_TF(TypeShow, Tuple) {
+ ASSERT_EQ(
+ ToString(*f.Tuple({})),
+ "Tuple<>");
+ ASSERT_EQ(
+ ToString(*f.Tuple({{f.Void()}})),
+ "Tuple<Void>");
+ ASSERT_EQ(
+ ToString(*f.Tuple({{f.Void()}, {f.String()}})),
+ "Tuple<Void, String>");
+ ASSERT_EQ(
+ ToString(*f.Tuple("Name", {})),
+ "Tuple['Name']<>");
+ ASSERT_EQ(
+ ToString(*f.Tuple("Name", {{f.Void()}})),
+ "Tuple['Name']<Void>");
+ ASSERT_EQ(
+ ToString(*f.Tuple("Name", {{f.Void()}, {f.String()}})),
+ "Tuple['Name']<Void, String>");
+}
+
+TEST_TF(TypeShow, VariantStruct) {
+ ASSERT_EQ(
+ ToString(*f.Variant(f.Struct({{"x1", f.Void()}}))),
+ "Variant<'x1': Void>");
+ ASSERT_EQ(
+ ToString(*f.Variant(f.Struct({{"x1", f.Void()}, {"x2", f.String()}}))),
+ "Variant<'x1': Void, 'x2': String>");
+ ASSERT_EQ(
+ ToString(*f.Variant("Name", f.Struct({{"x1", f.Void()}}))),
+ "Variant['Name']<'x1': Void>");
+ ASSERT_EQ(
+ ToString(*f.Variant("Name", f.Struct({{"x1", f.Void()}, {"x2", f.String()}}))),
+ "Variant['Name']<'x1': Void, 'x2': String>");
+}
+
+TEST_TF(TypeShow, VariantTuple) {
+ ASSERT_EQ(
+ ToString(*f.Variant(f.Tuple({{f.Void()}}))),
+ "Variant<Void>");
+ ASSERT_EQ(
+ ToString(*f.Variant(f.Tuple({{f.Void()}, {f.String()}}))),
+ "Variant<Void, String>");
+ ASSERT_EQ(
+ ToString(*f.Variant("Name", f.Tuple({{f.Void()}}))),
+ "Variant['Name']<Void>");
+ ASSERT_EQ(
+ ToString(*f.Variant("Name", f.Tuple({{f.Void()}, {f.String()}}))),
+ "Variant['Name']<Void, String>");
+}
+
+TEST_TF(TypeShow, Tagged) {
+ ASSERT_EQ(
+ ToString(*f.Tagged(f.Void(), "Tag")),
+ "Tagged<Void, 'Tag'>");
+}
diff --git a/library/cpp/type_info/ut/type_strip_tags.cpp b/library/cpp/type_info/ut/type_strip_tags.cpp
new file mode 100644
index 0000000000..b3ef4c0825
--- /dev/null
+++ b/library/cpp/type_info/ut/type_strip_tags.cpp
@@ -0,0 +1,31 @@
+#include <library/cpp/testing/unittest/gtest.h>
+
+#include <library/cpp/type_info/type_info.h>
+
+#include "utils.h"
+
+TEST_TF(TypeStripTags, StripTags) {
+ auto t = f.List(f.Tagged(f.Void(), "Tag"));
+
+ ASSERT_EQ(t->StripTags().Get(), t.Get());
+ ASSERT_EQ(f.Tagged(t, "Tag")->StripTags().Get(), t.Get());
+ ASSERT_EQ(f.Tagged(f.Tagged(t, "Tag"), "Tag2")->StripTags().Get(), t.Get());
+}
+
+TEST_TF(TypeStripTags, StripOptionals) {
+ auto t = f.Tagged(f.Optional(f.Void()), "Tag");
+
+ ASSERT_EQ(t->StripOptionals().Get(), t.Get());
+ ASSERT_EQ(f.Optional(t)->StripOptionals().Get(), t.Get());
+ ASSERT_EQ(f.Optional(f.Optional(t))->StripOptionals().Get(), t.Get());
+}
+
+TEST_TF(TypeStripTags, StripTagsAndOptionals) {
+ auto t = f.Void();
+
+ ASSERT_EQ(t->StripTagsAndOptionals().Get(), t.Get());
+ ASSERT_EQ(f.Optional(t)->StripTagsAndOptionals().Get(), t.Get());
+ ASSERT_EQ(f.Optional(f.Optional(t))->StripTagsAndOptionals().Get(), t.Get());
+ ASSERT_EQ(f.Optional(f.Tagged(t, "Tag"))->StripTagsAndOptionals().Get(), t.Get());
+ ASSERT_EQ(f.Optional(f.Optional(f.Optional(t)))->StripTagsAndOptionals().Get(), t.Get());
+}
diff --git a/library/cpp/type_info/ut/utils.h b/library/cpp/type_info/ut/utils.h
new file mode 100644
index 0000000000..b35316cb2f
--- /dev/null
+++ b/library/cpp/type_info/ut/utils.h
@@ -0,0 +1,73 @@
+#pragma once
+
+//! @file utils.h
+//!
+//! Infrastructure for running type info tests with different factories and settings.
+
+#include <library/cpp/testing/unittest/registar.h>
+
+#include <library/cpp/yson/consumer.h>
+#include <library/cpp/yson/node/node.h>
+#include <library/cpp/yson/node/node_builder.h>
+#include <library/cpp/yson/node/node_io.h>
+
+#include <library/cpp/type_info/fwd.h>
+
+template <typename T>
+inline TString ToCanonicalYson(const T& value) {
+ return NYT::NodeToCanonicalYsonString(NYT::NodeFromYsonString(TStringBuf(value)));
+}
+
+/// Assert that two YSON strings are equal.
+#define ASSERT_YSON_EQ(L, R) ASSERT_EQ(ToCanonicalYson(L), ToCanonicalYson(R))
+
+/// Assert that two types are strictly equal.
+#define ASSERT_STRICT_EQ(a, b) \
+ do { \
+ auto lhs = (a); \
+ auto rhs = (b); \
+ ASSERT_TRUE(NTi::NEq::TStrictlyEqual()(lhs, rhs)); \
+ ASSERT_TRUE(NTi::NEq::TStrictlyEqual().IgnoreHash(lhs, rhs)); \
+ ASSERT_EQ(lhs->GetHash(), NTi::NEq::TStrictlyEqualHash()(lhs)); \
+ ASSERT_EQ(rhs->GetHash(), NTi::NEq::TStrictlyEqualHash()(rhs)); \
+ ASSERT_EQ(lhs->GetHash(), rhs->GetHash()); \
+ } while (false)
+
+/// Assert that two types are strictly unequal.
+///
+/// Note: we check that, if types are not equal, their hashes are also not equal.
+/// While this is not guaranteed, we haven't seen any collisions so far.
+/// If some collision happen, check if hashing isn't broken before removing the assert.
+#define ASSERT_STRICT_NE(a, b) \
+ do { \
+ auto lhs = (a); \
+ auto rhs = (b); \
+ ASSERT_FALSE(NTi::NEq::TStrictlyEqual()(lhs, rhs)); \
+ ASSERT_FALSE(NTi::NEq::TStrictlyEqual().IgnoreHash(lhs, rhs)); \
+ ASSERT_EQ(lhs->GetHash(), NTi::NEq::TStrictlyEqualHash()(lhs)); \
+ ASSERT_EQ(rhs->GetHash(), NTi::NEq::TStrictlyEqualHash()(rhs)); \
+ ASSERT_NE(lhs->GetHash(), rhs->GetHash()); \
+ } while (false)
+
+/// Assert that a type string is equal to the given type after deserialization.
+#define ASSERT_DESERIALIZED_EQ(canonicalType, serializedType) \
+ do { \
+ auto reader = NYsonPull::TReader(NYsonPull::NInput::FromMemory(serializedType), NYsonPull::EStreamType::Node); \
+ auto deserializedType = NTi::NIo::DeserializeYson(*NTi::HeapFactory(), reader); \
+ ASSERT_STRICT_EQ(deserializedType, canonicalType); \
+ ASSERT_YSON_EQ(NTi::NIo::SerializeYson(canonicalType.Get()), NTi::NIo::SerializeYson(deserializedType.Get())); \
+ } while (false)
+
+/// Test parametrized over different type factories.
+#define TEST_TF(N, NN) \
+ void Test##N##NN(NTi::ITypeFactory& f); \
+ TEST(N, NN##_Heap) { \
+ Test##N##NN(*NTi::HeapFactory()); \
+ } \
+ TEST(N, NN##_Pool) { \
+ Test##N##NN(*NTi::PoolFactory(false)); \
+ } \
+ TEST(N, NN##_PoolDedup) { \
+ Test##N##NN(*NTi::PoolFactory(true)); \
+ } \
+ void Test##N##NN(NTi::ITypeFactory& f)
diff --git a/library/cpp/type_info/ut/ya.make b/library/cpp/type_info/ut/ya.make
new file mode 100644
index 0000000000..dc27e2a7db
--- /dev/null
+++ b/library/cpp/type_info/ut/ya.make
@@ -0,0 +1,32 @@
+UNITTEST()
+
+SRCS(
+ builder.cpp
+ type_basics.cpp
+ type_complexity_ut.cpp
+ type_constraints.cpp
+ type_deserialize.cpp
+ type_equivalence.cpp
+ type_factory.cpp
+ type_factory_raw.cpp
+ type_io.cpp
+ type_list.cpp
+ type_serialize.cpp
+ type_show.cpp
+ type_strip_tags.cpp
+ test_data.cpp
+)
+
+PEERDIR(
+ library/cpp/type_info
+ library/cpp/yson
+ library/cpp/yson/node
+ library/cpp/resource
+)
+
+RESOURCE(
+ ${ARCADIA_ROOT}/library/cpp/type_info/test-data/good-types.txt /good
+ ${ARCADIA_ROOT}/library/cpp/type_info/test-data/bad-types.txt /bad
+)
+
+END()
diff --git a/library/cpp/type_info/ya.make b/library/cpp/type_info/ya.make
new file mode 100644
index 0000000000..555f0d1433
--- /dev/null
+++ b/library/cpp/type_info/ya.make
@@ -0,0 +1,26 @@
+LIBRARY()
+
+SRCS(
+ type_info.cpp
+
+ builder.cpp
+ error.cpp
+ type.cpp
+ type_complexity.cpp
+ type_equivalence.cpp
+ type_factory.cpp
+ type_io.cpp
+ type_list.cpp
+)
+
+GENERATE_ENUM_SERIALIZATION(
+ type_list.h
+)
+
+PEERDIR(
+ library/cpp/yson_pull
+)
+
+END()
+
+RECURSE_FOR_TESTS(ut)