aboutsummaryrefslogtreecommitdiffstats
path: root/util/string
diff options
context:
space:
mode:
authorAlexey Salmin <alexey.salmin@gmail.com>2022-02-10 16:49:37 +0300
committerDaniil Cherednik <dcherednik@yandex-team.ru>2022-02-10 16:49:37 +0300
commit71af077a5dfe7e9f932a508422c2dac81a57ebc0 (patch)
tree5d5cb817648f650d76cf1076100726fd9b8448e8 /util/string
parent3c5b1607b38f637d2f3313791ed25c2e080d2647 (diff)
downloadydb-71af077a5dfe7e9f932a508422c2dac81a57ebc0.tar.gz
Restoring authorship annotation for Alexey Salmin <alexey.salmin@gmail.com>. Commit 2 of 2.
Diffstat (limited to 'util/string')
-rw-r--r--util/string/benchmark/join/main.cpp160
-rw-r--r--util/string/benchmark/join/metrics/main.py8
-rw-r--r--util/string/benchmark/join/metrics/ya.make28
-rw-r--r--util/string/benchmark/join/ya.make22
-rw-r--r--util/string/benchmark/ya.make4
-rw-r--r--util/string/cast.h8
-rw-r--r--util/string/join.h158
-rw-r--r--util/string/join_ut.cpp56
8 files changed, 222 insertions, 222 deletions
diff --git a/util/string/benchmark/join/main.cpp b/util/string/benchmark/join/main.cpp
index 94466069d4..1a8633d3a8 100644
--- a/util/string/benchmark/join/main.cpp
+++ b/util/string/benchmark/join/main.cpp
@@ -1,70 +1,70 @@
#include <library/cpp/testing/benchmark/bench.h>
-
-#include <util/generic/function.h>
-#include <util/generic/singleton.h>
-#include <util/generic/vector.h>
-#include <util/generic/xrange.h>
-#include <util/random/fast.h>
-#include <util/string/cast.h>
-#include <util/string/join.h>
-
-namespace {
- // This class assigns random values to variadic lists of variables of different types.
- // It can be used to randomize a tuple via Apply() (arcadia version of std::apply).
- class TRandomizer {
- public:
- TRandomizer(ui64 seed)
- : Prng(seed)
- {
- }
-
- void Randomize(ui16& i) {
- i = static_cast<ui16>(Prng.GenRand());
- }
-
- void Randomize(ui32& i) {
- i = static_cast<ui32>(Prng.GenRand());
- }
-
- void Randomize(double& d) {
- d = Prng.GenRandReal4() + Prng.Uniform(Max<ui16>());
- }
-
- void Randomize(TString& s) {
- s = ::ToString(Prng.GenRand());
- }
-
+
+#include <util/generic/function.h>
+#include <util/generic/singleton.h>
+#include <util/generic/vector.h>
+#include <util/generic/xrange.h>
+#include <util/random/fast.h>
+#include <util/string/cast.h>
+#include <util/string/join.h>
+
+namespace {
+ // This class assigns random values to variadic lists of variables of different types.
+ // It can be used to randomize a tuple via Apply() (arcadia version of std::apply).
+ class TRandomizer {
+ public:
+ TRandomizer(ui64 seed)
+ : Prng(seed)
+ {
+ }
+
+ void Randomize(ui16& i) {
+ i = static_cast<ui16>(Prng.GenRand());
+ }
+
+ void Randomize(ui32& i) {
+ i = static_cast<ui32>(Prng.GenRand());
+ }
+
+ void Randomize(double& d) {
+ d = Prng.GenRandReal4() + Prng.Uniform(Max<ui16>());
+ }
+
+ void Randomize(TString& s) {
+ s = ::ToString(Prng.GenRand());
+ }
+
template <typename T, typename... TArgs>
- void Randomize(T& t, TArgs&... args) {
- Randomize(t);
- Randomize(args...);
- }
-
- private:
- TFastRng<ui64> Prng;
- };
-
+ void Randomize(T& t, TArgs&... args) {
+ Randomize(t);
+ Randomize(args...);
+ }
+
+ private:
+ TFastRng<ui64> Prng;
+ };
+
template <size_t N, typename... T>
- struct TExamplesHolder {
+ struct TExamplesHolder {
using TExamples = TVector<std::tuple<T...>>;
- TExamples Examples;
-
- TExamplesHolder()
- : Examples(N)
- {
- TRandomizer r{N * sizeof(typename TExamples::value_type) * 42};
- for (auto& x : Examples) {
+ TExamples Examples;
+
+ TExamplesHolder()
+ : Examples(N)
+ {
+ TRandomizer r{N * sizeof(typename TExamples::value_type) * 42};
+ for (auto& x : Examples) {
Apply([&r](T&... t) { r.Randomize(t...); }, x);
- }
- }
- };
-
+ }
+ }
+ };
+
template <typename... TArgs>
- TString JoinTuple(std::tuple<TArgs...> t) {
- return Apply([](TArgs... x) -> TString { return Join("-", x...); }, t);
- }
-}
-
+ TString JoinTuple(std::tuple<TArgs...> t) {
+ return Apply([](TArgs... x) -> TString { return Join("-", x...); }, t);
+ }
+}
+
#define DEFINE_BENCHMARK(count, types, ...) \
Y_CPU_BENCHMARK(Join_##count##_##types, iface) { \
const auto& examples = Default<TExamplesHolder<count, __VA_ARGS__>>().Examples; \
@@ -74,22 +74,22 @@ namespace {
Y_DO_NOT_OPTIMIZE_AWAY(JoinTuple(e)); \
} \
} \
- }
-
-DEFINE_BENCHMARK(100, SS, TString, TString);
-DEFINE_BENCHMARK(100, SSS, TString, TString, TString);
-DEFINE_BENCHMARK(100, SSSSS, TString, TString, TString, TString, TString);
-
-DEFINE_BENCHMARK(100, ss, ui16, ui16);
-DEFINE_BENCHMARK(100, SsS, TString, ui16, TString);
-DEFINE_BENCHMARK(100, SsSsS, TString, ui16, TString, ui16, TString);
-
-DEFINE_BENCHMARK(100, ii, ui32, ui32);
-DEFINE_BENCHMARK(100, SiS, TString, ui32, TString);
-DEFINE_BENCHMARK(100, SiSiS, TString, ui32, TString, ui32, TString);
-
-DEFINE_BENCHMARK(100, dd, double, double);
-DEFINE_BENCHMARK(100, SdS, TString, double, TString);
-DEFINE_BENCHMARK(100, SdSdS, TString, double, TString, double, TString);
-
-#undef DEFINE_BENCHMARK
+ }
+
+DEFINE_BENCHMARK(100, SS, TString, TString);
+DEFINE_BENCHMARK(100, SSS, TString, TString, TString);
+DEFINE_BENCHMARK(100, SSSSS, TString, TString, TString, TString, TString);
+
+DEFINE_BENCHMARK(100, ss, ui16, ui16);
+DEFINE_BENCHMARK(100, SsS, TString, ui16, TString);
+DEFINE_BENCHMARK(100, SsSsS, TString, ui16, TString, ui16, TString);
+
+DEFINE_BENCHMARK(100, ii, ui32, ui32);
+DEFINE_BENCHMARK(100, SiS, TString, ui32, TString);
+DEFINE_BENCHMARK(100, SiSiS, TString, ui32, TString, ui32, TString);
+
+DEFINE_BENCHMARK(100, dd, double, double);
+DEFINE_BENCHMARK(100, SdS, TString, double, TString);
+DEFINE_BENCHMARK(100, SdSdS, TString, double, TString, double, TString);
+
+#undef DEFINE_BENCHMARK
diff --git a/util/string/benchmark/join/metrics/main.py b/util/string/benchmark/join/metrics/main.py
index 60eb0e470f..1ed5014808 100644
--- a/util/string/benchmark/join/metrics/main.py
+++ b/util/string/benchmark/join/metrics/main.py
@@ -1,5 +1,5 @@
-import yatest.common as yc
-
-
-def test_export_metrics(metrics):
+import yatest.common as yc
+
+
+def test_export_metrics(metrics):
metrics.set_benchmark(yc.execute_benchmark('util/string/benchmark/join/join', threads=8))
diff --git a/util/string/benchmark/join/metrics/ya.make b/util/string/benchmark/join/metrics/ya.make
index bdbf806aa2..08ff3a149f 100644
--- a/util/string/benchmark/join/metrics/ya.make
+++ b/util/string/benchmark/join/metrics/ya.make
@@ -1,21 +1,21 @@
-OWNER(
- salmin
- g:util
-)
+OWNER(
+ salmin
+ g:util
+)
SUBSCRIBER(g:util-subscribers)
-
+
PY2TEST()
-
+
SIZE(LARGE)
-
-TAG(
+
+TAG(
ya:force_sandbox
- sb:intel_e5_2660v1
+ sb:intel_e5_2660v1
ya:fat
-)
-
+)
+
TEST_SRCS(main.py)
-
+
DEPENDS(util/string/benchmark/join)
-
-END()
+
+END()
diff --git a/util/string/benchmark/join/ya.make b/util/string/benchmark/join/ya.make
index 6ba2d2dadb..dfcc1d264e 100644
--- a/util/string/benchmark/join/ya.make
+++ b/util/string/benchmark/join/ya.make
@@ -1,13 +1,13 @@
Y_BENCHMARK()
-
-OWNER(
- salmin
- g:util
-)
+
+OWNER(
+ salmin
+ g:util
+)
SUBSCRIBER(g:util-subscribers)
-
-SRCS(
- main.cpp
-)
-
-END()
+
+SRCS(
+ main.cpp
+)
+
+END()
diff --git a/util/string/benchmark/ya.make b/util/string/benchmark/ya.make
index e6ab78b6f6..266b53c7b3 100644
--- a/util/string/benchmark/ya.make
+++ b/util/string/benchmark/ya.make
@@ -9,8 +9,8 @@ RECURSE(
cast
float_to_string
float_to_string/metrics
- join
- join/metrics
+ join
+ join/metrics
subst_global
subst_global/metrics
)
diff --git a/util/string/cast.h b/util/string/cast.h
index 9c230d1095..90e925c194 100644
--- a/util/string/cast.h
+++ b/util/string/cast.h
@@ -69,10 +69,10 @@ namespace NPrivate {
template <class T>
struct TToString<T, false> {
static inline TString Cvt(const T& t) {
- TString s;
- TStringOutput o(s);
- o << t;
- return s;
+ TString s;
+ TStringOutput o(s);
+ o << t;
+ return s;
}
};
}
diff --git a/util/string/join.h b/util/string/join.h
index a49e6dfe50..b166fad1f3 100644
--- a/util/string/join.h
+++ b/util/string/join.h
@@ -5,31 +5,31 @@
#include <util/string/cast.h>
#include "cast.h"
-/*
- * Default implementation of AppendToString uses a temporary TString object which is inefficient. You can overload it
- * for your type to speed up string joins. If you already have an Out() or operator<<() implementation you can simply
- * do the following:
- *
- * inline void AppendToString(TString& dst, const TMyType& t) {
- * TStringOutput o(dst);
- * o << t;
- * }
- *
- * Unfortunately we can't do this by default because for some types ToString() is defined while Out() is not.
- * For standard types (strings of all kinds and arithmetic types) we don't use a temporary TString in AppendToString().
- */
-
+/*
+ * Default implementation of AppendToString uses a temporary TString object which is inefficient. You can overload it
+ * for your type to speed up string joins. If you already have an Out() or operator<<() implementation you can simply
+ * do the following:
+ *
+ * inline void AppendToString(TString& dst, const TMyType& t) {
+ * TStringOutput o(dst);
+ * o << t;
+ * }
+ *
+ * Unfortunately we can't do this by default because for some types ToString() is defined while Out() is not.
+ * For standard types (strings of all kinds and arithmetic types) we don't use a temporary TString in AppendToString().
+ */
+
template <typename TCharType, typename T>
-inline std::enable_if_t<!std::is_arithmetic<std::remove_cv_t<T>>::value, void>
+inline std::enable_if_t<!std::is_arithmetic<std::remove_cv_t<T>>::value, void>
AppendToString(TBasicString<TCharType>& dst, const T& t) {
dst.AppendNoAlias(ToString(t));
-}
+}
template <typename TCharType, typename T>
-inline std::enable_if_t<std::is_arithmetic<std::remove_cv_t<T>>::value, void>
+inline std::enable_if_t<std::is_arithmetic<std::remove_cv_t<T>>::value, void>
AppendToString(TBasicString<TCharType>& dst, const T& t) {
- char buf[512];
- dst.append(buf, ToString<std::remove_cv_t<T>>(t, buf, sizeof(buf)));
+ char buf[512];
+ dst.append(buf, ToString<std::remove_cv_t<T>>(t, buf, sizeof(buf)));
}
template <typename TCharType>
@@ -42,59 +42,59 @@ inline void AppendToString(TBasicString<TCharType>& dst, TBasicStringBuf<TCharTy
dst.append(t);
}
-namespace NPrivate {
- template <typename T>
- inline size_t GetLength(const T&) {
- // By default don't pre-allocate space when joining and appending non-string types.
- // This code can be extended by estimating stringified length for specific types (e.g. 10 for ui32).
- return 0;
- }
-
- template <>
- inline size_t GetLength(const TString& s) {
- return s.length();
- }
-
- template <>
- inline size_t GetLength(const TStringBuf& s) {
- return s.length();
- }
-
- template <>
- inline size_t GetLength(const char* const& s) {
+namespace NPrivate {
+ template <typename T>
+ inline size_t GetLength(const T&) {
+ // By default don't pre-allocate space when joining and appending non-string types.
+ // This code can be extended by estimating stringified length for specific types (e.g. 10 for ui32).
+ return 0;
+ }
+
+ template <>
+ inline size_t GetLength(const TString& s) {
+ return s.length();
+ }
+
+ template <>
+ inline size_t GetLength(const TStringBuf& s) {
+ return s.length();
+ }
+
+ template <>
+ inline size_t GetLength(const char* const& s) {
return (s ? std::char_traits<char>::length(s) : 0);
- }
-
- inline size_t GetAppendLength(const TStringBuf /*delim*/) {
- return 0;
- }
-
- template <typename TFirst, typename... TRest>
- size_t GetAppendLength(const TStringBuf delim, const TFirst& f, const TRest&... r) {
- return delim.length() + ::NPrivate::GetLength(f) + ::NPrivate::GetAppendLength(delim, r...);
- }
+ }
+
+ inline size_t GetAppendLength(const TStringBuf /*delim*/) {
+ return 0;
+ }
+
+ template <typename TFirst, typename... TRest>
+ size_t GetAppendLength(const TStringBuf delim, const TFirst& f, const TRest&... r) {
+ return delim.length() + ::NPrivate::GetLength(f) + ::NPrivate::GetAppendLength(delim, r...);
+ }
}
template <typename TCharType>
inline void AppendJoinNoReserve(TBasicString<TCharType>&, TBasicStringBuf<TCharType>) {
-}
-
+}
+
template <typename TCharType, typename TFirst, typename... TRest>
inline void AppendJoinNoReserve(TBasicString<TCharType>& dst, TBasicStringBuf<TCharType> delim, const TFirst& f, const TRest&... r) {
AppendToString(dst, delim);
AppendToString(dst, f);
- AppendJoinNoReserve(dst, delim, r...);
+ AppendJoinNoReserve(dst, delim, r...);
+}
+
+template <typename... TValues>
+inline void AppendJoin(TString& dst, const TStringBuf delim, const TValues&... values) {
+ const size_t appendLength = ::NPrivate::GetAppendLength(delim, values...);
+ if (appendLength > 0) {
+ dst.reserve(dst.length() + appendLength);
+ }
+ AppendJoinNoReserve(dst, delim, values...);
}
-template <typename... TValues>
-inline void AppendJoin(TString& dst, const TStringBuf delim, const TValues&... values) {
- const size_t appendLength = ::NPrivate::GetAppendLength(delim, values...);
- if (appendLength > 0) {
- dst.reserve(dst.length() + appendLength);
- }
- AppendJoinNoReserve(dst, delim, values...);
-}
-
template <typename TFirst, typename... TRest>
inline TString Join(const TStringBuf delim, const TFirst& f, const TRest&... r) {
TString ret = ToString(f);
@@ -127,8 +127,8 @@ namespace NPrivate {
for (TIter pos = beg; ++pos != end;) {
AppendJoinNoReserve(out, delim, *pos);
}
- }
-
+ }
+
return out;
}
@@ -241,25 +241,25 @@ constexpr auto MakeRangeJoiner(TStringBuf delim, const std::initializer_list<TVa
return MakeRangeJoiner(delim, std::cbegin(data), std::cend(data));
}
-/* We force (std::initializer_list<TStringBuf>) input type for (TString) and (const char*) types because:
- * # When (std::initializer_list<TString>) is used, TString objects are copied into the initializer_list object.
- * Storing TStringBufs instead is faster, even with COW-enabled strings.
- * # For (const char*) we calculate length only once and store it in TStringBuf. Otherwise strlen scan would be executed
- * in both GetAppendLength and AppendToString. For string literals constant lengths get propagated in compile-time.
- *
- * This way JoinSeq(",", { s1, s2 }) always does the right thing whatever types s1 and s2 have.
- *
- * If someone needs to join std::initializer_list<TString> -- it still works because of the TContainer template above.
-*/
-
+/* We force (std::initializer_list<TStringBuf>) input type for (TString) and (const char*) types because:
+ * # When (std::initializer_list<TString>) is used, TString objects are copied into the initializer_list object.
+ * Storing TStringBufs instead is faster, even with COW-enabled strings.
+ * # For (const char*) we calculate length only once and store it in TStringBuf. Otherwise strlen scan would be executed
+ * in both GetAppendLength and AppendToString. For string literals constant lengths get propagated in compile-time.
+ *
+ * This way JoinSeq(",", { s1, s2 }) always does the right thing whatever types s1 and s2 have.
+ *
+ * If someone needs to join std::initializer_list<TString> -- it still works because of the TContainer template above.
+*/
+
template <typename T>
inline std::enable_if_t<
!std::is_same<std::decay_t<T>, TString>::value && !std::is_same<std::decay_t<T>, const char*>::value,
TString>
-JoinSeq(const TStringBuf delim, const std::initializer_list<T>& data) {
+JoinSeq(const TStringBuf delim, const std::initializer_list<T>& data) {
+ return JoinRange(delim, data.begin(), data.end());
+}
+
+inline TString JoinSeq(const TStringBuf delim, const std::initializer_list<TStringBuf>& data) {
return JoinRange(delim, data.begin(), data.end());
}
-
-inline TString JoinSeq(const TStringBuf delim, const std::initializer_list<TStringBuf>& data) {
- return JoinRange(delim, data.begin(), data.end());
-}
diff --git a/util/string/join_ut.cpp b/util/string/join_ut.cpp
index 74ff515da9..3ed2b2459c 100644
--- a/util/string/join_ut.cpp
+++ b/util/string/join_ut.cpp
@@ -39,18 +39,18 @@ Y_UNIT_TEST_SUITE(JoinStringTest) {
}
Y_UNIT_TEST(StrContainerItems) {
- // try various overloads and template type arguments
- static const char* const result = "1 22 333";
- static const char* const v[] = {"1", "22", "333"};
+ // try various overloads and template type arguments
+ static const char* const result = "1 22 333";
+ static const char* const v[] = {"1", "22", "333"};
TVector<const char*> vchar(v, v + sizeof(v) / sizeof(v[0]));
TVector<TStringBuf> vbuf(v, v + sizeof(v) / sizeof(v[0]));
TVector<TString> vstring(v, v + sizeof(v) / sizeof(v[0]));
-
- // ranges
- UNIT_ASSERT_EQUAL(JoinRange(" ", v, v + 3), result);
- UNIT_ASSERT_EQUAL(JoinRange(" ", vchar.begin(), vchar.end()), result);
- UNIT_ASSERT_EQUAL(JoinRange(" ", vbuf.begin(), vbuf.end()), result);
- UNIT_ASSERT_EQUAL(JoinRange(" ", vstring.begin(), vstring.end()), result);
+
+ // ranges
+ UNIT_ASSERT_EQUAL(JoinRange(" ", v, v + 3), result);
+ UNIT_ASSERT_EQUAL(JoinRange(" ", vchar.begin(), vchar.end()), result);
+ UNIT_ASSERT_EQUAL(JoinRange(" ", vbuf.begin(), vbuf.end()), result);
+ UNIT_ASSERT_EQUAL(JoinRange(" ", vstring.begin(), vstring.end()), result);
{
TStringStream stream;
stream << MakeRangeJoiner(" ", v, v + 3);
@@ -71,11 +71,11 @@ Y_UNIT_TEST_SUITE(JoinStringTest) {
stream << MakeRangeJoiner(" ", vstring.begin(), vstring.end());
UNIT_ASSERT_EQUAL(stream.Str(), result);
}
-
- // vectors
- UNIT_ASSERT_EQUAL(JoinSeq(" ", vchar), result);
- UNIT_ASSERT_EQUAL(JoinSeq(" ", vbuf), result);
- UNIT_ASSERT_EQUAL(JoinSeq(" ", vstring), result);
+
+ // vectors
+ UNIT_ASSERT_EQUAL(JoinSeq(" ", vchar), result);
+ UNIT_ASSERT_EQUAL(JoinSeq(" ", vbuf), result);
+ UNIT_ASSERT_EQUAL(JoinSeq(" ", vstring), result);
{
TStringStream stream;
stream << MakeRangeJoiner(" ", vchar);
@@ -91,12 +91,12 @@ Y_UNIT_TEST_SUITE(JoinStringTest) {
stream << MakeRangeJoiner(" ", vstring);
UNIT_ASSERT_EQUAL(stream.Str(), result);
}
-
- // initializer lists with type deduction
- UNIT_ASSERT_EQUAL(JoinSeq(" ", {v[0], v[1], v[2]}), result);
- UNIT_ASSERT_EQUAL(JoinSeq(" ", {vchar[0], vchar[1], vchar[2]}), result);
- UNIT_ASSERT_EQUAL(JoinSeq(" ", {vbuf[0], vbuf[1], vbuf[2]}), result);
- UNIT_ASSERT_EQUAL(JoinSeq(" ", {vstring[0], vstring[1], vstring[2]}), result);
+
+ // initializer lists with type deduction
+ UNIT_ASSERT_EQUAL(JoinSeq(" ", {v[0], v[1], v[2]}), result);
+ UNIT_ASSERT_EQUAL(JoinSeq(" ", {vchar[0], vchar[1], vchar[2]}), result);
+ UNIT_ASSERT_EQUAL(JoinSeq(" ", {vbuf[0], vbuf[1], vbuf[2]}), result);
+ UNIT_ASSERT_EQUAL(JoinSeq(" ", {vstring[0], vstring[1], vstring[2]}), result);
{
TStringStream stream;
stream << MakeRangeJoiner(" ", {v[0], v[1], v[2]});
@@ -117,12 +117,12 @@ Y_UNIT_TEST_SUITE(JoinStringTest) {
stream << MakeRangeJoiner(" ", {vstring[0], vstring[1], vstring[2]});
UNIT_ASSERT_EQUAL(stream.Str(), result);
}
-
- // initializer lists with explicit types
- UNIT_ASSERT_EQUAL(JoinSeq(" ", std::initializer_list<const char*>{v[0], v[1], v[2]}), result);
- UNIT_ASSERT_EQUAL(JoinSeq(" ", std::initializer_list<const char*>{vchar[0], vchar[1], vchar[2]}), result);
- UNIT_ASSERT_EQUAL(JoinSeq(" ", std::initializer_list<TStringBuf>{vbuf[0], vbuf[1], vbuf[2]}), result);
- UNIT_ASSERT_EQUAL(JoinSeq(" ", std::initializer_list<TString>{vstring[0], vstring[1], vstring[2]}), result);
+
+ // initializer lists with explicit types
+ UNIT_ASSERT_EQUAL(JoinSeq(" ", std::initializer_list<const char*>{v[0], v[1], v[2]}), result);
+ UNIT_ASSERT_EQUAL(JoinSeq(" ", std::initializer_list<const char*>{vchar[0], vchar[1], vchar[2]}), result);
+ UNIT_ASSERT_EQUAL(JoinSeq(" ", std::initializer_list<TStringBuf>{vbuf[0], vbuf[1], vbuf[2]}), result);
+ UNIT_ASSERT_EQUAL(JoinSeq(" ", std::initializer_list<TString>{vstring[0], vstring[1], vstring[2]}), result);
{
TStringStream stream;
stream << MakeRangeJoiner(" ", std::initializer_list<const char*>{v[0], v[1], v[2]});
@@ -146,8 +146,8 @@ Y_UNIT_TEST_SUITE(JoinStringTest) {
// c-style array
UNIT_ASSERT_VALUES_EQUAL(JoinSeq(" ", v), result);
- }
-
+ }
+
Y_UNIT_TEST(CustomToString) {
TCustomData d1{{1, 2, 3, 4, 5}};
TCustomData d2{{0, -1, -2}};