Coverage Report

Created: 2025-06-10 13:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/bitcoin/src/util/strencodings.cpp
Line
Count
Source
1
// Copyright (c) 2009-2010 Satoshi Nakamoto
2
// Copyright (c) 2009-present 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 <util/strencodings.h>
7
8
#include <crypto/hex_base.h>
9
#include <span.h>
10
11
#include <array>
12
#include <cassert>
13
#include <cstring>
14
#include <limits>
15
#include <optional>
16
#include <ostream>
17
#include <string>
18
#include <vector>
19
20
static const std::string CHARS_ALPHA_NUM = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
21
22
static const std::string SAFE_CHARS[] =
23
{
24
    CHARS_ALPHA_NUM + " .,;-_/:?@()", // SAFE_CHARS_DEFAULT
25
    CHARS_ALPHA_NUM + " .,;-_?@", // SAFE_CHARS_UA_COMMENT
26
    CHARS_ALPHA_NUM + ".-_", // SAFE_CHARS_FILENAME
27
    CHARS_ALPHA_NUM + "!*'();:@&=+$,/?#[]-_.~%", // SAFE_CHARS_URI
28
};
29
30
std::string SanitizeString(std::string_view str, int rule)
31
10.3M
{
32
10.3M
    std::string result;
33
86.7M
    for (char c : str) {
  Branch (33:17): [True: 86.7M, False: 10.3M]
34
86.7M
        if (SAFE_CHARS[rule].find(c) != std::string::npos) {
  Branch (34:13): [True: 86.7M, False: 18.4E]
35
86.7M
            result.push_back(c);
36
86.7M
        }
37
86.7M
    }
38
10.3M
    return result;
39
10.3M
}
40
41
bool IsHex(std::string_view str)
42
0
{
43
0
    for (char c : str) {
  Branch (43:17): [True: 0, False: 0]
44
0
        if (HexDigit(c) < 0) return false;
  Branch (44:13): [True: 0, False: 0]
45
0
    }
46
0
    return (str.size() > 0) && (str.size()%2 == 0);
  Branch (46:12): [True: 0, False: 0]
  Branch (46:32): [True: 0, False: 0]
47
0
}
48
49
template <typename Byte>
50
std::optional<std::vector<Byte>> TryParseHex(std::string_view str)
51
0
{
52
0
    std::vector<Byte> vch;
53
0
    vch.reserve(str.size() / 2); // two hex characters form a single byte
54
55
0
    auto it = str.begin();
56
0
    while (it != str.end()) {
  Branch (56:12): [True: 0, False: 0]
  Branch (56:12): [True: 0, False: 0]
57
0
        if (IsSpace(*it)) {
  Branch (57:13): [True: 0, False: 0]
  Branch (57:13): [True: 0, False: 0]
58
0
            ++it;
59
0
            continue;
60
0
        }
61
0
        auto c1 = HexDigit(*(it++));
62
0
        if (it == str.end()) return std::nullopt;
  Branch (62:13): [True: 0, False: 0]
  Branch (62:13): [True: 0, False: 0]
63
0
        auto c2 = HexDigit(*(it++));
64
0
        if (c1 < 0 || c2 < 0) return std::nullopt;
  Branch (64:13): [True: 0, False: 0]
  Branch (64:23): [True: 0, False: 0]
  Branch (64:13): [True: 0, False: 0]
  Branch (64:23): [True: 0, False: 0]
65
0
        vch.push_back(Byte(c1 << 4) | Byte(c2));
66
0
    }
67
0
    return vch;
68
0
}
Unexecuted instantiation: std::optional<std::vector<std::byte, std::allocator<std::byte> > > TryParseHex<std::byte>(std::basic_string_view<char, std::char_traits<char> >)
Unexecuted instantiation: std::optional<std::vector<unsigned char, std::allocator<unsigned char> > > TryParseHex<unsigned char>(std::basic_string_view<char, std::char_traits<char> >)
69
template std::optional<std::vector<std::byte>> TryParseHex(std::string_view);
70
template std::optional<std::vector<uint8_t>> TryParseHex(std::string_view);
71
72
bool SplitHostPort(std::string_view in, uint16_t& portOut, std::string& hostOut)
73
7.13M
{
74
7.13M
    bool valid = false;
75
7.13M
    size_t colon = in.find_last_of(':');
76
    // if a : is found, and it either follows a [...], or no other : is in the string, treat it as port separator
77
7.13M
    bool fHaveColon = colon != in.npos;
78
7.13M
    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
  Branch (78:23): [True: 44.3k, False: 7.08M]
  Branch (78:38): [True: 0, False: 44.3k]
  Branch (78:54): [True: 0, False: 0]
79
7.13M
    bool fMultiColon{fHaveColon && colon != 0 && (in.find_last_of(':', colon - 1) != in.npos)};
  Branch (79:22): [True: 44.3k, False: 7.08M]
  Branch (79:36): [True: 44.3k, False: 0]
  Branch (79:50): [True: 0, False: 44.3k]
80
7.13M
    if (fHaveColon && (colon == 0 || fBracketed || !fMultiColon)) {
  Branch (80:9): [True: 44.3k, False: 7.08M]
  Branch (80:24): [True: 0, False: 44.3k]
  Branch (80:38): [True: 0, False: 44.3k]
  Branch (80:52): [True: 44.3k, False: 0]
81
44.3k
        if (const auto n{ToIntegral<uint16_t>(in.substr(colon + 1))}) {
  Branch (81:24): [True: 44.3k, False: 0]
82
44.3k
            in = in.substr(0, colon);
83
44.3k
            portOut = *n;
84
44.3k
            valid = (portOut != 0);
85
44.3k
        }
86
7.08M
    } else {
87
7.08M
        valid = true;
88
7.08M
    }
89
7.13M
    if (in.size() > 0 && in[0] == '[' && in[in.size() - 1] == ']') {
  Branch (89:9): [True: 7.13M, False: 0]
  Branch (89:26): [True: 0, False: 7.13M]
  Branch (89:42): [True: 0, False: 0]
90
0
        hostOut = in.substr(1, in.size() - 2);
91
7.13M
    } else {
92
7.13M
        hostOut = in;
93
7.13M
    }
94
95
7.13M
    return valid;
96
7.13M
}
97
98
std::string EncodeBase64(std::span<const unsigned char> input)
99
0
{
100
0
    static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
101
102
0
    std::string str;
103
0
    str.reserve(((input.size() + 2) / 3) * 4);
104
0
    ConvertBits<8, 6, true>([&](int v) { str += pbase64[v]; }, input.begin(), input.end());
105
0
    while (str.size() % 4) str += '=';
  Branch (105:12): [True: 0, False: 0]
106
0
    return str;
107
0
}
108
109
std::optional<std::vector<unsigned char>> DecodeBase64(std::string_view str)
110
2.35M
{
111
2.35M
    static const int8_t decode64_table[256]{
112
2.35M
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
113
2.35M
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
114
2.35M
        -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1,
115
2.35M
        -1, -1, -1, -1, -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
116
2.35M
        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
117
2.35M
        29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
118
2.35M
        49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
119
2.35M
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
120
2.35M
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
121
2.35M
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
122
2.35M
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
123
2.35M
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
124
2.35M
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
125
2.35M
    };
126
127
2.35M
    if (str.size() % 4 != 0) return {};
  Branch (127:9): [True: 0, False: 2.35M]
128
    /* One or two = characters at the end are permitted. */
129
2.35M
    if (str.size() >= 1 && str.back() == '=') str.remove_suffix(1);
  Branch (129:9): [True: 2.35M, False: 0]
  Branch (129:28): [True: 0, False: 2.35M]
130
2.35M
    if (str.size() >= 1 && str.back() == '=') str.remove_suffix(1);
  Branch (130:9): [True: 2.35M, False: 0]
  Branch (130:28): [True: 0, False: 2.35M]
131
132
2.35M
    std::vector<unsigned char> ret;
133
2.35M
    ret.reserve((str.size() * 3) / 4);
134
2.35M
    bool valid = ConvertBits<6, 8, false>(
135
176M
        [&](unsigned char c) { ret.push_back(c); },
136
2.35M
        str.begin(), str.end(),
137
235M
        [](char c) { return decode64_table[uint8_t(c)]; }
138
2.35M
    );
139
2.35M
    if (!valid) return {};
  Branch (139:9): [True: 0, False: 2.35M]
140
141
2.35M
    return ret;
142
2.35M
}
143
144
std::string EncodeBase32(std::span<const unsigned char> input, bool pad)
145
0
{
146
0
    static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567";
147
148
0
    std::string str;
149
0
    str.reserve(((input.size() + 4) / 5) * 8);
150
0
    ConvertBits<8, 5, true>([&](int v) { str += pbase32[v]; }, input.begin(), input.end());
151
0
    if (pad) {
  Branch (151:9): [True: 0, False: 0]
152
0
        while (str.size() % 8) {
  Branch (152:16): [True: 0, False: 0]
153
0
            str += '=';
154
0
        }
155
0
    }
156
0
    return str;
157
0
}
158
159
std::string EncodeBase32(std::string_view str, bool pad)
160
0
{
161
0
    return EncodeBase32(MakeUCharSpan(str), pad);
162
0
}
163
164
std::optional<std::vector<unsigned char>> DecodeBase32(std::string_view str)
165
0
{
166
0
    static const int8_t decode32_table[256]{
167
0
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
168
0
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
169
0
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1,
170
0
        -1, -1, -1, -1, -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
171
0
        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1,  0,  1,  2,
172
0
         3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
173
0
        23, 24, 25, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
174
0
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
175
0
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
176
0
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
177
0
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
178
0
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
179
0
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
180
0
    };
181
182
0
    if (str.size() % 8 != 0) return {};
  Branch (182:9): [True: 0, False: 0]
183
    /* 1, 3, 4, or 6 padding '=' suffix characters are permitted. */
184
0
    if (str.size() >= 1 && str.back() == '=') str.remove_suffix(1);
  Branch (184:9): [True: 0, False: 0]
  Branch (184:28): [True: 0, False: 0]
185
0
    if (str.size() >= 2 && str.substr(str.size() - 2) == "==") str.remove_suffix(2);
  Branch (185:9): [True: 0, False: 0]
  Branch (185:28): [True: 0, False: 0]
186
0
    if (str.size() >= 1 && str.back() == '=') str.remove_suffix(1);
  Branch (186:9): [True: 0, False: 0]
  Branch (186:28): [True: 0, False: 0]
187
0
    if (str.size() >= 2 && str.substr(str.size() - 2) == "==") str.remove_suffix(2);
  Branch (187:9): [True: 0, False: 0]
  Branch (187:28): [True: 0, False: 0]
188
189
0
    std::vector<unsigned char> ret;
190
0
    ret.reserve((str.size() * 5) / 8);
191
0
    bool valid = ConvertBits<5, 8, false>(
192
0
        [&](unsigned char c) { ret.push_back(c); },
193
0
        str.begin(), str.end(),
194
0
        [](char c) { return decode32_table[uint8_t(c)]; }
195
0
    );
196
197
0
    if (!valid) return {};
  Branch (197:9): [True: 0, False: 0]
198
199
0
    return ret;
200
0
}
201
202
std::string FormatParagraph(std::string_view in, size_t width, size_t indent)
203
0
{
204
0
    assert(width >= indent);
  Branch (204:5): [True: 0, False: 0]
205
0
    std::stringstream out;
206
0
    size_t ptr = 0;
207
0
    size_t indented = 0;
208
0
    while (ptr < in.size())
  Branch (208:12): [True: 0, False: 0]
209
0
    {
210
0
        size_t lineend = in.find_first_of('\n', ptr);
211
0
        if (lineend == std::string::npos) {
  Branch (211:13): [True: 0, False: 0]
212
0
            lineend = in.size();
213
0
        }
214
0
        const size_t linelen = lineend - ptr;
215
0
        const size_t rem_width = width - indented;
216
0
        if (linelen <= rem_width) {
  Branch (216:13): [True: 0, False: 0]
217
0
            out << in.substr(ptr, linelen + 1);
218
0
            ptr = lineend + 1;
219
0
            indented = 0;
220
0
        } else {
221
0
            size_t finalspace = in.find_last_of(" \n", ptr + rem_width);
222
0
            if (finalspace == std::string::npos || finalspace < ptr) {
  Branch (222:17): [True: 0, False: 0]
  Branch (222:52): [True: 0, False: 0]
223
                // No place to break; just include the entire word and move on
224
0
                finalspace = in.find_first_of("\n ", ptr);
225
0
                if (finalspace == std::string::npos) {
  Branch (225:21): [True: 0, False: 0]
226
                    // End of the string, just add it and break
227
0
                    out << in.substr(ptr);
228
0
                    break;
229
0
                }
230
0
            }
231
0
            out << in.substr(ptr, finalspace - ptr) << "\n";
232
0
            if (in[finalspace] == '\n') {
  Branch (232:17): [True: 0, False: 0]
233
0
                indented = 0;
234
0
            } else if (indent) {
  Branch (234:24): [True: 0, False: 0]
235
0
                out << std::string(indent, ' ');
236
0
                indented = indent;
237
0
            }
238
0
            ptr = finalspace + 1;
239
0
        }
240
0
    }
241
0
    return out.str();
242
0
}
243
244
/** Upper bound for mantissa.
245
 * 10^18-1 is the largest arbitrary decimal that will fit in a signed 64-bit integer.
246
 * Larger integers cannot consist of arbitrary combinations of 0-9:
247
 *
248
 *   999999999999999999  1^18-1
249
 *  9223372036854775807  (1<<63)-1  (max int64_t)
250
 *  9999999999999999999  1^19-1     (would overflow)
251
 */
252
static const int64_t UPPER_BOUND = 1000000000000000000LL - 1LL;
253
254
/** Helper function for ParseFixedPoint */
255
static inline bool ProcessMantissaDigit(char ch, int64_t &mantissa, int &mantissa_tzeros)
256
0
{
257
0
    if(ch == '0')
  Branch (257:8): [True: 0, False: 0]
258
0
        ++mantissa_tzeros;
259
0
    else {
260
0
        for (int i=0; i<=mantissa_tzeros; ++i) {
  Branch (260:23): [True: 0, False: 0]
261
0
            if (mantissa > (UPPER_BOUND / 10LL))
  Branch (261:17): [True: 0, False: 0]
262
0
                return false; /* overflow */
263
0
            mantissa *= 10;
264
0
        }
265
0
        mantissa += ch - '0';
266
0
        mantissa_tzeros = 0;
267
0
    }
268
0
    return true;
269
0
}
270
271
bool ParseFixedPoint(std::string_view val, int decimals, int64_t *amount_out)
272
0
{
273
0
    int64_t mantissa = 0;
274
0
    int64_t exponent = 0;
275
0
    int mantissa_tzeros = 0;
276
0
    bool mantissa_sign = false;
277
0
    bool exponent_sign = false;
278
0
    int ptr = 0;
279
0
    int end = val.size();
280
0
    int point_ofs = 0;
281
282
0
    if (ptr < end && val[ptr] == '-') {
  Branch (282:9): [True: 0, False: 0]
  Branch (282:22): [True: 0, False: 0]
283
0
        mantissa_sign = true;
284
0
        ++ptr;
285
0
    }
286
0
    if (ptr < end)
  Branch (286:9): [True: 0, False: 0]
287
0
    {
288
0
        if (val[ptr] == '0') {
  Branch (288:13): [True: 0, False: 0]
289
            /* pass single 0 */
290
0
            ++ptr;
291
0
        } else if (val[ptr] >= '1' && val[ptr] <= '9') {
  Branch (291:20): [True: 0, False: 0]
  Branch (291:39): [True: 0, False: 0]
292
0
            while (ptr < end && IsDigit(val[ptr])) {
  Branch (292:20): [True: 0, False: 0]
  Branch (292:33): [True: 0, False: 0]
293
0
                if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros))
  Branch (293:21): [True: 0, False: 0]
294
0
                    return false; /* overflow */
295
0
                ++ptr;
296
0
            }
297
0
        } else return false; /* missing expected digit */
298
0
    } else return false; /* empty string or loose '-' */
299
0
    if (ptr < end && val[ptr] == '.')
  Branch (299:9): [True: 0, False: 0]
  Branch (299:22): [True: 0, False: 0]
300
0
    {
301
0
        ++ptr;
302
0
        if (ptr < end && IsDigit(val[ptr]))
  Branch (302:13): [True: 0, False: 0]
  Branch (302:26): [True: 0, False: 0]
303
0
        {
304
0
            while (ptr < end && IsDigit(val[ptr])) {
  Branch (304:20): [True: 0, False: 0]
  Branch (304:33): [True: 0, False: 0]
305
0
                if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros))
  Branch (305:21): [True: 0, False: 0]
306
0
                    return false; /* overflow */
307
0
                ++ptr;
308
0
                ++point_ofs;
309
0
            }
310
0
        } else return false; /* missing expected digit */
311
0
    }
312
0
    if (ptr < end && (val[ptr] == 'e' || val[ptr] == 'E'))
  Branch (312:9): [True: 0, False: 0]
  Branch (312:23): [True: 0, False: 0]
  Branch (312:42): [True: 0, False: 0]
313
0
    {
314
0
        ++ptr;
315
0
        if (ptr < end && val[ptr] == '+')
  Branch (315:13): [True: 0, False: 0]
  Branch (315:26): [True: 0, False: 0]
316
0
            ++ptr;
317
0
        else if (ptr < end && val[ptr] == '-') {
  Branch (317:18): [True: 0, False: 0]
  Branch (317:31): [True: 0, False: 0]
318
0
            exponent_sign = true;
319
0
            ++ptr;
320
0
        }
321
0
        if (ptr < end && IsDigit(val[ptr])) {
  Branch (321:13): [True: 0, False: 0]
  Branch (321:26): [True: 0, False: 0]
322
0
            while (ptr < end && IsDigit(val[ptr])) {
  Branch (322:20): [True: 0, False: 0]
  Branch (322:33): [True: 0, False: 0]
323
0
                if (exponent > (UPPER_BOUND / 10LL))
  Branch (323:21): [True: 0, False: 0]
324
0
                    return false; /* overflow */
325
0
                exponent = exponent * 10 + val[ptr] - '0';
326
0
                ++ptr;
327
0
            }
328
0
        } else return false; /* missing expected digit */
329
0
    }
330
0
    if (ptr != end)
  Branch (330:9): [True: 0, False: 0]
331
0
        return false; /* trailing garbage */
332
333
    /* finalize exponent */
334
0
    if (exponent_sign)
  Branch (334:9): [True: 0, False: 0]
335
0
        exponent = -exponent;
336
0
    exponent = exponent - point_ofs + mantissa_tzeros;
337
338
    /* finalize mantissa */
339
0
    if (mantissa_sign)
  Branch (339:9): [True: 0, False: 0]
340
0
        mantissa = -mantissa;
341
342
    /* convert to one 64-bit fixed-point value */
343
0
    exponent += decimals;
344
0
    if (exponent < 0)
  Branch (344:9): [True: 0, False: 0]
345
0
        return false; /* cannot represent values smaller than 10^-decimals */
346
0
    if (exponent >= 18)
  Branch (346:9): [True: 0, False: 0]
347
0
        return false; /* cannot represent values larger than or equal to 10^(18-decimals) */
348
349
0
    for (int i=0; i < exponent; ++i) {
  Branch (349:19): [True: 0, False: 0]
350
0
        if (mantissa > (UPPER_BOUND / 10LL) || mantissa < -(UPPER_BOUND / 10LL))
  Branch (350:13): [True: 0, False: 0]
  Branch (350:48): [True: 0, False: 0]
351
0
            return false; /* overflow */
352
0
        mantissa *= 10;
353
0
    }
354
0
    if (mantissa > UPPER_BOUND || mantissa < -UPPER_BOUND)
  Branch (354:9): [True: 0, False: 0]
  Branch (354:35): [True: 0, False: 0]
355
0
        return false; /* overflow */
356
357
0
    if (amount_out)
  Branch (357:9): [True: 0, False: 0]
358
0
        *amount_out = mantissa;
359
360
0
    return true;
361
0
}
362
363
std::string ToLower(std::string_view str)
364
0
{
365
0
    std::string r;
366
0
    r.reserve(str.size());
367
0
    for (auto ch : str) r += ToLower(ch);
  Branch (367:18): [True: 0, False: 0]
368
0
    return r;
369
0
}
370
371
std::string ToUpper(std::string_view str)
372
0
{
373
0
    std::string r;
374
0
    r.reserve(str.size());
375
0
    for (auto ch : str) r += ToUpper(ch);
  Branch (375:18): [True: 0, False: 0]
376
0
    return r;
377
0
}
378
379
std::string Capitalize(std::string str)
380
0
{
381
0
    if (str.empty()) return str;
  Branch (381:9): [True: 0, False: 0]
382
0
    str[0] = ToUpper(str.front());
383
0
    return str;
384
0
}
385
386
std::optional<uint64_t> ParseByteUnits(std::string_view str, ByteUnit default_multiplier)
387
11.0k
{
388
11.0k
    if (str.empty()) {
  Branch (388:9): [True: 0, False: 11.0k]
389
0
        return std::nullopt;
390
0
    }
391
11.0k
    auto multiplier = default_multiplier;
392
11.0k
    char unit = str.back();
393
11.0k
    switch (unit) {
394
0
    case 'k':
  Branch (394:5): [True: 0, False: 11.0k]
395
0
        multiplier = ByteUnit::k;
396
0
        break;
397
0
    case 'K':
  Branch (397:5): [True: 0, False: 11.0k]
398
0
        multiplier = ByteUnit::K;
399
0
        break;
400
0
    case 'm':
  Branch (400:5): [True: 0, False: 11.0k]
401
0
        multiplier = ByteUnit::m;
402
0
        break;
403
11.0k
    case 'M':
  Branch (403:5): [True: 11.0k, False: 0]
404
11.0k
        multiplier = ByteUnit::M;
405
11.0k
        break;
406
0
    case 'g':
  Branch (406:5): [True: 0, False: 11.0k]
407
0
        multiplier = ByteUnit::g;
408
0
        break;
409
0
    case 'G':
  Branch (409:5): [True: 0, False: 11.0k]
410
0
        multiplier = ByteUnit::G;
411
0
        break;
412
0
    case 't':
  Branch (412:5): [True: 0, False: 11.0k]
413
0
        multiplier = ByteUnit::t;
414
0
        break;
415
0
    case 'T':
  Branch (415:5): [True: 0, False: 11.0k]
416
0
        multiplier = ByteUnit::T;
417
0
        break;
418
0
    default:
  Branch (418:5): [True: 0, False: 11.0k]
419
0
        unit = 0;
420
0
        break;
421
11.0k
    }
422
423
11.0k
    uint64_t unit_amount = static_cast<uint64_t>(multiplier);
424
11.0k
    auto parsed_num = ToIntegral<uint64_t>(unit ? str.substr(0, str.size() - 1) : str);
  Branch (424:44): [True: 11.0k, False: 0]
425
11.0k
    if (!parsed_num || parsed_num > std::numeric_limits<uint64_t>::max() / unit_amount) { // check overflow
  Branch (425:9): [True: 0, False: 11.0k]
  Branch (425:9): [True: 0, False: 11.0k]
  Branch (425:24): [True: 0, False: 11.0k]
426
0
        return std::nullopt;
427
0
    }
428
11.0k
    return *parsed_num * unit_amount;
429
11.0k
}