| 4369 | template <typename char_type_, typename allocator_> |
| 4370 | template <typename pattern_type> |
| 4371 | bool basic_string<char_type_, allocator_>::try_replace_all_(pattern_type pattern, string_view replacement) noexcept { |
| 4372 | // Depending on the size of the pattern and the replacement, we may need to allocate more space. |
| 4373 | // There are 3 cases to consider: |
| 4374 | // 1. The pattern and the replacement are of the same length. Piece of cake! |
| 4375 | // 2. The pattern is longer than the replacement. We need to compact the strings. |
| 4376 | // 3. The pattern is shorter than the replacement. We may have to allocate more memory. |
| 4377 | using matcher_type = typename std::conditional<is_same_type<pattern_type, byteset>::value, |
| 4378 | matcher_find_first_of<string_view, pattern_type>, |
| 4379 | matcher_find<string_view, exclude_overlaps_type>>::type; |
| 4380 | matcher_type matcher({pattern}); |
| 4381 | string_view this_view = view(); |
| 4382 | |
| 4383 | // 1. The pattern and the replacement are of the same length. |
| 4384 | if (matcher.needle_length() == replacement.length()) { |
| 4385 | using matches_type = range_matches<string_view, matcher_type>; |
| 4386 | // Instead of iterating with `begin()` and `end()`, we could use the cheaper sentinel-based approach. |
| 4387 | // for (string_view match : matches) { ... } |
| 4388 | matches_type matches = matches_type(this_view, {pattern}); |
| 4389 | for (auto matches_iterator = matches.begin(); matches_iterator != end_sentinel_type {}; ++matches_iterator) { |
| 4390 | replacement.copy(const_cast<pointer>((*matches_iterator).data())); |
| 4391 | } |
| 4392 | return true; |
| 4393 | } |
| 4394 | |
| 4395 | // 2. The pattern is longer than the replacement. We need to compact the strings. |
| 4396 | else if (matcher.needle_length() > replacement.length()) { |
| 4397 | // Dealing with shorter replacements, we will avoid memory allocations, but we can also minimize the number |
| 4398 | // of `memmove`-s, by keeping one more iterator, pointing to the end of the last compacted area. |
| 4399 | // Having the split-ranges, however, we reuse their logic. |
| 4400 | using splits_type = range_splits<string_view, matcher_type>; |
| 4401 | splits_type splits = splits_type(this_view, {pattern}); |
| 4402 | auto matches_iterator = splits.begin(); |
| 4403 | auto compacted_end = (*matches_iterator).end(); |
| 4404 | if (compacted_end == end()) return true; // No matches. |
| 4405 | |
| 4406 | ++matches_iterator; // Skip the first match. |
| 4407 | do { |
| 4408 | string_view match_view = *matches_iterator; |
| 4409 | replacement.copy(const_cast<pointer>(compacted_end)); |
| 4410 | compacted_end += replacement.length(); |
| 4411 | sz_move((sz_ptr_t)compacted_end, match_view.begin(), match_view.length()); |
| 4412 | compacted_end += match_view.length(); |
| 4413 | ++matches_iterator; |
| 4414 | } while (matches_iterator != end_sentinel_type {}); |
| 4415 | |
| 4416 | // Can't fail, so let's just return true :) |
| 4417 | try_resize(compacted_end - begin()); |
| 4418 | return true; |
| 4419 | } |
| 4420 | |
| 4421 | // 3. The pattern is shorter than the replacement. We may have to allocate more memory. |
| 4422 | else { |
| 4423 | using rmatcher_type = typename std::conditional<is_same_type<pattern_type, byteset>::value, |
| 4424 | matcher_find_last_of<string_view, pattern_type>, |
| 4425 | matcher_rfind<string_view, exclude_overlaps_type>>::type; |
| 4426 | using rmatches_type = range_rmatches<string_view, rmatcher_type>; |
| 4427 | rmatches_type rmatches = rmatches_type(this_view, {pattern}); |
| 4428 | |