From 620383a5a4980053cb9b592a49ea7f8721725590 Mon Sep 17 00:00:00 2001 From: Aleksandr Orefkov Date: Sat, 24 Jan 2026 17:59:59 +0300 Subject: [PATCH] - Added the ability to convert signed numbers to hexadecimal strings. - Added methods for replacing substrings for standard strings. - Added the ability to pass a string expression as the replacement string to the substring replacement methods. If the searched substring is not found, it is not instantiated. If it is found, it is instantiated at the replacement insertion point, and is simply copied from there in subsequent replacements. --- include/simstr/sstring.h | 31 ++- include/simstr/strexpr.h | 420 ++++++++++++++++++++++++++++++++------- tests/test_expr_only.cpp | 27 +++ tests/test_str.cpp | 19 ++ 4 files changed, 412 insertions(+), 85 deletions(-) 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");