123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- #include "subst.h"
- #include <util/generic/strbuf.h>
- #include <util/generic/string.h>
- #include <util/system/compiler.h>
- #include <string>
- #include <type_traits>
- // a bit of template magic (to be fast and unreadable)
- template <class TStringType, class TTo, bool Main>
- static Y_FORCE_INLINE void MoveBlock(typename TStringType::value_type* ptr, size_t& srcPos, size_t& dstPos, const size_t off, const TTo to, const size_t toSize) {
- const size_t unchangedSize = off - srcPos;
- if (dstPos < srcPos) {
- for (size_t i = 0; i < unchangedSize; ++i) {
- ptr[dstPos++] = ptr[srcPos++];
- }
- } else {
- dstPos += unchangedSize;
- srcPos += unchangedSize;
- }
- if (Main) {
- for (size_t i = 0; i < toSize; ++i) {
- ptr[dstPos++] = to[i];
- }
- }
- }
- template <typename T, typename U>
- static bool IsIntersect(const T& a, const U& b) noexcept {
- if (b.data() < a.data()) {
- return IsIntersect(b, a);
- }
- return !a.empty() && !b.empty() &&
- ((a.data() <= b.data() && b.data() < a.data() + a.size()) ||
- (a.data() < b.data() + b.size() && b.data() + b.size() <= a.data() + a.size()));
- }
- /**
- * Replaces all occurences of substring @c from in string @c s to string @c to.
- * Uses two separate implementations (inplace for shrink and append for grow case)
- * See IGNIETFERRO-394
- **/
- template <class TStringType, typename TStringViewType = TBasicStringBuf<typename TStringType::value_type>>
- static inline size_t SubstGlobalImpl(TStringType& s, const TStringViewType from, const TStringViewType to, size_t fromPos = 0) {
- if (from.empty()) {
- return 0;
- }
- Y_ASSERT(!IsIntersect(s, from));
- Y_ASSERT(!IsIntersect(s, to));
- const size_t fromSize = from.size();
- const size_t toSize = to.size();
- size_t replacementsCount = 0;
- size_t off = fromPos;
- size_t srcPos = 0;
- if (toSize > fromSize) {
- // string will grow: append to another string
- TStringType result;
- for (; (off = TStringViewType(s).find(from, off)) != TStringType::npos; off += fromSize) {
- if (!replacementsCount) {
- // first replacement occured, we can prepare result string
- result.reserve(s.size() + s.size() / 3);
- }
- result.append(s.begin() + srcPos, s.begin() + off);
- result.append(to.data(), to.size());
- srcPos = off + fromSize;
- ++replacementsCount;
- }
- if (replacementsCount) {
- // append tail
- result.append(s.begin() + srcPos, s.end());
- s = std::move(result);
- }
- return replacementsCount;
- }
- // string will not grow: use inplace algo
- size_t dstPos = 0;
- typename TStringType::value_type* ptr = &*s.begin();
- for (; (off = TStringViewType(s).find(from, off)) != TStringType::npos; off += fromSize) {
- Y_ASSERT(dstPos <= srcPos);
- MoveBlock<TStringType, TStringViewType, true>(ptr, srcPos, dstPos, off, to, toSize);
- srcPos = off + fromSize;
- ++replacementsCount;
- }
- if (replacementsCount) {
- // append tail
- MoveBlock<TStringType, TStringViewType, false>(ptr, srcPos, dstPos, s.size(), to, toSize);
- s.resize(dstPos);
- }
- return replacementsCount;
- }
- /// Replaces all occurences of the 'from' symbol in a string to the 'to' symbol.
- template <class TStringType>
- inline size_t SubstCharGlobalImpl(TStringType& s, typename TStringType::value_type from, typename TStringType::value_type to, size_t fromPos = 0) {
- if (fromPos >= s.size()) {
- return 0;
- }
- size_t result = 0;
- fromPos = s.find(from, fromPos);
- // s.begin() might cause memory copying, so call it only if needed
- if (fromPos != TStringType::npos) {
- auto* it = &*s.begin() + fromPos;
- *it = to;
- ++result;
- // at this point string is copied and it's safe to use constant s.end() to iterate
- const auto* const sEnd = &*s.end();
- // unrolled loop goes first because it is more likely that `it` will be properly aligned
- for (const auto* const end = sEnd - (sEnd - it) % 4; it < end;) {
- if (*it == from) {
- *it = to;
- ++result;
- }
- ++it;
- if (*it == from) {
- *it = to;
- ++result;
- }
- ++it;
- if (*it == from) {
- *it = to;
- ++result;
- }
- ++it;
- if (*it == from) {
- *it = to;
- ++result;
- }
- ++it;
- }
- for (; it < sEnd; ++it) {
- if (*it == from) {
- *it = to;
- ++result;
- }
- }
- }
- return result;
- }
- /* Standard says that `char16_t` is a distinct type and has same size, signedness and alignment as
- * `std::uint_least16_t`, so we check if `char16_t` has same signedness and size as `wchar16` to be
- * sure that we can make safe casts between values of these types and pointers.
- */
- static_assert(sizeof(wchar16) == sizeof(char16_t), "");
- static_assert(sizeof(wchar32) == sizeof(char32_t), "");
- static_assert(std::is_unsigned<wchar16>::value == std::is_unsigned<char16_t>::value, "");
- static_assert(std::is_unsigned<wchar32>::value == std::is_unsigned<char32_t>::value, "");
- size_t SubstGlobal(TString& text, const TStringBuf what, const TStringBuf with, size_t from) {
- return SubstGlobalImpl(text, what, with, from);
- }
- size_t SubstGlobal(std::string& text, const TStringBuf what, const TStringBuf with, size_t from) {
- return SubstGlobalImpl(text, what, with, from);
- }
- size_t SubstGlobal(TUtf16String& text, const TWtringBuf what, const TWtringBuf with, size_t from) {
- return SubstGlobalImpl(text, what, with, from);
- }
- size_t SubstGlobal(TUtf32String& text, const TUtf32StringBuf what, const TUtf32StringBuf with, size_t from) {
- return SubstGlobalImpl(text, what, with, from);
- }
- size_t SubstGlobal(std::u16string& text, const TWtringBuf what, const TWtringBuf with, size_t from) {
- return SubstGlobalImpl(text,
- std::u16string_view(reinterpret_cast<const char16_t*>(what.data()), what.size()),
- std::u16string_view(reinterpret_cast<const char16_t*>(with.data()), with.size()),
- from);
- }
- size_t SubstGlobal(TString& text, char what, char with, size_t from) {
- return SubstCharGlobalImpl(text, what, with, from);
- }
- size_t SubstGlobal(std::string& text, char what, char with, size_t from) {
- return SubstCharGlobalImpl(text, what, with, from);
- }
- size_t SubstGlobal(TUtf16String& text, wchar16 what, wchar16 with, size_t from) {
- return SubstCharGlobalImpl(text, (char16_t)what, (char16_t)with, from);
- }
- size_t SubstGlobal(std::u16string& text, wchar16 what, wchar16 with, size_t from) {
- return SubstCharGlobalImpl(text, (char16_t)what, (char16_t)with, from);
- }
- size_t SubstGlobal(TUtf32String& text, wchar32 what, wchar32 with, size_t from) {
- return SubstCharGlobalImpl(text, (char32_t)what, (char32_t)with, from);
- }
|