diff --git a/include/simstr/sstring.h b/include/simstr/sstring.h index 69f8eae..511b68f 100644 --- a/include/simstr/sstring.h +++ b/include/simstr/sstring.h @@ -2039,7 +2039,7 @@ public: // Заменяем inplace на подстроку такой же длины // Replace inplace with a substring of the same length K* ptr = str(); - for (size_t i = 0; i < maxCount; i++) { + while (maxCount--) { traits::copy(ptr + offset, repl.symbols(), replLength); offset = d().find(pattern, offset + replLength);// replLength == patternLength if (offset == str::npos) @@ -2051,10 +2051,9 @@ public: K* ptr = str(); traits::copy(ptr + offset, repl.symbols(), replLength); size_t posWrite = offset + replLength; - maxCount--; offset += patternLength; - for (size_t i = 0; i < maxCount; i++) { + while (--maxCount) { size_t idx = d().find(pattern, offset); if (idx == str::npos) break; @@ -2083,25 +2082,21 @@ public: size_t total_length{}; void replace(size_t offset) { - size_t finded[16] = {source.find(pattern, offset)}; - if (finded[0] == str::npos) { - return; - } + size_t found[16] = {offset}; maxCount--; - offset = finded[0] + pattern.length(); + offset += pattern.length(); all_delta += delta; size_t idx = 1; - for (size_t end = std::min(maxCount, std::size(finded)); idx < end; idx++, maxCount--) { - finded[idx] = source.find(pattern, offset); - if (finded[idx] == str::npos) { + for (; idx < std::size(found) && maxCount > 0; idx++, maxCount--) { + found[idx] = source.find(pattern, offset); + if (found[idx] == str::npos) { break; } - offset = finded[idx] + pattern.length(); + offset = found[idx] + pattern.length(); all_delta += delta; } - bool needMore = maxCount > 0 && idx == std::size(finded) && offset < source.length() - pattern.length(); - if (needMore) { - replace(offset); // здесь произведутся замены в оставшемся хвосте | replacements will be made here in the remaining tail + if (idx == std::size(found) && maxCount > 0 && (offset = source.find(pattern, offset)) != str::npos) { + replace(offset); // здесь произойдут замены в оставшемся хвосте | replacements will be made here in the remaining tail } // Теперь делаем свои замены // Now we make our replacements @@ -2115,15 +2110,15 @@ public: K* dst_start = reserve_for_copy; const K* src_start = source.symbols(); while(idx-- > 0) { - size_t pos = finded[idx] + pattern.length(); + size_t pos = found[idx] + pattern.length(); size_t lenOfPiece = end_of_piece - pos; ch_traits::move(dst_start + pos + all_delta, src_start + pos, lenOfPiece); ch_traits::copy(dst_start + pos + all_delta - repl.length(), repl.symbols(), repl.length()); all_delta -= delta; - end_of_piece = finded[idx]; + end_of_piece = found[idx]; } if (!all_delta && reserve_for_copy != src_start) { - ch_traits::copy(dst_start, src_start, finded[0]); + ch_traits::copy(dst_start, src_start, found[0]); } } } helper(d(), pattern, repl, maxCount, repl.length() - pattern.length()); diff --git a/include/simstr/strexpr.h b/include/simstr/strexpr.h index 1344c2c..4fa4991 100644 --- a/include/simstr/strexpr.h +++ b/include/simstr/strexpr.h @@ -191,6 +191,11 @@ struct const_lit { constexpr static size_t Count = N; }; +template +concept is_const_lit_v = requires { + typename const_lit::symb_type; +}; + // Тут ещё дополнительно ограничиваем тип литерала // Here we further restrict the type of the literal template struct const_lit_for; @@ -1604,48 +1609,59 @@ constexpr K hex_symbols[16] = {K('0'), K('1'), K('2'), K('3'), K('4'), K('5'), K K('7'), K('8'), K('9'), K(Ucase ? 'A' : 'a'), K(Ucase ? 'B' : 'b'), K(Ucase ? 'C' : 'c'), K(Ucase ? 'D' : 'd'), K(Ucase ? 'E' : 'e'), K(Ucase ? 'F' : 'f')}; -template -requires std::is_unsigned_v +template struct expr_hex : expr_to_std_string> { using symb_type = K; - mutable Val v_; + mutable need_sign, Val> v_; mutable K buf_[sizeof(Val) * 2]; explicit constexpr expr_hex(Val v) : v_(v){} constexpr size_t length() const noexcept { - K* ptr = buf_ + sizeof(Val) * 2; - Val value = v_; + K *ptr = buf_ + std::size(buf_); size_t l = 0; for (;;) { - *--ptr = hex_symbols[value & 0xF]; - value >>= 4; + *--ptr = hex_symbols[v_.val & 0xF]; + v_.val >>= 4; l++; - if (value) { - *--ptr = hex_symbols[value & 0xF]; - value >>= 4; + if (v_.val) { + *--ptr = hex_symbols[v_.val & 0xF]; + v_.val >>= 4; l++; } - if (!value) { + if (!v_.val) { if constexpr (All) { if (size_t need = sizeof(Val) * 2 - l) { ch_traits::assign(buf_, need, K('0')); - l = sizeof(Val) * 2; } + l = sizeof(Val) * 2; } break; } } - v_ = (Val)l; + v_.val = l; + if constexpr (std::is_signed_v) { + return l + (Ox ? 2 : 0) + (v_.negate ? 1 : 0); + } return l + (Ox ? 2 : 0); } constexpr K* place(K* ptr) const noexcept { + if constexpr (std::is_signed_v) { + if (v_.negate) { + *ptr++ = K('-'); + } + } if constexpr (Ox) { *ptr++ = K('0'); *ptr++ = K('x'); } - ch_traits::copy(ptr, buf_ + sizeof(Val) * 2 - v_, v_); - return ptr + v_; + if constexpr (All) { + ch_traits::copy(ptr, buf_, sizeof(Val) * 2); + return ptr + sizeof(Val) * 2; + } else { + ch_traits::copy(ptr, buf_ + std::size(buf_) - v_.val, v_.val); + return ptr + v_.val; + } } }; @@ -1684,7 +1700,7 @@ enum HexFlags : unsigned { * EXPECT_EQ(textu, u"val = 0X12a"); * ``` */ -template requires std::is_unsigned_v +template constexpr auto e_hex(T v) { return expr_hex_src{v}; } @@ -4476,15 +4492,17 @@ constexpr auto e_repl(A&& w, T&& p, X&& r) { * stringa result = "
" + expr_replaced{source, pattern, repl} + "
"; * ``` */ -template +template struct expr_replaced : expr_to_std_string> { using symb_type = K; using my_type = expr_replaced; str_src what; const str_src pattern; - const str_src repl; + mutable K* replStart; + mutable size_t replLen; mutable find_all_container matches_; mutable size_t last_; + const E& expr; /*! * @ru @brief Конструктор. * @param w - исходная строка. @@ -4495,18 +4513,20 @@ struct expr_replaced : expr_to_std_string> { * @param p - the searched substring. * @param r - replacement string. */ - constexpr expr_replaced(str_src w, str_src p, str_src r) : what(w), pattern(p), repl(r) {} + constexpr expr_replaced(str_src w, str_src p, const K* r, size_t rl, const E& e) : what(w), pattern(p), replStart(const_cast(r)), replLen(rl), expr(e) {} constexpr size_t length() const { - size_t l = what.length(), plen = pattern.length(), rlen = repl.length(); - - if (!plen || plen == rlen) { + size_t l = what.length(), plen = pattern.length(); + if constexpr (!std::is_same_v) { + replLen = expr.length(); + } + if (!plen || plen == replLen) { return l; } what.find_all_to(matches_, pattern.symbols(), plen, 0, FIND_CACHE_SIZE); if (matches_.added_) { last_ = matches_.positions_[matches_.added_ - 1] + plen; - l += int(rlen - plen) * matches_.added_; + l += int(replLen - plen) * matches_.added_; if (matches_.added_ == FIND_CACHE_SIZE) { for (;;) { @@ -4515,7 +4535,7 @@ struct expr_replaced : expr_to_std_string> { break; } last_ = next + plen; - l += rlen - plen; + l += replLen - plen; } } } @@ -4525,8 +4545,8 @@ struct expr_replaced : expr_to_std_string> { return l; } constexpr K* place(K* ptr) const noexcept { - size_t plen = pattern.length(), rlen = repl.length(); - if (plen == rlen) { + size_t plen = pattern.length(); + if (plen == replLen) { const K* from = what.symbols(); for (size_t start = 0; start < what.length();) { size_t next = what.find(pattern, start); @@ -4536,8 +4556,17 @@ struct expr_replaced : expr_to_std_string> { size_t delta = next - start; ch_traits::copy(ptr, from + start, delta); ptr += delta; - ch_traits::copy(ptr, repl.symbols(), rlen); - ptr += rlen; + if constexpr (std::is_same_v) { + ch_traits::copy(ptr, replStart, replLen); + } else { + if (!replStart) { + replStart = ptr; + expr.place(replStart); + } else { + ch_traits::copy(ptr, replStart, replLen); + } + } + ptr += replLen; start = next + plen; } return ptr; @@ -4552,8 +4581,17 @@ struct expr_replaced : expr_to_std_string> { for (size_t start = 0, offset = matches_.positions_[0], idx = 1; ;) { ch_traits::copy(ptr, from + start, offset - start); ptr += offset - start; - ch_traits::copy(ptr, repl.symbols(), rlen); - ptr += rlen; + if constexpr (std::is_same_v) { + ch_traits::copy(ptr, replStart, replLen); + } else { + if (!replStart) { + replStart = ptr; + expr.place(replStart); + } else { + ch_traits::copy(ptr, replStart, replLen); + } + } + ptr += replLen; start = offset + plen; if (start >= last_) { size_t tail = what.length() - last_; @@ -4573,18 +4611,20 @@ struct expr_replaced : expr_to_std_string> { * @ru @brief Получить строковое выражение, генерирующее строку с заменой всех вхождений заданной подстроки. * @tparam K - тип символа, выводится из первого аргумента. * @param w - начальная строка. - * @param p - строковый литерал, искомая подстрока. - * @param r - строковый объект, может быть рантайм. + * @param p - строковый объект, искомая подстрока, может быть рантайм. + * @param r - строковый объект, на что заменять, может быть рантайм. * @en @brief Get a string expression that generates a string with all occurrences of a given substring replaced. * @tparam K - the type of the symbol, inferred from the first argument. * @param w - starting string. - * @param p - string literal, searched substring. - * @param r - string object, maybe runtime. + * @param p - string object, searched substring, maybe runtime. + * @param r - string object, replace substring, maybe runtime. */ -template, typename T, size_t N = const_lit_for::Count, StrSource X> - requires std::is_same_v> +template, typename T, typename X> + requires (std::is_constructible_v, T> && std::is_constructible_v, X> && (!is_const_lit_v || !is_const_lit_v)) constexpr auto e_repl(A&& w, T&& p, X&& r) { - return expr_replaced{get_str_src_from(std::forward(w)), p, get_str_src_from(std::forward(r))}; + str_src pattern{std::forward(p)}; + str_src repl{std::forward(r)}; + return expr_replaced{get_str_src_from(std::forward(w)), pattern, repl.str, repl.len, 0}; } /*! @@ -4592,37 +4632,19 @@ constexpr auto e_repl(A&& w, T&& p, X&& r) { * @ru @brief Получить строковое выражение, генерирующее строку с заменой всех вхождений заданной подстроки. * @tparam K - тип символа, выводится из первого аргумента. * @param w - начальная строка. - * @param p - строковый объект, может быть рантайм. - * @param r - строковый литерал, на что заменять. + * @param p - строковый объект, искомая подстрока, может быть рантайм. + * @param expr - строковое выражение, на что заменять. * @en @brief Get a string expression that generates a string with all occurrences of a given substring replaced. * @tparam K - the type of the symbol, inferred from the first argument. * @param w - starting string. - * @param p - string object, maybe runtime. - * @param r - string literal, what to replace with. + * @param p - string object, searched substring, maybe runtime. + * @param expr - string expression, what to replace with. */ -template, StrSource T, typename X, size_t L = const_lit_for::Count> - requires std::is_same_v> -constexpr auto e_repl(A&& w, T&& p, X&& r) { - return expr_replaced{get_str_src_from(std::forward(w)), get_str_src_from(std::forward(p)), r}; -} - -/*! - * @ingroup StrExprs - * @ru @brief Получить строковое выражение, генерирующее строку с заменой всех вхождений заданной подстроки. - * @tparam K - тип символа, выводится из первого аргумента. - * @param w - начальная строка. - * @param p - строковый объект, может быть рантайм. - * @param r - строковый объект, может быть рантайм. - * @en @brief Get a string expression that generates a string with all occurrences of a given substring replaced. - * @tparam K - the type of the symbol, inferred from the first argument. - * @param w - starting string. - * @param p - string object, maybe runtime. - * @param r - string object, maybe runtime. - */ -template, StrSource T, StrSource X> - requires (std::is_same_v> && std::is_same_v>) -constexpr auto e_repl(A&& w, T&& p, X&& r) { - return expr_replaced{get_str_src_from(std::forward(w)), get_str_src_from(std::forward(p)), get_str_src_from(std::forward(r))}; +template, typename T, StrExprForType E> + requires std::is_constructible_v, T> +constexpr auto e_repl(A&& w, T&& p, const E& expr) { + str_src pattern{std::forward(p)}; + return expr_replaced{get_str_src_from(std::forward(w)), pattern, nullptr, 0, expr}; } template @@ -5206,7 +5228,7 @@ std::basic_string, A>& change(std::basic_string E> +template E> std::basic_string, A>& append(std::basic_string, A>& str, const E& expr) { return change(str, str.length(), 0, expr); } @@ -5239,7 +5261,7 @@ std::basic_string, A>& append(std::basic_string E> +template E> std::basic_string, A>& prepend(std::basic_string, A>& str, const E& expr) { return change(str, 0, 0, expr); } @@ -5274,11 +5296,275 @@ std::basic_string, A>& prepend(std::basic_string E> +template E> std::basic_string, A>& insert(std::basic_string, A>& str, size_t from, const E& expr) { return change(str, from, 0, expr); } +namespace details { + +template +struct replace_grow_helper { + using my_type = std::basic_string, A>; + + replace_grow_helper(my_type& src, str_src p, const K* r, size_t rl, size_t mc, size_t d, const E& e) + : str(src), source(src), pattern(p), repl(const_cast(r)), replLen(rl), maxCount(mc), delta(d), expr(e) {} + my_type& str; + + const str_src source; + const str_src pattern; + K* repl; + const size_t replLen; + + size_t maxCount; + const size_t delta; + size_t all_delta{}; + const E& expr; + + K* reserve_for_copy{}; + size_t end_of_piece{}; + size_t total_length{}; + + std::optional dst; + + void replace(size_t offset) { + size_t found[16] = {offset}; + maxCount--; + + offset += pattern.len; + all_delta += delta; + size_t idx = 1; + for (; idx < std::size(found) && maxCount > 0; idx++, maxCount--) { + found[idx] = source.find(pattern, offset); + if (found[idx] == npos) { + break; + } + offset = found[idx] + pattern.len; + all_delta += delta; + } + if (idx == std::size(found) && maxCount > 0 && (offset = source.find(pattern, offset)) != str::npos) { + replace(offset); // здесь произойдут замены в оставшемся хвосте | replacements will be made here in the remaining tail + } + // Теперь делаем свои замены + // Now we make our replacements + if (!reserve_for_copy) { + // Только начинаем + // Just getting started + end_of_piece = source.length(); + total_length = end_of_piece + all_delta; + my_type* dst_str{}; + if (total_length <= str.capacity()) { + // Строка поместится в старое место | The line will be placed in the old location. + dst_str = &str; + } else { + // Будем создавать в другом буфере | We will create in another buffer. + dst_str = &dst.emplace(); + } + auto fill = [this](K* p, size_t) -> size_t { + reserve_for_copy = p; + return total_length; + }; + if constexpr (requires{dst_str->_Resize_and_overwrite(total_length, fill);}) { + dst_str->_Resize_and_overwrite(total_length, fill); + } else if constexpr (requires{dst_str->resize_and_overwrite(total_length, fill);}) { + dst_str->resize_and_overwrite(total_length, fill); + } else { + dst_str->resize(total_length); + reserve_for_copy = dst_str->data(); + } + } + K* dst_start = reserve_for_copy; + const K* src_start = str.c_str(); + while(idx-- > 0) { + size_t pos = found[idx] + pattern.len; + size_t lenOfPiece = end_of_piece - pos; + ch_traits::move(dst_start + pos + all_delta, src_start + pos, lenOfPiece); + if constexpr (std::is_same_v) { + ch_traits::copy(dst_start + pos + all_delta - replLen, repl, replLen); + } else { + if (!repl) { + repl = dst_start + pos + all_delta - replLen; + expr.place(repl); + } else { + ch_traits::copy(dst_start + pos + all_delta - replLen, repl, replLen); + } + } + all_delta -= delta; + end_of_piece = found[idx]; + } + if (!all_delta && reserve_for_copy != src_start) { + ch_traits::copy(dst_start, src_start, found[0]); + str = std::move(*dst); + } + } +}; + +} // namespace details + +/*! + * @ru @brief Функция поиска подстрок в стандартной строке и замены найденных вхождений на значение строкового выражения. + * @tparam K - тип символов строки. + * @tparam A - тип аллокатора строки. + * @tparam E - тип строкового выражения. + * @tparam T - тип подстроки поиска. + * @param str - строка, в которой заменяем вхождения подстрок. + * @param pattern - искомая подстрока (любой тип, конвертирующийся в simple_str). + * @param repl - строковое выражение для замены. + * @param offset - начальное смещение для поиска. + * @param max_count - максимальное количество замен. + * @return std::basic_string, A>& - ссылку на модифицируемую строку. + * @details Части строкового выражения не должны ссылаться на саму модифицируемую строку. + * Если искомая подстрока не найдена, то строковое выражение даже не вычисляется. + * Затем при осуществлении замены строковое выражение вычисляется только один раз в место первой замены, + * а в следующие места замен просто копируется символы из первого места. Это позволяет экономить память + * и время, если вам надо сделать замену на какую-либо "сборную" строку. + * @en @brief A function for searching for substrings in a standard string and replacing the found occurrences with a string expression. + * @tparam K - the string character type. + * @tparam A - the string allocator type. + * @tparam E - the string expression type. + * @tparam T - the search substring type. + * @param str - the string in which to replace substring occurrences. + * @param pattern - the search substring (any type convertible to simple_str). + * @param repl - the string expression to replace. + * @param offset - the starting offset for the search. + * @param max_count - the maximum number of replacements. + * @return std::basic_string, A>& - a reference to the string being modified. + * @details Parts of the string expression must not reference the string being modified itself. + * If the search substring is not found, the string expression is not even evaluated. + * Then, when performing a replacement, the string expression is evaluated only once at the first replacement location, + * and characters from the first location are simply copied to subsequent replacement locations. This saves memory + * and time if you need to replace with some kind of "composite" string. + */ +template E, typename T> +requires (std::is_constructible_v, T>) +std::basic_string, A>& replace(std::basic_string, A>& str, T&& pattern, const E& repl, size_t offset = 0, size_t max_count = -1) { + if (!max_count) { + return str; + } + str_src src = str; + str_src spattern{std::forward(pattern)}; + offset = src.find(pattern, offset); + if (offset == npos) { + return str; + } + size_t replLen = repl.length(); + K* replStart{}; + if (spattern.len == replLen) { + // Заменяем inplace на подстроку такой же длины + // Replace inplace with a substring of the same length + K* ptr = str.data(); + replStart = ptr + offset; + repl.place(replStart); + + while (--max_count) { + offset = src.find(spattern, offset + replLen); + if (offset == npos) + break; + ch_traits::copy(ptr + offset, replStart, replLen); + } + } else if (spattern.len > replLen) { + // Заменяем на более короткий кусок, длина текста уменьшится, идём слева направо + // Replace with a shorter piece, the length of the text will decrease, go from left to right + K* ptr = str.data(); + replStart = ptr + offset; + repl.place(replStart); + size_t posWrite = offset + replLen; + offset += spattern.len; + + while (--max_count) { + size_t idx = src.find(spattern, offset); + if (idx == npos) + break; + size_t lenOfPiece = idx - offset; + ch_traits::move(ptr + posWrite, ptr + offset, lenOfPiece); + posWrite += lenOfPiece; + ch_traits::copy(ptr + posWrite, replStart, replLen); + posWrite += replLen; + offset = idx + spattern.len; + } + size_t tailLen = src.len - offset; + ch_traits::move(ptr + posWrite, ptr + offset, tailLen); + str.resize(posWrite + tailLen); + } else { + details::replace_grow_helper(str, spattern, nullptr, replLen, max_count, replLen - spattern.len, repl).replace(offset); + } + return str; +} + +/*! + * @ru @brief Функция поиска подстрок в стандартной строке и замены найденных вхождений на другую подстроку. + * @tparam K - тип символов строки. + * @tparam A - тип аллокатора строки. + * @param str - строка, в которой заменяем вхождения подстрок. + * @param pattern - искомая подстрока (любой тип, конвертирующийся в simple_str). + * @param repl - строка для замены (любой тип, конвертирующийся в simple_str). + * @param offset - начальное смещение для поиска. + * @param max_count - максимальное количество замен. + * @return std::basic_string, A>& - ссылку на модифицируемую строку. + * @en @brief Function for searching for substrings in a standard string and replacing found occurrences with another substring. + * @tparam K - the character type of the string. + * @tparam A - the type of the string allocator. + * @param str - the string in which we replace occurrences of substrings. + * @param pattern - the searched substring (any type that converts to simple_str). + * @param repl - replacement string (any type convertible to simple_str). + * @param offset - the starting offset for the search. + * @param max_count - maximum number of replacements. + * @return std::basic_string, A>& - reference to the modified string. + */ +template +std::basic_string, A>& replace(std::basic_string, A>& str, str_src pattern, str_src repl, size_t offset = 0, size_t max_count = -1) { + if (!max_count) { + return str; + } + str_src src = str; + offset = src.find(pattern, offset); + if (offset == npos) { + return str; + } + if (pattern.len == repl.len) { + // Заменяем inplace на подстроку такой же длины + // Replace inplace with a substring of the same length + K* ptr = str.data(); + while (max_count--) { + ch_traits::copy(ptr + offset, repl.str, repl.len); + offset = src.find(pattern, offset + repl.len); + if (offset == npos) + break; + } + } else if (pattern.len > repl.len) { + // Заменяем на более короткий кусок, длина текста уменьшится, идём слева направо + // Replace with a shorter piece, the length of the text will decrease, go from left to right + K* ptr = str.data(); + ch_traits::copy(ptr + offset, repl.str, repl.len); + size_t posWrite = offset + repl.len; + offset += pattern.len; + + while (--max_count) { + size_t idx = src.find(pattern, offset); + if (idx == npos) + break; + size_t lenOfPiece = idx - offset; + ch_traits::move(ptr + posWrite, ptr + offset, lenOfPiece); + posWrite += lenOfPiece; + ch_traits::copy(ptr + posWrite, repl.str, repl.len); + posWrite += repl.len; + offset = idx + pattern.len; + } + size_t tailLen = src.len - offset; + ch_traits::move(ptr + posWrite, ptr + offset, tailLen); + str.resize(posWrite + tailLen); + } else { + details::replace_grow_helper(str, pattern, repl.str, repl.len, max_count, repl.len - pattern.len, 0).replace(offset); + } + return str; +} + +template +requires (std::is_constructible_v, T> && std::is_constructible_v, M>) +std::basic_string, A>& replace(std::basic_string, A>& str, T&& pattern, M&& repl, size_t offset = 0, size_t max_count = -1) { + return replace(str, str_src{std::forward(pattern)}, str_src{std::forward(repl)}, offset, max_count); +} + } // namespace str } // namespace simstr diff --git a/tests/test_expr_only.cpp b/tests/test_expr_only.cpp index 822a1c2..c0b3369 100644 --- a/tests/test_expr_only.cpp +++ b/tests/test_expr_only.cpp @@ -259,4 +259,31 @@ TEST(StrExpr, StrChange) { } } +TEST(StrExpr, StrReplace) { + { + std::string src = "-aaaaaaaaaaaaaaaa--"; + EXPECT_EQ(str::replace(src, "a", "aa"), "-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa--"); + EXPECT_EQ(str::replace(src, "aa", "b"), "-bbbbbbbbbbbbbbbb--"); + EXPECT_EQ(str::replace(src, "b", ""_ss), "---"); + } + { + std::string src = "-aaaaaaaaaaaaaaaaaa--"; + EXPECT_EQ(str::replace(src, "a", "aa"), "-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa--"); + } + { + std::string src = "-aaaaaaaaaaaaaaaa--"; + EXPECT_EQ(str::replace(src, "a", "a"_ss + "a"), "-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa--"); + EXPECT_EQ(str::replace(src, "aa", eea + "b"), "-bbbbbbbbbbbbbbbb--"); + EXPECT_EQ(str::replace(src, "b", eea), "---"); + } + { + std::u16string src = u"-aaaaaaaaaaaaaaaa--"; + EXPECT_EQ(str::replace(src, u"a", u"vv", 5, 3), u"-aaaavvvvvvaaaaaaaaa--"); + } + { + std::u16string src = u"-aaaaaaaaaaaaaaaa--"; + EXPECT_EQ(str::replace(src, u"a", u"vv"_ss + 10, 5, 3), u"-aaaavv10vv10vv10aaaaaaaaa--"); + } +} + } // namespace simstr::tests diff --git a/tests/test_str.cpp b/tests/test_str.cpp index 266f08b..8133f5b 100644 --- a/tests/test_str.cpp +++ b/tests/test_str.cpp @@ -1071,6 +1071,16 @@ TEST(SimStr, ExprNum) { } TEST(SimStr, LStrSelfReplace) { + { + lstringa<40> test = "-aaaaaaaaaaaaaaaa--"; + test.replace("a", "aa"); + EXPECT_EQ(test, "-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa--"); + } + { + lstringa<40> test = "-aaaaaaaaaaaaaaaaaa--"; + test.replace("a", "aa"); + EXPECT_EQ(test, "-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa--"); + } { lstringa<40> test = "test string"; ssa before = test; @@ -1905,6 +1915,9 @@ TEST(SimStr, StrRepl) { a = e_repl("test"_ss, "t"_ss, "-t-"_ss); EXPECT_EQ(a, "-t-es-t-"); + + a = e_repl("test"_ss, "t", "a"_ss + "1" + 10); + EXPECT_EQ(a, "a110esa110"); } TEST(SimStr, HexEpr) { @@ -1920,6 +1933,12 @@ TEST(SimStr, HexEpr) { stringb hexb = expr_hex{0xcd0102}; EXPECT_EQ(hexb, u8"0x00CD0102"); + hexb = expr_hex{0xcd0102}; + EXPECT_EQ(hexb, u8"0x00CD0102"); + + hexb = expr_hex{-0xcd0102}; + EXPECT_EQ(hexb, u8"-0x00CD0102"); + hexa = expr_hex{0xabcd0102}; EXPECT_EQ(hexa, "00000000abcd0102");