Branch data Line data Source code
1 : : // Copyright (c) 2009-2010 Satoshi Nakamoto
2 : : // Copyright (c) 2009-2022 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 : 49171 : std::string SanitizeString(std::string_view str, int rule)
31 : : {
32 : 49171 : std::string result;
33 [ + + ]: 442049 : for (char c : str) {
34 [ + + ]: 392878 : if (SAFE_CHARS[rule].find(c) != std::string::npos) {
35 [ + - ]: 168243 : result.push_back(c);
36 : : }
37 : : }
38 : 49171 : return result;
39 : 0 : }
40 : :
41 : 234367 : bool IsHex(std::string_view str)
42 : : {
43 [ + + ]: 421030218 : for (char c : str) {
44 [ + + ]: 420890266 : if (HexDigit(c) < 0) return false;
45 : : }
46 [ + + + + ]: 139952 : return (str.size() > 0) && (str.size()%2 == 0);
47 : : }
48 : :
49 : : template <typename Byte>
50 : 751179 : std::optional<std::vector<Byte>> TryParseHex(std::string_view str)
51 : : {
52 [ + - ]: 751179 : std::vector<Byte> vch;
53 [ + - ]: 751179 : vch.reserve(str.size() / 2); // two hex characters form a single byte
54 : :
55 : 751179 : auto it = str.begin();
56 : 751179 : while (it != str.end()) {
57 [ + + ]: 206490667 : if (IsSpace(*it)) {
58 : 6376 : ++it;
59 : 6376 : continue;
60 : : }
61 [ + - ]: 206484291 : auto c1 = HexDigit(*(it++));
62 [ + + ]: 206484291 : if (it == str.end()) return std::nullopt;
63 [ + - ]: 206484264 : auto c2 = HexDigit(*(it++));
64 [ + + ]: 206484264 : if (c1 < 0 || c2 < 0) return std::nullopt;
65 [ + - + + ]: 413724893 : vch.push_back(Byte(c1 << 4) | Byte(c2));
66 : : }
67 : 750557 : return vch;
68 : 751179 : }
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 : 1056938 : bool SplitHostPort(std::string_view in, uint16_t& portOut, std::string& hostOut)
73 : : {
74 : 1056938 : bool valid = false;
75 [ + + ]: 1056938 : 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 : 1056938 : bool fHaveColon = colon != in.npos;
78 [ + + + + : 1056938 : 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
+ + ]
79 [ + + + + ]: 1056938 : bool fMultiColon{fHaveColon && colon != 0 && (in.find_last_of(':', colon - 1) != in.npos)};
80 [ + + + + : 1056938 : if (fHaveColon && (colon == 0 || fBracketed || !fMultiColon)) {
+ + ]
81 : 235799 : uint16_t n;
82 [ + + ]: 235799 : if (ParseUInt16(in.substr(colon + 1), &n)) {
83 : 106930 : in = in.substr(0, colon);
84 : 106930 : portOut = n;
85 : 106930 : valid = (portOut != 0);
86 : : }
87 : : } else {
88 : : valid = true;
89 : : }
90 [ + + + + : 1056938 : if (in.size() > 0 && in[0] == '[' && in[in.size() - 1] == ']') {
+ + ]
91 : 6041 : hostOut = in.substr(1, in.size() - 2);
92 : : } else {
93 : 1050897 : hostOut = in;
94 : : }
95 : :
96 : 1056938 : return valid;
97 : : }
98 : :
99 : 62371 : std::string EncodeBase64(Span<const unsigned char> input)
100 : : {
101 : 62371 : static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
102 : :
103 [ + - ]: 62371 : std::string str;
104 [ + - ]: 62371 : str.reserve(((input.size() + 2) / 3) * 4);
105 [ + - ]: 110129369 : ConvertBits<8, 6, true>([&](int v) { str += pbase64[v]; }, input.begin(), input.end());
106 [ + - + + ]: 128013 : while (str.size() % 4) str += '=';
107 : 62371 : return str;
108 : 0 : }
109 : :
110 : 28048 : std::optional<std::vector<unsigned char>> DecodeBase64(std::string_view str)
111 : : {
112 : 28048 : static const int8_t decode64_table[256]{
113 : : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
114 : : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
115 : : -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1,
116 : : -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
117 : : 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
118 : : 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
119 : : 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
120 : : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
121 : : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
122 : : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
123 : : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
124 : : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
125 : : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
126 : : };
127 : :
128 [ + + ]: 28048 : if (str.size() % 4 != 0) return {};
129 : : /* One or two = characters at the end are permitted. */
130 [ + + + + ]: 27785 : if (str.size() >= 1 && str.back() == '=') str.remove_suffix(1);
131 [ + + + + ]: 27785 : if (str.size() >= 1 && str.back() == '=') str.remove_suffix(1);
132 : :
133 : 27785 : std::vector<unsigned char> ret;
134 [ + - ]: 27785 : ret.reserve((str.size() * 3) / 4);
135 [ + - ]: 27785 : bool valid = ConvertBits<6, 8, false>(
136 : 81361076 : [&](unsigned char c) { ret.push_back(c); },
137 : : str.begin(), str.end(),
138 [ + + ]: 108491022 : [](char c) { return decode64_table[uint8_t(c)]; }
139 : : );
140 [ + + ]: 27785 : if (!valid) return {};
141 : :
142 : 27651 : return ret;
143 : 27785 : }
144 : :
145 : 341228 : std::string EncodeBase32(Span<const unsigned char> input, bool pad)
146 : : {
147 : 341228 : static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567";
148 : :
149 [ + - ]: 341228 : std::string str;
150 [ + - ]: 341228 : str.reserve(((input.size() + 4) / 5) * 8);
151 [ + - ]: 23605950 : ConvertBits<8, 5, true>([&](int v) { str += pbase32[v]; }, input.begin(), input.end());
152 [ + + ]: 341228 : if (pad) {
153 : 254870 : while (str.size() % 8) {
154 [ + - + + ]: 254870 : str += '=';
155 : : }
156 : : }
157 : 341228 : return str;
158 : 0 : }
159 : :
160 : 7600 : std::string EncodeBase32(std::string_view str, bool pad)
161 : : {
162 : 7600 : return EncodeBase32(MakeUCharSpan(str), pad);
163 : : }
164 : :
165 : 142096 : std::optional<std::vector<unsigned char>> DecodeBase32(std::string_view str)
166 : : {
167 : 142096 : static const int8_t decode32_table[256]{
168 : : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
169 : : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
170 : : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1,
171 : : -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
172 : : 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 0, 1, 2,
173 : : 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
174 : : 23, 24, 25, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
175 : : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
176 : : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
177 : : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
178 : : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
179 : : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
180 : : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
181 : : };
182 : :
183 [ + + ]: 142096 : if (str.size() % 8 != 0) return {};
184 : : /* 1, 3, 4, or 6 padding '=' suffix characters are permitted. */
185 [ + - + + ]: 102944 : if (str.size() >= 1 && str.back() == '=') str.remove_suffix(1);
186 [ + - + + ]: 102944 : if (str.size() >= 2 && str.substr(str.size() - 2) == "==") str.remove_suffix(2);
187 [ + - + + ]: 102944 : if (str.size() >= 1 && str.back() == '=') str.remove_suffix(1);
188 [ + - + + ]: 102944 : if (str.size() >= 2 && str.substr(str.size() - 2) == "==") str.remove_suffix(2);
189 : :
190 : 102944 : std::vector<unsigned char> ret;
191 [ + - ]: 102944 : ret.reserve((str.size() * 5) / 8);
192 [ + - ]: 102944 : bool valid = ConvertBits<5, 8, false>(
193 : 2648909 : [&](unsigned char c) { ret.push_back(c); },
194 : : str.begin(), str.end(),
195 [ + + ]: 4328859 : [](char c) { return decode32_table[uint8_t(c)]; }
196 : : );
197 : :
198 [ + + ]: 102944 : if (!valid) return {};
199 : :
200 : 72266 : return ret;
201 : 102944 : }
202 : :
203 : : namespace {
204 : : template <typename T>
205 [ + + ]: 894869 : bool ParseIntegral(std::string_view str, T* out)
206 : : {
207 : : static_assert(std::is_integral<T>::value);
208 : : // Replicate the exact behavior of strtol/strtoll/strtoul/strtoull when
209 : : // handling leading +/- for backwards compatibility.
210 [ + + + + : 894869 : if (str.length() >= 2 && str[0] == '+' && str[1] == '-') {
+ + ]
211 : : return false;
212 : : }
213 [ + + + + : 883120 : const std::optional<T> opt_int = ToIntegral<T>((!str.empty() && str[0] == '+') ? str.substr(1) : str);
+ + ]
214 [ + + ]: 883120 : if (!opt_int) {
215 : : return false;
216 : : }
217 [ + - ]: 761310 : if (out != nullptr) {
218 : 761310 : *out = *opt_int;
219 : : }
220 : : return true;
221 : : }
222 : : }; // namespace
223 : :
224 : 3417 : bool ParseInt32(std::string_view str, int32_t* out)
225 : : {
226 : 3417 : return ParseIntegral<int32_t>(str, out);
227 : : }
228 : :
229 : 280 : bool ParseInt64(std::string_view str, int64_t* out)
230 : : {
231 : 280 : return ParseIntegral<int64_t>(str, out);
232 : : }
233 : :
234 : 27656 : bool ParseUInt8(std::string_view str, uint8_t* out)
235 : : {
236 : 27656 : return ParseIntegral<uint8_t>(str, out);
237 : : }
238 : :
239 : 235989 : bool ParseUInt16(std::string_view str, uint16_t* out)
240 : : {
241 : 235989 : return ParseIntegral<uint16_t>(str, out);
242 : : }
243 : :
244 : 627337 : bool ParseUInt32(std::string_view str, uint32_t* out)
245 : : {
246 : 627337 : return ParseIntegral<uint32_t>(str, out);
247 : : }
248 : :
249 : 190 : bool ParseUInt64(std::string_view str, uint64_t* out)
250 : : {
251 : 190 : return ParseIntegral<uint64_t>(str, out);
252 : : }
253 : :
254 : 4635 : std::string FormatParagraph(std::string_view in, size_t width, size_t indent)
255 : : {
256 [ - + ]: 4635 : assert(width >= indent);
257 : 4635 : std::stringstream out;
258 : 4635 : size_t ptr = 0;
259 : 4635 : size_t indented = 0;
260 [ + + ]: 13092 : while (ptr < in.size())
261 : : {
262 [ + + ]: 4606 : size_t lineend = in.find_first_of('\n', ptr);
263 [ + + ]: 4606 : if (lineend == std::string::npos) {
264 : 3881 : lineend = in.size();
265 : : }
266 : 4606 : const size_t linelen = lineend - ptr;
267 : 4606 : const size_t rem_width = width - indented;
268 [ + + ]: 4606 : if (linelen <= rem_width) {
269 [ + - ]: 3374 : out << in.substr(ptr, linelen + 1);
270 : 3374 : ptr = lineend + 1;
271 : 3374 : indented = 0;
272 : : } else {
273 : 1232 : size_t finalspace = in.find_last_of(" \n", ptr + rem_width);
274 [ + + ]: 1232 : if (finalspace == std::string::npos || finalspace < ptr) {
275 : : // No place to break; just include the entire word and move on
276 : 937 : finalspace = in.find_first_of("\n ", ptr);
277 [ + + ]: 937 : if (finalspace == std::string::npos) {
278 : : // End of the string, just add it and break
279 [ + - ]: 784 : out << in.substr(ptr);
280 : : break;
281 : : }
282 : : }
283 [ + - + - ]: 896 : out << in.substr(ptr, finalspace - ptr) << "\n";
284 [ + + ]: 448 : if (in[finalspace] == '\n') {
285 : : indented = 0;
286 [ + + ]: 381 : } else if (indent) {
287 [ + - + - ]: 288 : out << std::string(indent, ' ');
288 : 288 : indented = indent;
289 : : }
290 : 448 : ptr = finalspace + 1;
291 : : }
292 : : }
293 [ + - ]: 9270 : return out.str();
294 : 4635 : }
295 : :
296 : : /** Upper bound for mantissa.
297 : : * 10^18-1 is the largest arbitrary decimal that will fit in a signed 64-bit integer.
298 : : * Larger integers cannot consist of arbitrary combinations of 0-9:
299 : : *
300 : : * 999999999999999999 1^18-1
301 : : * 9223372036854775807 (1<<63)-1 (max int64_t)
302 : : * 9999999999999999999 1^19-1 (would overflow)
303 : : */
304 : : static const int64_t UPPER_BOUND = 1000000000000000000LL - 1LL;
305 : :
306 : : /** Helper function for ParseFixedPoint */
307 : 1755261 : static inline bool ProcessMantissaDigit(char ch, int64_t &mantissa, int &mantissa_tzeros)
308 : : {
309 [ + + ]: 1755261 : if(ch == '0')
310 : 1752574 : ++mantissa_tzeros;
311 : : else {
312 [ + + ]: 97792 : for (int i=0; i<=mantissa_tzeros; ++i) {
313 [ + + ]: 95166 : if (mantissa > (UPPER_BOUND / 10LL))
314 : : return false; /* overflow */
315 : 95105 : mantissa *= 10;
316 : : }
317 : 2626 : mantissa += ch - '0';
318 : 2626 : mantissa_tzeros = 0;
319 : : }
320 : : return true;
321 : : }
322 : :
323 : 3982 : bool ParseFixedPoint(std::string_view val, int decimals, int64_t *amount_out)
324 : : {
325 : 3982 : int64_t mantissa = 0;
326 : 3982 : int64_t exponent = 0;
327 : 3982 : int mantissa_tzeros = 0;
328 : 3982 : bool mantissa_sign = false;
329 : 3982 : bool exponent_sign = false;
330 : 3982 : int ptr = 0;
331 [ + + ]: 3982 : int end = val.size();
332 : 3982 : int point_ofs = 0;
333 : :
334 [ + + + + ]: 3982 : if (ptr < end && val[ptr] == '-') {
335 : : mantissa_sign = true;
336 : : ++ptr;
337 : : }
338 [ + + ]: 3982 : if (ptr < end)
339 : : {
340 [ + + ]: 3947 : if (val[ptr] == '0') {
341 : : /* pass single 0 */
342 : 668 : ++ptr;
343 [ + + + + ]: 3279 : } else if (val[ptr] >= '1' && val[ptr] <= '9') {
344 [ + + + + ]: 1371418 : while (ptr < end && IsDigit(val[ptr])) {
345 [ + + ]: 1371143 : if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros))
346 : : return false; /* overflow */
347 : 1371105 : ++ptr;
348 : : }
349 : : } else return false; /* missing expected digit */
350 : : } else return false; /* empty string or loose '-' */
351 [ + + + + ]: 943 : if (ptr < end && val[ptr] == '.')
352 : : {
353 : 739 : ++ptr;
354 [ + + + + ]: 739 : if (ptr < end && IsDigit(val[ptr]))
355 : : {
356 [ + + + + ]: 384814 : while (ptr < end && IsDigit(val[ptr])) {
357 [ + + ]: 384118 : if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros))
358 : : return false; /* overflow */
359 : 384095 : ++ptr;
360 : 384095 : ++point_ofs;
361 : : }
362 : : } else return false; /* missing expected digit */
363 : : }
364 [ + + + + : 900 : if (ptr < end && (val[ptr] == 'e' || val[ptr] == 'E'))
+ + ]
365 : : {
366 : 140 : ++ptr;
367 [ + + + + ]: 140 : if (ptr < end && val[ptr] == '+')
368 : 6 : ++ptr;
369 [ + + + + ]: 134 : else if (ptr < end && val[ptr] == '-') {
370 : 23 : exponent_sign = true;
371 : 23 : ++ptr;
372 : : }
373 [ + + + + ]: 140 : if (ptr < end && IsDigit(val[ptr])) {
374 [ + + + + ]: 55628 : while (ptr < end && IsDigit(val[ptr])) {
375 [ + + ]: 55524 : if (exponent > (UPPER_BOUND / 10LL))
376 : : return false; /* overflow */
377 : 55513 : exponent = exponent * 10 + val[ptr] - '0';
378 : 55513 : ++ptr;
379 : : }
380 : : } else return false; /* missing expected digit */
381 : : }
382 [ + + ]: 864 : if (ptr != end)
383 : : return false; /* trailing garbage */
384 : :
385 : : /* finalize exponent */
386 [ + + ]: 782 : if (exponent_sign)
387 : 17 : exponent = -exponent;
388 : 782 : exponent = exponent - point_ofs + mantissa_tzeros;
389 : :
390 : : /* finalize mantissa */
391 [ + + ]: 782 : if (mantissa_sign)
392 : 37 : mantissa = -mantissa;
393 : :
394 : : /* convert to one 64-bit fixed-point value */
395 : 782 : exponent += decimals;
396 [ + + ]: 782 : if (exponent < 0)
397 : : return false; /* cannot represent values smaller than 10^-decimals */
398 [ + + ]: 745 : if (exponent >= 18)
399 : : return false; /* cannot represent values larger than or equal to 10^(18-decimals) */
400 : :
401 [ + + ]: 5476 : for (int i=0; i < exponent; ++i) {
402 [ + + ]: 4805 : if (mantissa > (UPPER_BOUND / 10LL) || mantissa < -(UPPER_BOUND / 10LL))
403 : : return false; /* overflow */
404 : 4765 : mantissa *= 10;
405 : : }
406 [ + - ]: 671 : if (mantissa > UPPER_BOUND || mantissa < -UPPER_BOUND)
407 : : return false; /* overflow */
408 : :
409 [ + - ]: 671 : if (amount_out)
410 : 671 : *amount_out = mantissa;
411 : :
412 : : return true;
413 : : }
414 : :
415 : 119522 : std::string ToLower(std::string_view str)
416 : : {
417 [ + - ]: 119522 : std::string r;
418 [ + - ]: 119522 : r.reserve(str.size());
419 [ + + + - : 200721717 : for (auto ch : str) r += ToLower(ch);
+ + ]
420 : 119522 : return r;
421 : 0 : }
422 : :
423 : 4782 : std::string ToUpper(std::string_view str)
424 : : {
425 [ + - ]: 4782 : std::string r;
426 [ + - ]: 4782 : r.reserve(str.size());
427 [ + + + - : 83976 : for (auto ch : str) r += ToUpper(ch);
+ + ]
428 : 4782 : return r;
429 : 0 : }
430 : :
431 : 1229 : std::string Capitalize(std::string str)
432 : : {
433 [ + + ]: 1229 : if (str.empty()) return str;
434 [ + + ]: 2087 : str[0] = ToUpper(str.front());
435 : 1198 : return str;
436 : : }
437 : :
438 : 0 : std::optional<uint64_t> ParseByteUnits(std::string_view str, ByteUnit default_multiplier)
439 : : {
440 [ # # ]: 0 : if (str.empty()) {
441 : 0 : return std::nullopt;
442 : : }
443 : 0 : auto multiplier = default_multiplier;
444 [ # # # # : 0 : char unit = str.back();
# # # #
# ]
445 [ # # # # : 0 : switch (unit) {
# # # #
# ]
446 : : case 'k':
447 : : multiplier = ByteUnit::k;
448 : : break;
449 : 0 : case 'K':
450 : 0 : multiplier = ByteUnit::K;
451 : 0 : break;
452 : 0 : case 'm':
453 : 0 : multiplier = ByteUnit::m;
454 : 0 : break;
455 : 0 : case 'M':
456 : 0 : multiplier = ByteUnit::M;
457 : 0 : break;
458 : 0 : case 'g':
459 : 0 : multiplier = ByteUnit::g;
460 : 0 : break;
461 : 0 : case 'G':
462 : 0 : multiplier = ByteUnit::G;
463 : 0 : break;
464 : 0 : case 't':
465 : 0 : multiplier = ByteUnit::t;
466 : 0 : break;
467 : 0 : case 'T':
468 : 0 : multiplier = ByteUnit::T;
469 : 0 : break;
470 : 0 : default:
471 : 0 : unit = 0;
472 : 0 : break;
473 : : }
474 : :
475 : 0 : uint64_t unit_amount = static_cast<uint64_t>(multiplier);
476 : 0 : auto parsed_num = ToIntegral<uint64_t>(unit ? str.substr(0, str.size() - 1) : str);
477 [ # # # # ]: 0 : if (!parsed_num || parsed_num > std::numeric_limits<uint64_t>::max() / unit_amount) { // check overflow
478 : 0 : return std::nullopt;
479 : : }
480 : 0 : return *parsed_num * unit_amount;
481 : : }
|