aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorswarmer <swarmer@yandex-team.ru>2022-05-24 01:42:42 +0300
committerswarmer <swarmer@yandex-team.ru>2022-05-24 01:42:42 +0300
commit9e6590dab63a1a07e738a246030af9b888333214 (patch)
tree038ab3bdf14231ab80bcfa12922f2371ea0c0a31
parentcc9b48149be0bca6e6c350983ace4fc660902b80 (diff)
downloadydb-9e6590dab63a1a07e738a246030af9b888333214.tar.gz
[util] StripInPlace should modify string in-place
ref:2aec19528c2378a0994534ec032550413a095389
-rw-r--r--util/generic/typetraits.h15
-rw-r--r--util/generic/typetraits_ut.cpp10
-rw-r--r--util/string/benchmark/strip/main.cpp65
-rw-r--r--util/string/strip.h7
-rw-r--r--util/string/strip_ut.cpp57
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;