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

Code before:
    }
    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")
{
    return std::string(TrimStringView(str, pattern));
}

[[nodiscard]] inline std::string_view RemoveSuffixView(std::string_view str, std::string_view suffix)
{
    if (str.ends_with(suffix)) {
        return str.substr(0, str.size() - suffix.size());
    }
    return str;
}


Review comment (suggestion):
Alternatively, we could cheaply collect the referenced indexes to a `std::set<unsigned> referenced_params;` and check that all of them were referenced, i.e. `if (referenced_params.size() != num_params) throw "Not all parameters are referenced!";`

Code after:
        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!";
    }
};

void ReplaceAll(std::string& in_out, const std::string& search, const std::string& substitute);

/** Split a string on any char found in separators, 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>>