diff options
author | swarmer <swarmer@yandex-team.ru> | 2022-05-24 01:42:42 +0300 |
---|---|---|
committer | swarmer <swarmer@yandex-team.ru> | 2022-05-24 01:42:42 +0300 |
commit | 9e6590dab63a1a07e738a246030af9b888333214 (patch) | |
tree | 038ab3bdf14231ab80bcfa12922f2371ea0c0a31 | |
parent | cc9b48149be0bca6e6c350983ace4fc660902b80 (diff) | |
download | ydb-9e6590dab63a1a07e738a246030af9b888333214.tar.gz |
[util] StripInPlace should modify string in-place
ref:2aec19528c2378a0994534ec032550413a095389
-rw-r--r-- | util/generic/typetraits.h | 15 | ||||
-rw-r--r-- | util/generic/typetraits_ut.cpp | 10 | ||||
-rw-r--r-- | util/string/benchmark/strip/main.cpp | 65 | ||||
-rw-r--r-- | util/string/strip.h | 7 | ||||
-rw-r--r-- | util/string/strip_ut.cpp | 57 |
5 files changed, 132 insertions, 22 deletions
diff --git a/util/generic/typetraits.h b/util/generic/typetraits.h index d165bd1a06..6542d73621 100644 --- a/util/generic/typetraits.h +++ b/util/generic/typetraits.h @@ -263,12 +263,27 @@ template <class R, class T, class... Args> struct TIsPointerToConstMemberFunction<R (T::*)(Args..., ...) const&&>: std::true_type { }; +namespace NPrivate { + template <template <typename...> class TBase, class TDerived> + struct TIsBaseOfTemplateHelper { + template <typename... Ts> + static constexpr std::true_type Check(const TBase<Ts...>*); + + static constexpr std::false_type Check(...); + + using TType = decltype(Check(std::declval<TDerived*>())); + }; +} + template <template <class...> class T, class U> struct TIsSpecializationOf: std::false_type {}; template <template <class...> class T, class... Ts> struct TIsSpecializationOf<T, T<Ts...>>: std::true_type {}; +template <template <typename...> class TBase, class TDerived> +using TIsTemplateBaseOf = typename ::NPrivate::TIsBaseOfTemplateHelper<TBase, TDerived>::TType; + /* * TDependentFalse is a constant dependent on a template parameter. * Use it in static_assert in a false branch of if constexpr to produce a compile error. diff --git a/util/generic/typetraits_ut.cpp b/util/generic/typetraits_ut.cpp index e7571c75ec..6d17e06b4a 100644 --- a/util/generic/typetraits_ut.cpp +++ b/util/generic/typetraits_ut.cpp @@ -457,6 +457,16 @@ static_assert(TIsSpecializationOf<std::tuple, std::tuple<int, double, char>>::va static_assert(!TIsSpecializationOf<std::vector, std::tuple<int, double, char>>::value, ""); static_assert(!TIsSpecializationOf<std::pair, std::vector<int>>::value, ""); +// test for TIsTemplateBaseOf +static_assert(TIsTemplateBaseOf<std::vector, std::vector<int>>::value); +static_assert(TIsTemplateBaseOf<std::tuple, std::tuple<int, double, char>>::value); +static_assert(TIsTemplateBaseOf<std::basic_string_view, std::wstring_view>::value); +static_assert(TIsTemplateBaseOf<std::vector, TVector<int>>::value); +static_assert(!TIsTemplateBaseOf<TVector, std::vector<int>>::value); +static_assert(TIsTemplateBaseOf<TBasicStringBuf, TWtringBuf>::value); +static_assert(TIsTemplateBaseOf<std::basic_string_view, TUtf32StringBuf>::value); +static_assert(TIsTemplateBaseOf<std::basic_string_view, TWtringBuf>::value); + // test for TIsIterable static_assert(TIsIterable<std::vector<int>>::value, ""); static_assert(!TIsIterable<int>::value, ""); diff --git a/util/string/benchmark/strip/main.cpp b/util/string/benchmark/strip/main.cpp new file mode 100644 index 0000000000..35a266e4f4 --- /dev/null +++ b/util/string/benchmark/strip/main.cpp @@ -0,0 +1,65 @@ +#include <library/cpp/testing/benchmark/bench.h> + +#include <util/string/strip.h> +#include <util/generic/xrange.h> + +static const TString SHORT_STRING = " foo "; +static const TString LONG_STRING = TString(200, ' ') + TString(200, 'f') + TString(200, ' '); + +Y_CPU_BENCHMARK(StripInPlaceShortNoChange, iface) { + TString s = "foo"; + for (const auto i : xrange(iface.Iterations())) { + Y_UNUSED(i); + StripInPlace(s); + Y_DO_NOT_OPTIMIZE_AWAY(s); + } +} + +Y_CPU_BENCHMARK(StripInPlaceLongNoChange, iface) { + TString s = TString{200, 'f'}; + for (const auto i : xrange(iface.Iterations())) { + Y_UNUSED(i); + StripInPlace(s); + Y_DO_NOT_OPTIMIZE_AWAY(s); + } +} + +Y_CPU_BENCHMARK(StripInPlaceShort, iface) { + TString s; + for (const auto i : xrange(iface.Iterations())) { + Y_UNUSED(i); + s.assign(SHORT_STRING.begin(), SHORT_STRING.end()); + StripInPlace(s); + Y_DO_NOT_OPTIMIZE_AWAY(s); + } +} + +Y_CPU_BENCHMARK(StripInPlaceLong, iface) { + TString s; + for (const auto i : xrange(iface.Iterations())) { + Y_UNUSED(i); + s.assign(LONG_STRING.begin(), LONG_STRING.end()); + StripInPlace(s); + Y_DO_NOT_OPTIMIZE_AWAY(s); + } +} + +Y_CPU_BENCHMARK(StripInPlaceShortMut, iface) { + TString s = SHORT_STRING; + for (const auto i : xrange(iface.Iterations())) { + Y_UNUSED(i); + s.append(' '); + StripInPlace(s); + Y_DO_NOT_OPTIMIZE_AWAY(s); + } +} + +Y_CPU_BENCHMARK(StripInPlaceLongMut, iface) { + TString s = LONG_STRING; + for (const auto i : xrange(iface.Iterations())) { + Y_UNUSED(i); + s.append(100, ' '); + StripInPlace(s); + Y_DO_NOT_OPTIMIZE_AWAY(s); + } +} diff --git a/util/string/strip.h b/util/string/strip.h index c2ea61adce..8d9e5ed988 100644 --- a/util/string/strip.h +++ b/util/string/strip.h @@ -4,6 +4,7 @@ #include <util/generic/string.h> #include <util/generic/strbuf.h> +#include <util/generic/typetraits.h> #include <utility> template <class It> @@ -86,7 +87,11 @@ struct TStripImpl { auto e = from.end(); if (StripRange(b, e, criterion)) { - to = T(b, e - b); + if constexpr (::TIsTemplateBaseOf<std::basic_string_view, T>::value) { + to = T(b, e - b); + } else { + to.assign(b, e - b); + } return true; } diff --git a/util/string/strip_ut.cpp b/util/string/strip_ut.cpp index df4f9bc57d..0b1687ffa7 100644 --- a/util/string/strip_ut.cpp +++ b/util/string/strip_ut.cpp @@ -5,28 +5,29 @@ #include <util/charset/wide.h> Y_UNIT_TEST_SUITE(TStripStringTest) { - Y_UNIT_TEST(TestStrip) { - struct TTest { - const char* Str; - const char* StripLeftRes; - const char* StripRightRes; - const char* StripRes; - }; - static const TTest tests[] = { - {" 012 ", "012 ", " 012", "012"}, - {" 012", "012", " 012", "012"}, - {"012\t\t", "012\t\t", "012", "012"}, - {"\t012\t", "012\t", "\t012", "012"}, - {"012", "012", "012", "012"}, - {"012\r\n", "012\r\n", "012", "012"}, - {"\n012\r", "012\r", "\n012", "012"}, - {"\n \t\r", "", "", ""}, - {"", "", "", ""}, - {"abc", "abc", "abc", "abc"}, - {"a c", "a c", "a c", "a c"}, - }; + struct TStripTest { + TStringBuf Str; + TStringBuf StripLeftRes; + TStringBuf StripRightRes; + TStringBuf StripRes; + }; + static constexpr TStripTest StripTests[] = { + {" 012 ", "012 ", " 012", "012"}, + {" 012", "012", " 012", "012"}, + {"012\t\t", "012\t\t", "012", "012"}, + {"\t012\t", "012\t", "\t012", "012"}, + {"012", "012", "012", "012"}, + {"012\r\n", "012\r\n", "012", "012"}, + {"\n012\r", "012\r", "\n012", "012"}, + {"\n \t\r", "", "", ""}, + {"", "", "", ""}, + {"abc", "abc", "abc", "abc"}, + {"a c", "a c", "a c", "a c"}, + {" long string to avoid SSO \n", "long string to avoid SSO \n", " long string to avoid SSO", "long string to avoid SSO"}, + }; - for (const auto& test : tests) { + Y_UNIT_TEST(TestStrip) { + for (const auto& test : StripTests) { TString inputStr(test.Str); TString s; @@ -44,6 +45,20 @@ Y_UNIT_TEST_SUITE(TStripStringTest) { }; } + Y_UNIT_TEST(TestStripInPlace) { + for (const auto& test : StripTests) { + TString str(test.Str); + Y_ASSERT(str.IsDetached() || str.empty()); // prerequisite of the test; check that we don't try to modify shared COW-string in-place by accident + const void* stringPtrPrior = str.data(); + StripInPlace(str); + const void* stringPtrAfter = str.data(); + UNIT_ASSERT_VALUES_EQUAL(str, test.StripRes); + if (!test.Str.empty()) { + UNIT_ASSERT_EQUAL_C(stringPtrPrior, stringPtrAfter, TString(test.Str).Quote()); // StripInPlace should reuse buffer of original string + } + } + } + Y_UNIT_TEST(TestCustomStrip) { struct TTest { const char* Str; |