Coverage Report

Created: 2026-06-16 16:41

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