Repository: bitcoin/bitcoin
File: src/util/string.h (C/C++)

Code before:
template <typename T = Span<const char>>
std::vector<T> Split(const Span<const char>& sp, std::string_view separators)
{
    std::vector<T> ret;
    auto it = sp.begin();
    auto start = it;
    while (it != sp.end()) {
        if (separators.find(*it) != std::string::npos) {
            ret.emplace_back(start, it);
            start = it + 1;
        }
        ++it;
    }
    ret.emplace_back(start, it);
    return ret;
}

/** Split a string on every instance of sep, returning a vector.
 *
 * If sep does not occur in sp, a singleton with the entirety of sp is returned.
 *
 * Note that this function does not care about braces, so splitting
 * "foo(bar(1),2),3) on ',' will return {"foo(bar(1)", "2)", "3)"}.
 */
template <typename T = Span<const char>>
std::vector<T> Split(const Span<const char>& sp, char sep)
{
    return Split<T>(sp, std::string_view{&sep, 1});
}

[[nodiscard]] inline std::vector<std::string> SplitString(std::string_view str, char sep)
{
    return Split<std::string>(str, sep);
}

[[nodiscard]] inline std::vector<std::string> SplitString(std::string_view str, std::string_view separators)
{
    return Split<std::string>(str, separators);
}

[[nodiscard]] inline std::string_view TrimStringView(std::string_view str, std::string_view pattern = " \f\n\r\t\v")
{
    std::string::size_type front = str.find_first_not_of(pattern);
    if (front == std::string::npos) {
        return {};
    }
    std::string::size_type end = str.find_last_not_of(pattern);
    return str.substr(front, end - front + 1);
}

[[nodiscard]] inline std::string TrimString(std::string_view str, std::string_view pattern = " \f\n\r\t\v")

Review comment (suggestion):
phrasing nit
```suggestion
                if (maybe_num == 0) throw "Positional format specifier must have position of at least 1";
```

Code after:
 * @note Counting of `*` dynamic width and precision fields (such as `%*c`,
 * `%2$*3$d`, `%.*f`) is not implemented to minimize code complexity as long as
 * they are not used in the codebase. Usage of these fields is not counted and
 * can lead to run-time exceptions. Code wanting to use the `*` specifier can
 * side-step this struct and call tinyformat directly.
 */
template <unsigned num_params>
struct ConstevalFormatString {
    const char* const fmt;
    consteval ConstevalFormatString(const char* str) : fmt{str} { Detail_CheckNumFormatSpecifiers(fmt); }
    constexpr static void Detail_CheckNumFormatSpecifiers(std::string_view str)
    {
        unsigned count_normal{0}; // Number of "normal" specifiers, like %s
        unsigned count_pos{0};    // Max number in positional specifier, like %8$s
        for (auto it{str.begin()}; it < str.end();) {
            if (*it != '%') {
                ++it;
                continue;
            }

            if (++it >= str.end()) throw "Format specifier incorrectly terminated by end of string";
            if (*it == '%') {
                // Percent escape: %%
                ++it;
                continue;
            }

            unsigned maybe_num{0};
            while ('0' <= *it && *it <= '9') {
                maybe_num *= 10;
                maybe_num += *it - '0';
                ++it;
            };

            if (*it == '$') {
                // Positional specifier, like %8$s
                if (maybe_num == 0) throw "Positional format specifier must have position of at least 1";
                count_pos = std::max(count_pos, maybe_num);
                if (++it >= str.end()) throw "Format specifier incorrectly terminated by end of string";
            } else {
                // Non-positional specifier, like %s
                ++count_normal;
                ++it;
            }
            // The remainder "[flags][width][.precision][length]type" of the
            // specifier is not checked. Parsing continues with the next '%'.
        }
        if (count_normal && count_pos) throw "Format specifiers must be all positional or all non-positional!";
        unsigned count{count_normal | count_pos};
        if (num_params != count) throw "Format specifier count must match the argument count!";
    }