#include "subst.h" #include #include #include #include #include // a bit of template magic (to be fast and unreadable) template 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 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 > 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(ptr, srcPos, dstPos, off, to, toSize); srcPos = off + fromSize; ++replacementsCount; } if (replacementsCount) { // append tail MoveBlock(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 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::value == std::is_unsigned::value, ""); static_assert(std::is_unsigned::value == std::is_unsigned::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(what.data()), what.size()), std::u16string_view(reinterpret_cast(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); }