LCOV - code coverage report
Current view: top level - src/util - strencodings.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 301 305 98.7 %
Date: 2026-06-25 07:23:43 Functions: 40 40 100.0 %

          Line data    Source code
       1             : // Copyright (c) 2009-2010 Satoshi Nakamoto
       2             : // Copyright (c) 2009-2021 The Bitcoin Core developers
       3             : // Distributed under the MIT software license, see the accompanying
       4             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       5             : 
       6             : #include <span.h>
       7             : #include <util/strencodings.h>
       8             : 
       9             : #include <array>
      10             : #include <cassert>
      11             : #include <cstring>
      12             : #include <limits>
      13             : #include <optional>
      14             : #include <ostream>
      15             : #include <string>
      16             : #include <vector>
      17             : 
      18        4714 : static const std::string CHARS_ALPHA_NUM = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
      19             : 
      20           0 : static const std::string SAFE_CHARS[] =
      21        4714 : {
      22        4714 :     CHARS_ALPHA_NUM + " .,;-_/:?@()", // SAFE_CHARS_DEFAULT
      23        4714 :     CHARS_ALPHA_NUM + " .,;-_?@", // SAFE_CHARS_UA_COMMENT
      24        4714 :     CHARS_ALPHA_NUM + ".-_", // SAFE_CHARS_FILENAME
      25        4714 :     CHARS_ALPHA_NUM + "!*'();:@&=+$,/?#[]-_.~%", // SAFE_CHARS_URI
      26             : };
      27             : 
      28     2721024 : std::string SanitizeString(std::string_view str, int rule)
      29             : {
      30     2721024 :     std::string result;
      31    22340137 :     for (char c : str) {
      32    19619113 :         if (SAFE_CHARS[rule].find(c) != std::string::npos) {
      33    19618869 :             result.push_back(c);
      34    19618869 :         }
      35             :     }
      36     2721024 :     return result;
      37     2721024 : }
      38             : 
      39             : const signed char p_util_hexdigit[256] =
      40             : { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
      41             :   -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
      42             :   -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
      43             :   0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1,
      44             :   -1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1,
      45             :   -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
      46             :   -1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1,
      47             :   -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
      48             :   -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
      49             :   -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
      50             :   -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
      51             :   -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
      52             :   -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
      53             :   -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
      54             :   -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
      55             :   -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, };
      56             : 
      57   517635111 : signed char HexDigit(char c)
      58             : {
      59   517635111 :     return p_util_hexdigit[(unsigned char)c];
      60             : }
      61             : 
      62      104244 : bool IsHex(std::string_view str)
      63             : {
      64   227916105 :     for (char c : str) {
      65   227812484 :         if (HexDigit(c) < 0) return false;
      66             :     }
      67      103621 :     return (str.size() > 0) && (str.size()%2 == 0);
      68      104244 : }
      69             : 
      70          46 : bool IsHexNumber(std::string_view str)
      71             : {
      72          46 :     if (str.substr(0, 2) == "0x") str.remove_prefix(2);
      73         272 :     for (char c : str) {
      74         242 :         if (HexDigit(c) < 0) return false;
      75             :     }
      76             :     // Return false for empty string or "0x".
      77          30 :     return str.size() > 0;
      78          46 : }
      79             : 
      80             : template <typename Byte>
      81       59029 : std::optional<std::vector<Byte>> TryParseHex(std::string_view str)
      82             : {
      83       59029 :     std::vector<Byte> vch;
      84       59029 :     auto it = str.begin();
      85   114437179 :     while (it != str.end()) {
      86   114378166 :         if (IsSpace(*it)) {
      87         168 :             ++it;
      88         168 :             continue;
      89             :         }
      90   114377998 :         auto c1 = HexDigit(*(it++));
      91   114377998 :         if (it == str.end()) return std::nullopt;
      92   114377994 :         auto c2 = HexDigit(*(it++));
      93   114377994 :         if (c1 < 0 || c2 < 0) return std::nullopt;
      94   114377982 :         vch.push_back(Byte(c1 << 4) | Byte(c2));
      95             :     }
      96       59013 :     return vch;
      97       59029 : }
      98             : template std::optional<std::vector<std::byte>> TryParseHex(std::string_view);
      99             : template std::optional<std::vector<uint8_t>> TryParseHex(std::string_view);
     100             : 
     101     1543492 : bool SplitHostPort(std::string_view in, uint16_t& portOut, std::string& hostOut)
     102             : {
     103     1543492 :     bool valid = false;
     104     1543492 :     size_t colon = in.find_last_of(':');
     105             :     // if a : is found, and it either follows a [...], or no other : is in the string, treat it as port separator
     106     1543492 :     bool fHaveColon = colon != in.npos;
     107     1547243 :     bool fBracketed = fHaveColon && (in[0] == '[' && in[colon - 1] == ']'); // if there is a colon, and in[0]=='[', colon is not 0, so in[colon-1] is safe
     108     1543492 :     bool fMultiColon{fHaveColon && colon != 0 && (in.find_last_of(':', colon - 1) != in.npos)};
     109     1543492 :     if (fHaveColon && (colon == 0 || fBracketed || !fMultiColon)) {
     110             :         uint16_t n;
     111        3728 :         if (ParseUInt16(in.substr(colon + 1), &n)) {
     112        3568 :             in = in.substr(0, colon);
     113        3568 :             portOut = n;
     114        3568 :             valid = (portOut != 0);
     115        3568 :         }
     116        3728 :     } else {
     117     1539764 :         valid = true;
     118             :     }
     119     1543492 :     if (in.size() > 0 && in[0] == '[' && in[in.size() - 1] == ']') {
     120          33 :         hostOut = in.substr(1, in.size() - 2);
     121          33 :     } else {
     122     1543459 :         hostOut = in;
     123             :     }
     124             : 
     125     1543492 :     return valid;
     126             : }
     127             : 
     128        2339 : std::string EncodeBase64(Span<const unsigned char> input)
     129             : {
     130             :     static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
     131             : 
     132        2339 :     std::string str;
     133        2339 :     str.reserve(((input.size() + 2) / 3) * 4);
     134      647451 :     ConvertBits<8, 6, true>([&](int v) { str += pbase64[v]; }, input.begin(), input.end());
     135        3231 :     while (str.size() % 4) str += '=';
     136        2339 :     return str;
     137        2339 : }
     138             : 
     139      501225 : std::optional<std::vector<unsigned char>> DecodeBase64(std::string_view str)
     140             : {
     141             :     static const int8_t decode64_table[256]{
     142             :         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
     143             :         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
     144             :         -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1,
     145             :         -1, -1, -1, -1, -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
     146             :         15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
     147             :         29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
     148             :         49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
     149             :         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
     150             :         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
     151             :         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
     152             :         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
     153             :         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
     154             :         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
     155             :     };
     156             : 
     157      501225 :     if (str.size() % 4 != 0) return {};
     158             :     /* One or two = characters at the end are permitted. */
     159      501205 :     if (str.size() >= 1 && str.back() == '=') str.remove_suffix(1);
     160      501205 :     if (str.size() >= 1 && str.back() == '=') str.remove_suffix(1);
     161             : 
     162      501205 :     std::vector<unsigned char> ret;
     163      501205 :     ret.reserve((str.size() * 3) / 4);
     164      501205 :     bool valid = ConvertBits<6, 8, false>(
     165    38367868 :         [&](unsigned char c) { ret.push_back(c); },
     166      501205 :         str.begin(), str.end(),
     167    50489255 :         [](char c) { return decode64_table[uint8_t(c)]; }
     168             :     );
     169      501205 :     if (!valid) return {};
     170             : 
     171      501202 :     return ret;
     172      501225 : }
     173             : 
     174         340 : std::string EncodeBase32(Span<const unsigned char> input, bool pad)
     175             : {
     176             :     static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567";
     177             : 
     178         340 :     std::string str;
     179         340 :     str.reserve(((input.size() + 4) / 5) * 8);
     180       18028 :     ConvertBits<8, 5, true>([&](int v) { str += pbase32[v]; }, input.begin(), input.end());
     181         340 :     if (pad) {
     182         213 :         while (str.size() % 8) {
     183          20 :             str += '=';
     184             :         }
     185         193 :     }
     186         340 :     return str;
     187         340 : }
     188             : 
     189          14 : std::string EncodeBase32(std::string_view str, bool pad)
     190             : {
     191          14 :     return EncodeBase32(MakeUCharSpan(str), pad);
     192             : }
     193             : 
     194         131 : std::optional<std::vector<unsigned char>> DecodeBase32(std::string_view str)
     195             : {
     196             :     static const int8_t decode32_table[256]{
     197             :         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
     198             :         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
     199             :         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1,
     200             :         -1, -1, -1, -1, -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
     201             :         15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1,  0,  1,  2,
     202             :          3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
     203             :         23, 24, 25, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
     204             :         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
     205             :         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
     206             :         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
     207             :         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
     208             :         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
     209             :         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
     210             :     };
     211             : 
     212         131 :     if (str.size() % 8 != 0) return {};
     213             :     /* 1, 3, 4, or 6 padding '=' suffix characters are permitted. */
     214         130 :     if (str.size() >= 1 && str.back() == '=') str.remove_suffix(1);
     215         130 :     if (str.size() >= 2 && str.substr(str.size() - 2) == "==") str.remove_suffix(2);
     216         130 :     if (str.size() >= 1 && str.back() == '=') str.remove_suffix(1);
     217         130 :     if (str.size() >= 2 && str.substr(str.size() - 2) == "==") str.remove_suffix(2);
     218             : 
     219         130 :     std::vector<unsigned char> ret;
     220         130 :     ret.reserve((str.size() * 5) / 8);
     221         130 :     bool valid = ConvertBits<5, 8, false>(
     222        4093 :         [&](unsigned char c) { ret.push_back(c); },
     223         130 :         str.begin(), str.end(),
     224        6384 :         [](char c) { return decode32_table[uint8_t(c)]; }
     225             :     );
     226             : 
     227         130 :     if (!valid) return {};
     228             : 
     229         126 :     return ret;
     230         131 : }
     231             : 
     232             : namespace {
     233             : template <typename T>
     234       31034 : bool ParseIntegral(std::string_view str, T* out)
     235             : {
     236             :     static_assert(std::is_integral<T>::value);
     237             :     // Replicate the exact behavior of strtol/strtoll/strtoul/strtoull when
     238             :     // handling leading +/- for backwards compatibility.
     239       31034 :     if (str.length() >= 2 && str[0] == '+' && str[1] == '-') {
     240           8 :         return false;
     241             :     }
     242       31026 :     const std::optional<T> opt_int = ToIntegral<T>((!str.empty() && str[0] == '+') ? str.substr(1) : str);
     243       31026 :     if (!opt_int) {
     244         414 :         return false;
     245             :     }
     246       30612 :     if (out != nullptr) {
     247       30600 :         *out = *opt_int;
     248       30600 :     }
     249       30612 :     return true;
     250       31034 : }
     251             : }; // namespace
     252             : 
     253        7316 : bool ParseInt32(std::string_view str, int32_t* out)
     254             : {
     255        7316 :     return ParseIntegral<int32_t>(str, out);
     256             : }
     257             : 
     258        2167 : bool ParseInt64(std::string_view str, int64_t* out)
     259             : {
     260        2167 :     return ParseIntegral<int64_t>(str, out);
     261             : }
     262             : 
     263         180 : bool ParseUInt8(std::string_view str, uint8_t* out)
     264             : {
     265         180 :     return ParseIntegral<uint8_t>(str, out);
     266             : }
     267             : 
     268        9652 : bool ParseUInt16(std::string_view str, uint16_t* out)
     269             : {
     270        9652 :     return ParseIntegral<uint16_t>(str, out);
     271             : }
     272             : 
     273       11639 : bool ParseUInt32(std::string_view str, uint32_t* out)
     274             : {
     275       11639 :     return ParseIntegral<uint32_t>(str, out);
     276             : }
     277             : 
     278          80 : bool ParseUInt64(std::string_view str, uint64_t* out)
     279             : {
     280          80 :     return ParseIntegral<uint64_t>(str, out);
     281             : }
     282             : 
     283         380 : std::string FormatParagraph(std::string_view in, size_t width, size_t indent)
     284             : {
     285         380 :     assert(width >= indent);
     286         380 :     std::stringstream out;
     287         380 :     size_t ptr = 0;
     288         380 :     size_t indented = 0;
     289        1346 :     while (ptr < in.size())
     290             :     {
     291         968 :         size_t lineend = in.find_first_of('\n', ptr);
     292         968 :         if (lineend == std::string::npos) {
     293         892 :             lineend = in.size();
     294         892 :         }
     295         968 :         const size_t linelen = lineend - ptr;
     296         968 :         const size_t rem_width = width - indented;
     297         968 :         if (linelen <= rem_width) {
     298         430 :             out << in.substr(ptr, linelen + 1);
     299         430 :             ptr = lineend + 1;
     300         430 :             indented = 0;
     301         430 :         } else {
     302         538 :             size_t finalspace = in.find_last_of(" \n", ptr + rem_width);
     303         538 :             if (finalspace == std::string::npos || finalspace < ptr) {
     304             :                 // No place to break; just include the entire word and move on
     305           8 :                 finalspace = in.find_first_of("\n ", ptr);
     306           8 :                 if (finalspace == std::string::npos) {
     307             :                     // End of the string, just add it and break
     308           2 :                     out << in.substr(ptr);
     309           2 :                     break;
     310             :                 }
     311           6 :             }
     312         536 :             out << in.substr(ptr, finalspace - ptr) << "\n";
     313         536 :             if (in[finalspace] == '\n') {
     314           2 :                 indented = 0;
     315         536 :             } else if (indent) {
     316         506 :                 out << std::string(indent, ' ');
     317         506 :                 indented = indent;
     318         506 :             }
     319         536 :             ptr = finalspace + 1;
     320             :         }
     321             :     }
     322         380 :     return out.str();
     323         380 : }
     324             : 
     325             : /** Upper bound for mantissa.
     326             :  * 10^18-1 is the largest arbitrary decimal that will fit in a signed 64-bit integer.
     327             :  * Larger integers cannot consist of arbitrary combinations of 0-9:
     328             :  *
     329             :  *   999999999999999999  1^18-1
     330             :  *  9223372036854775807  (1<<63)-1  (max int64_t)
     331             :  *  9999999999999999999  1^19-1     (would overflow)
     332             :  */
     333             : static const int64_t UPPER_BOUND = 1000000000000000000LL - 1LL;
     334             : 
     335             : /** Helper function for ParseFixedPoint */
     336      183067 : static inline bool ProcessMantissaDigit(char ch, int64_t &mantissa, int &mantissa_tzeros)
     337             : {
     338      183067 :     if(ch == '0')
     339       29398 :         ++mantissa_tzeros;
     340             :     else {
     341      328080 :         for (int i=0; i<=mantissa_tzeros; ++i) {
     342      174477 :             if (mantissa > (UPPER_BOUND / 10LL))
     343          66 :                 return false; /* overflow */
     344      174411 :             mantissa *= 10;
     345      174411 :         }
     346      153603 :         mantissa += ch - '0';
     347      153603 :         mantissa_tzeros = 0;
     348             :     }
     349      183001 :     return true;
     350      183067 : }
     351             : 
     352       50076 : bool ParseFixedPoint(std::string_view val, int decimals, int64_t *amount_out)
     353             : {
     354       50076 :     int64_t mantissa = 0;
     355       50076 :     int64_t exponent = 0;
     356       50076 :     int mantissa_tzeros = 0;
     357       50076 :     bool mantissa_sign = false;
     358       50076 :     bool exponent_sign = false;
     359       50076 :     int ptr = 0;
     360       50076 :     int end = val.size();
     361       50076 :     int point_ofs = 0;
     362             : 
     363       50076 :     if (ptr < end && val[ptr] == '-') {
     364          84 :         mantissa_sign = true;
     365          84 :         ++ptr;
     366          84 :     }
     367       50076 :     if (ptr < end)
     368             :     {
     369       50029 :         if (val[ptr] == '0') {
     370             :             /* pass single 0 */
     371       26557 :             ++ptr;
     372       50029 :         } else if (val[ptr] >= '1' && val[ptr] <= '9') {
     373       52138 :             while (ptr < end && IsDigit(val[ptr])) {
     374       28677 :                 if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros))
     375           0 :                     return false; /* overflow */
     376       28677 :                 ++ptr;
     377             :             }
     378       23472 :         } else return false; /* missing expected digit */
     379       50065 :     } else return false; /* empty string or loose '-' */
     380       50018 :     if (ptr < end && val[ptr] == '.')
     381             :     {
     382       33903 :         ++ptr;
     383       33903 :         if (ptr < end && IsDigit(val[ptr]))
     384             :         {
     385      188225 :             while (ptr < end && IsDigit(val[ptr])) {
     386      154390 :                 if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros))
     387          66 :                     return false; /* overflow */
     388      154324 :                 ++ptr;
     389      154324 :                 ++point_ofs;
     390             :             }
     391       33837 :         } else return false; /* missing expected digit */
     392       33835 :     }
     393       49950 :     if (ptr < end && (val[ptr] == 'e' || val[ptr] == 'E'))
     394             :     {
     395       14699 :         ++ptr;
     396       14699 :         if (ptr < end && val[ptr] == '+')
     397           3 :             ++ptr;
     398       14696 :         else if (ptr < end && val[ptr] == '-') {
     399       14690 :             exponent_sign = true;
     400       14690 :             ++ptr;
     401       14690 :         }
     402       14699 :         if (ptr < end && IsDigit(val[ptr])) {
     403       43988 :             while (ptr < end && IsDigit(val[ptr])) {
     404       29293 :                 if (exponent > (UPPER_BOUND / 10LL))
     405           0 :                     return false; /* overflow */
     406       29293 :                 exponent = exponent * 10 + val[ptr] - '0';
     407       29293 :                 ++ptr;
     408             :             }
     409       14699 :         } else return false; /* missing expected digit */
     410       14695 :     }
     411       49946 :     if (ptr != end)
     412          12 :         return false; /* trailing garbage */
     413             : 
     414             :     /* finalize exponent */
     415       49934 :     if (exponent_sign)
     416       14688 :         exponent = -exponent;
     417       49934 :     exponent = exponent - point_ofs + mantissa_tzeros;
     418             : 
     419             :     /* finalize mantissa */
     420       49934 :     if (mantissa_sign)
     421          64 :         mantissa = -mantissa;
     422             : 
     423             :     /* convert to one 64-bit fixed-point value */
     424       49934 :     exponent += decimals;
     425       49934 :     if (exponent < 0)
     426         386 :         return false; /* cannot represent values smaller than 10^-decimals */
     427       49548 :     if (exponent >= 18)
     428           6 :         return false; /* cannot represent values larger than or equal to 10^(18-decimals) */
     429             : 
     430      224362 :     for (int i=0; i < exponent; ++i) {
     431      174864 :         if (mantissa > (UPPER_BOUND / 10LL) || mantissa < -(UPPER_BOUND / 10LL))
     432          44 :             return false; /* overflow */
     433      174820 :         mantissa *= 10;
     434      174820 :     }
     435       49498 :     if (mantissa > UPPER_BOUND || mantissa < -UPPER_BOUND)
     436           0 :         return false; /* overflow */
     437             : 
     438       49498 :     if (amount_out)
     439       49498 :         *amount_out = mantissa;
     440             : 
     441       49498 :     return true;
     442       50076 : }
     443             : 
     444        8496 : std::string ToLower(std::string_view str)
     445             : {
     446        8496 :     std::string r;
     447        8496 :     r.reserve(str.size());
     448       89269 :     for (auto ch : str) r += ToLower(ch);
     449        8496 :     return r;
     450        8496 : }
     451             : 
     452        1192 : std::string ToUpper(std::string_view str)
     453             : {
     454        1192 :     std::string r;
     455        1192 :     r.reserve(str.size());
     456       11042 :     for (auto ch : str) r += ToUpper(ch);
     457        1192 :     return r;
     458        1192 : }
     459             : 
     460         160 : std::string Capitalize(std::string str)
     461             : {
     462         160 :     if (str.empty()) return str;
     463         158 :     str[0] = ToUpper(str.front());
     464         158 :     return str;
     465         160 : }
     466             : 
     467             : namespace {
     468             : 
     469             : using ByteAsHex = std::array<char, 2>;
     470             : 
     471             : constexpr std::array<ByteAsHex, 256> CreateByteToHexMap()
     472             : {
     473             :     constexpr char hexmap[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
     474             : 
     475             :     std::array<ByteAsHex, 256> byte_to_hex{};
     476             :     for (size_t i = 0; i < byte_to_hex.size(); ++i) {
     477             :         byte_to_hex[i][0] = hexmap[i >> 4];
     478             :         byte_to_hex[i][1] = hexmap[i & 15];
     479             :     }
     480             :     return byte_to_hex;
     481             : }
     482             : 
     483             : } // namespace
     484             : 
     485    12488548 : std::string HexStr(const Span<const uint8_t> s)
     486             : {
     487    12488548 :     std::string rv(s.size() * 2, '\0');
     488             :     static constexpr auto byte_to_hex = CreateByteToHexMap();
     489             :     static_assert(sizeof(byte_to_hex) == 512);
     490             : 
     491    12488548 :     char* it = rv.data();
     492   467887081 :     for (uint8_t v : s) {
     493   455398533 :         std::memcpy(it, byte_to_hex[v].data(), 2);
     494   455398533 :         it += 2;
     495             :     }
     496             : 
     497    12488548 :     assert(it == rv.data() + rv.size());
     498    12488548 :     return rv;
     499    12488548 : }
     500             : 
     501        3051 : std::optional<uint64_t> ParseByteUnits(std::string_view str, ByteUnit default_multiplier)
     502             : {
     503        3051 :     if (str.empty()) {
     504           1 :         return std::nullopt;
     505             :     }
     506        3050 :     auto multiplier = default_multiplier;
     507        3050 :     char unit = str.back();
     508        3050 :     switch (unit) {
     509             :     case 'k':
     510           1 :         multiplier = ByteUnit::k;
     511           1 :         break;
     512             :     case 'K':
     513           1 :         multiplier = ByteUnit::K;
     514           1 :         break;
     515             :     case 'm':
     516           4 :         multiplier = ByteUnit::m;
     517           4 :         break;
     518             :     case 'M':
     519        3026 :         multiplier = ByteUnit::M;
     520        3026 :         break;
     521             :     case 'g':
     522           2 :         multiplier = ByteUnit::g;
     523           2 :         break;
     524             :     case 'G':
     525           1 :         multiplier = ByteUnit::G;
     526           1 :         break;
     527             :     case 't':
     528           1 :         multiplier = ByteUnit::t;
     529           1 :         break;
     530             :     case 'T':
     531           2 :         multiplier = ByteUnit::T;
     532           2 :         break;
     533             :     default:
     534          12 :         unit = 0;
     535          12 :         break;
     536             :     }
     537             : 
     538        3050 :     uint64_t unit_amount = static_cast<uint64_t>(multiplier);
     539        3050 :     auto parsed_num = ToIntegral<uint64_t>(unit ? str.substr(0, str.size() - 1) : str);
     540        3050 :     if (!parsed_num || parsed_num > std::numeric_limits<uint64_t>::max() / unit_amount) { // check overflow
     541          11 :         return std::nullopt;
     542             :     }
     543        3039 :     return *parsed_num * unit_amount;
     544        3051 : }

Generated by: LCOV version 1.16