From 12bc1d0b1e9681c338c9d0df0bbac1d4a3162322 Mon Sep 17 00:00:00 2001 From: Ava Chow Date: Mon, 14 Apr 2025 13:31:31 -0700 Subject: [PATCH] util/string: Allow Split to include the separator When splitting a string, sometimes the separator needs to be included. Split will now optionally include the separator at the end of the left side of the splits, i.e. it appears at the end of the splits, except for the last one. Specifically, for musig() descriptors, Split is used to separate a musig() from any derivation path that follows it by splitting on the closing parentheses. Since that parentheses is needed for Func() and Expr(), Split() needs to preserve the end parentheses instead of discarding it. --- src/test/util_tests.cpp | 13 +++++++++++++ src/util/string.h | 20 ++++++++++++++++---- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 8016c2d8c56..f9038c4fc97 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -1246,6 +1246,12 @@ BOOST_AUTO_TEST_CASE(test_script_parsing) BOOST_CHECK_EQUAL(SpanToStr(results[1]), "two"); BOOST_CHECK_EQUAL(SpanToStr(results[2]), "three"); + results = Split(input, '#', /*include_sep=*/true); + BOOST_CHECK_EQUAL(results.size(), 3U); + BOOST_CHECK_EQUAL(SpanToStr(results[0]), "one#"); + BOOST_CHECK_EQUAL(SpanToStr(results[1]), "two#"); + BOOST_CHECK_EQUAL(SpanToStr(results[2]), "three"); + input = "*foo*bar*"; results = Split(input, '*'); BOOST_CHECK_EQUAL(results.size(), 4U); @@ -1253,6 +1259,13 @@ BOOST_AUTO_TEST_CASE(test_script_parsing) BOOST_CHECK_EQUAL(SpanToStr(results[1]), "foo"); BOOST_CHECK_EQUAL(SpanToStr(results[2]), "bar"); BOOST_CHECK_EQUAL(SpanToStr(results[3]), ""); + + results = Split(input, '*', /*include_sep=*/true); + BOOST_CHECK_EQUAL(results.size(), 4U); + BOOST_CHECK_EQUAL(SpanToStr(results[0]), "*"); + BOOST_CHECK_EQUAL(SpanToStr(results[1]), "foo*"); + BOOST_CHECK_EQUAL(SpanToStr(results[2]), "bar*"); + BOOST_CHECK_EQUAL(SpanToStr(results[3]), ""); } BOOST_AUTO_TEST_CASE(test_SplitString) diff --git a/src/util/string.h b/src/util/string.h index 4b875256275..330c2a2a61e 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -100,18 +100,30 @@ void ReplaceAll(std::string& in_out, const std::string& search, const std::strin * * If sep does not occur in sp, a singleton with the entirety of sp is returned. * + * @param[in] include_sep Whether to include the separator at the end of the left side of the splits. + * * Note that this function does not care about braces, so splitting * "foo(bar(1),2),3) on ',' will return {"foo(bar(1)", "2)", "3)"}. + * + * If include_sep == true, splitting "foo(bar(1),2),3) on ',' + * will return: + * - foo(bar(1), + * - 2), + * - 3) */ template > -std::vector Split(const std::span& sp, std::string_view separators) +std::vector Split(const std::span& sp, std::string_view separators, bool include_sep = false) { std::vector ret; auto it = sp.begin(); auto start = it; while (it != sp.end()) { if (separators.find(*it) != std::string::npos) { - ret.emplace_back(start, it); + if (include_sep) { + ret.emplace_back(start, it + 1); + } else { + ret.emplace_back(start, it); + } start = it + 1; } ++it; @@ -128,9 +140,9 @@ std::vector Split(const std::span& sp, std::string_view separator * "foo(bar(1),2),3) on ',' will return {"foo(bar(1)", "2)", "3)"}. */ template > -std::vector Split(const std::span& sp, char sep) +std::vector Split(const std::span& sp, char sep, bool include_sep = false) { - return Split(sp, std::string_view{&sep, 1}); + return Split(sp, std::string_view{&sep, 1}, include_sep); } [[nodiscard]] inline std::vector SplitString(std::string_view str, char sep)