Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2019-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#ifndef BITCOIN_UTIL_STRING_H6#define BITCOIN_UTIL_STRING_H8#include <algorithm>9#include <array>10#include <cstddef>11#include <cstdint>12#include <initializer_list>13#include <locale>14#include <optional>15#include <span>16#include <sstream>17#include <string>18#include <string_view>19#include <vector>21#include <attributes.h>23namespace util {24namespace detail {25template <unsigned num_params>26constexpr void CheckNumFormatSpecifiers(const char* str)27{28unsigned count_normal{0}; // Number of "normal" specifiers, like %s29unsigned count_pos{0}; // Max number in positional specifier, like %8$s30for (auto it{str}; *it != '\0'; ++it) {31if (*it != '%' || *++it == '%') continue; // Skip escaped %%33auto add_arg = [&] {34unsigned maybe_num{0};35while ('0' <= *it && *it <= '9') {36maybe_num *= 10;37maybe_num += *it - '0';38++it;39}41if (*it == '$') {42++it;43// Positional specifier, like %8$s44if (maybe_num == 0) throw "Positional format specifier must have position of at least 1";45count_pos = std::max(count_pos, maybe_num);46} else {47// Non-positional specifier, like %s48++count_normal;49}50};52// Increase argument count and consume positional specifier, if present.53add_arg();55// Consume flags.56while (*it == '#' || *it == '0' || *it == '-' || *it == ' ' || *it == '+') ++it;58auto parse_size = [&] {59if (*it == '*') {60++it;61add_arg();62} else {63while ('0' <= *it && *it <= '9') ++it;64}65};67// Consume dynamic or static width value.68parse_size();70// Consume dynamic or static precision value.71if (*it == '.') {72++it;73parse_size();74}76if (*it == '\0') throw "Format specifier incorrectly terminated by end of string";78// Length and type in "[flags][width][.precision][length]type"79// is not checked. Parsing continues with the next '%'.80}81if (count_normal && count_pos) throw "Format specifiers must be all positional or all non-positional!";82unsigned count{count_normal | count_pos};83if (num_params != count) throw "Format specifier count must match the argument count!";84}85} // namespace detail87/**88* @brief A wrapper for a compile-time partially validated format string89*90* This struct can be used to enforce partial compile-time validation of format91* strings, to reduce the likelihood of tinyformat throwing exceptions at92* run-time. Validation is partial to try and prevent the most common errors93* while avoiding re-implementing the entire parsing logic.94*/95template <unsigned num_params>96struct ConstevalFormatString {97const char* const fmt;98consteval ConstevalFormatString(const char* str) : fmt{str} { detail::CheckNumFormatSpecifiers<num_params>(fmt); }99};101/// Replace every non-overlapping occurrence of `search` with `substitute`, treating both literally; the replacement text is not searched again.102void ReplaceAll(std::string& in_out, std::string_view search, std::string_view substitute);104/** Split a string on any char found in separators, returning a vector.105*106* If sep does not occur in sp, a singleton with the entirety of sp is returned.107*108* @param[in] include_sep Whether to include the separator at the end of the left side of the splits.109*110* Note that this function does not care about braces, so splitting111* "foo(bar(1),2),3) on ',' will return {"foo(bar(1)", "2)", "3)"}.112*113* If include_sep == true, splitting "foo(bar(1),2),3) on ','114* will return:115* - foo(bar(1),116* - 2),117* - 3)118*/119template <typename T = std::span<const char>>120std::vector<T> Split(std::span<const char> sp LIFETIMEBOUND, std::string_view separators, bool include_sep = false)121{122std::vector<T> ret;123auto it = sp.begin();124auto start = it;125while (it != sp.end()) {126if (separators.find(*it) != std::string::npos) {127if (include_sep) {128ret.emplace_back(start, it + 1);129} else {130ret.emplace_back(start, it);131}132start = it + 1;133}134++it;135}136ret.emplace_back(start, it);137return ret;138}140/** Split a string on every instance of sep, returning a vector.141*142* If sep does not occur in sp, a singleton with the entirety of sp is returned.143*144* Note that this function does not care about braces, so splitting145* "foo(bar(1),2),3) on ',' will return {"foo(bar(1)", "2)", "3)"}.146*/147template <typename T = std::span<const char>>148std::vector<T> Split(std::span<const char> sp LIFETIMEBOUND, char sep, bool include_sep = false)149{150return Split<T>(sp, std::string_view{&sep, 1}, include_sep);151}153[[nodiscard]] inline std::vector<std::string> SplitString(std::string_view str, char sep)154{155return Split<std::string>(str, sep);156}158[[nodiscard]] inline std::vector<std::string> SplitString(std::string_view str, std::string_view separators)159{160return Split<std::string>(str, separators);161}163[[nodiscard]] inline std::string_view TrimStringView(std::string_view str LIFETIMEBOUND, std::string_view pattern = " \f\n\r\t\v")164{165std::string::size_type front = str.find_first_not_of(pattern);166if (front == std::string::npos) {167return {};168}169std::string::size_type end = str.find_last_not_of(pattern);170return str.substr(front, end - front + 1);171}173[[nodiscard]] inline std::string TrimString(std::string_view str, std::string_view pattern = " \f\n\r\t\v")174{175return std::string(TrimStringView(str, pattern));176}178[[nodiscard]] inline std::string_view RemoveSuffixView(std::string_view str LIFETIMEBOUND, std::string_view suffix)179{180if (str.ends_with(suffix)) {181return str.substr(0, str.size() - suffix.size());182}183return str;184}186[[nodiscard]] inline std::string_view RemovePrefixView(std::string_view str LIFETIMEBOUND, std::string_view prefix)187{188if (str.starts_with(prefix)) {189return str.substr(prefix.size());190}191return str;192}194[[nodiscard]] inline std::string RemovePrefix(std::string_view str, std::string_view prefix)195{196return std::string(RemovePrefixView(str, prefix));197}199/**200* Join all container items. Typically used to concatenate strings but accepts201* containers with elements of any type.202*203* @param container The items to join204* @param separator The separator205* @param unary_op Apply this operator to each item206*/207template <typename C, typename S, typename UnaryOp>208// NOLINTNEXTLINE(misc-no-recursion)209auto Join(const C& container, const S& separator, UnaryOp unary_op)210{211decltype(unary_op(*container.begin())) ret;212bool first{true};213for (const auto& item : container) {214if (!first) ret += separator;215ret += unary_op(item);216first = false;217}218return ret;219}221template <typename C, typename S>222auto Join(const C& container, const S& separator)223{224return Join(container, separator, [](const auto& i) { return i; });225}227/**228* Create an unordered multi-line list of items.229*/230inline std::string MakeUnorderedList(const std::vector<std::string>& items)231{232return Join(items, "\n", [](const std::string& item) { return "- " + item; });233}235/**236* Check if a string contains any embedded NUL (\0) characters237*/238[[nodiscard]] inline bool ContainsNUL(std::string_view str) noexcept239{240for (auto c : str) {241if (c == 0) return true;242}243return false;244}246/**247* Locale-independent version of std::to_string248*/249template <typename T>250std::string ToString(const T& t)
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.