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 223 : static const std::string CHARS_ALPHA_NUM = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
19 :
20 0 : static const std::string SAFE_CHARS[] =
21 223 : {
22 223 : CHARS_ALPHA_NUM + " .,;-_/:?@()", // SAFE_CHARS_DEFAULT
23 223 : CHARS_ALPHA_NUM + " .,;-_?@", // SAFE_CHARS_UA_COMMENT
24 223 : CHARS_ALPHA_NUM + ".-_", // SAFE_CHARS_FILENAME
25 223 : CHARS_ALPHA_NUM + "!*'();:@&=+$,/?#[]-_.~%", // SAFE_CHARS_URI
26 : };
27 :
28 11 : std::string SanitizeString(std::string_view str, int rule)
29 : {
30 11 : std::string result;
31 147 : for (char c : str) {
32 136 : if (SAFE_CHARS[rule].find(c) != std::string::npos) {
33 88 : result.push_back(c);
34 88 : }
35 : }
36 11 : return result;
37 11 : }
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 12809449 : signed char HexDigit(char c)
58 : {
59 12809449 : return p_util_hexdigit[(unsigned char)c];
60 : }
61 :
62 2456 : bool IsHex(std::string_view str)
63 : {
64 211004 : for (char c : str) {
65 208611 : if (HexDigit(c) < 0) return false;
66 : }
67 2393 : return (str.size() > 0) && (str.size()%2 == 0);
68 2456 : }
69 :
70 38 : bool IsHexNumber(std::string_view str)
71 : {
72 38 : if (str.substr(0, 2) == "0x") str.remove_prefix(2);
73 252 : for (char c : str) {
74 228 : if (HexDigit(c) < 0) return false;
75 : }
76 : // Return false for empty string or "0x".
77 24 : return str.size() > 0;
78 38 : }
79 :
80 : template <typename Byte>
81 8169 : std::optional<std::vector<Byte>> TryParseHex(std::string_view str)
82 : {
83 8169 : std::vector<Byte> vch;
84 8169 : auto it = str.begin();
85 560487 : while (it != str.end()) {
86 552334 : if (IsSpace(*it)) {
87 116 : ++it;
88 116 : continue;
89 : }
90 552218 : auto c1 = HexDigit(*(it++));
91 552218 : if (it == str.end()) return std::nullopt;
92 552214 : auto c2 = HexDigit(*(it++));
93 552214 : if (c1 < 0 || c2 < 0) return std::nullopt;
94 552202 : vch.push_back(Byte(c1 << 4) | Byte(c2));
95 : }
96 8153 : return vch;
97 8169 : }
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 6990 : bool SplitHostPort(std::string_view in, uint16_t& portOut, std::string& hostOut)
102 : {
103 6990 : bool valid = false;
104 6990 : 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 6990 : bool fHaveColon = colon != in.npos;
107 7214 : 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 6990 : bool fMultiColon{fHaveColon && colon != 0 && (in.find_last_of(':', colon - 1) != in.npos)};
109 6990 : if (fHaveColon && (colon == 0 || fBracketed || !fMultiColon)) {
110 : uint16_t n;
111 211 : if (ParseUInt16(in.substr(colon + 1), &n)) {
112 195 : in = in.substr(0, colon);
113 195 : portOut = n;
114 195 : valid = (portOut != 0);
115 195 : }
116 211 : } else {
117 6779 : valid = true;
118 : }
119 6990 : if (in.size() > 0 && in[0] == '[' && in[in.size() - 1] == ']') {
120 21 : hostOut = in.substr(1, in.size() - 2);
121 21 : } else {
122 6969 : hostOut = in;
123 : }
124 :
125 6990 : return valid;
126 : }
127 :
128 23 : std::string EncodeBase64(Span<const unsigned char> input)
129 : {
130 : static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
131 :
132 23 : std::string str;
133 23 : str.reserve(((input.size() + 2) / 3) * 4);
134 6415 : ConvertBits<8, 6, true>([&](int v) { str += pbase64[v]; }, input.begin(), input.end());
135 47 : while (str.size() % 4) str += '=';
136 23 : return str;
137 23 : }
138 :
139 22 : 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 22 : if (str.size() % 4 != 0) return {};
158 : /* One or two = characters at the end are permitted. */
159 16 : if (str.size() >= 1 && str.back() == '=') str.remove_suffix(1);
160 16 : if (str.size() >= 1 && str.back() == '=') str.remove_suffix(1);
161 :
162 16 : std::vector<unsigned char> ret;
163 16 : ret.reserve((str.size() * 3) / 4);
164 16 : bool valid = ConvertBits<6, 8, false>(
165 996 : [&](unsigned char c) { ret.push_back(c); },
166 16 : str.begin(), str.end(),
167 1315 : [](char c) { return decode64_table[uint8_t(c)]; }
168 : );
169 16 : if (!valid) return {};
170 :
171 13 : return ret;
172 22 : }
173 :
174 68 : std::string EncodeBase32(Span<const unsigned char> input, bool pad)
175 : {
176 : static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567";
177 :
178 68 : std::string str;
179 68 : str.reserve(((input.size() + 4) / 5) * 8);
180 2972 : ConvertBits<8, 5, true>([&](int v) { str += pbase32[v]; }, input.begin(), input.end());
181 68 : if (pad) {
182 53 : while (str.size() % 8) {
183 20 : str += '=';
184 : }
185 33 : }
186 68 : return str;
187 68 : }
188 :
189 14 : std::string EncodeBase32(std::string_view str, bool pad)
190 : {
191 14 : return EncodeBase32(MakeUCharSpan(str), pad);
192 : }
193 :
194 57 : 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 57 : if (str.size() % 8 != 0) return {};
213 : /* 1, 3, 4, or 6 padding '=' suffix characters are permitted. */
214 56 : if (str.size() >= 1 && str.back() == '=') str.remove_suffix(1);
215 56 : if (str.size() >= 2 && str.substr(str.size() - 2) == "==") str.remove_suffix(2);
216 56 : if (str.size() >= 1 && str.back() == '=') str.remove_suffix(1);
217 56 : if (str.size() >= 2 && str.substr(str.size() - 2) == "==") str.remove_suffix(2);
218 :
219 56 : std::vector<unsigned char> ret;
220 56 : ret.reserve((str.size() * 5) / 8);
221 56 : bool valid = ConvertBits<5, 8, false>(
222 1519 : [&](unsigned char c) { ret.push_back(c); },
223 56 : str.begin(), str.end(),
224 2360 : [](char c) { return decode32_table[uint8_t(c)]; }
225 : );
226 :
227 56 : if (!valid) return {};
228 :
229 52 : return ret;
230 57 : }
231 :
232 : namespace {
233 : template <typename T>
234 1573 : 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 1573 : if (str.length() >= 2 && str[0] == '+' && str[1] == '-') {
240 8 : return false;
241 : }
242 1565 : const std::optional<T> opt_int = ToIntegral<T>((!str.empty() && str[0] == '+') ? str.substr(1) : str);
243 1565 : if (!opt_int) {
244 266 : return false;
245 : }
246 1299 : if (out != nullptr) {
247 1287 : *out = *opt_int;
248 1287 : }
249 1299 : return true;
250 1573 : }
251 : }; // namespace
252 :
253 122 : bool ParseInt32(std::string_view str, int32_t* out)
254 : {
255 122 : return ParseIntegral<int32_t>(str, out);
256 : }
257 :
258 350 : bool ParseInt64(std::string_view str, int64_t* out)
259 : {
260 350 : return ParseIntegral<int64_t>(str, out);
261 : }
262 :
263 132 : bool ParseUInt8(std::string_view str, uint8_t* out)
264 : {
265 132 : return ParseIntegral<uint8_t>(str, out);
266 : }
267 :
268 265 : bool ParseUInt16(std::string_view str, uint16_t* out)
269 : {
270 265 : return ParseIntegral<uint16_t>(str, out);
271 : }
272 :
273 660 : bool ParseUInt32(std::string_view str, uint32_t* out)
274 : {
275 660 : return ParseIntegral<uint32_t>(str, out);
276 : }
277 :
278 44 : bool ParseUInt64(std::string_view str, uint64_t* out)
279 : {
280 44 : return ParseIntegral<uint64_t>(str, out);
281 : }
282 :
283 32 : std::string FormatParagraph(std::string_view in, size_t width, size_t indent)
284 : {
285 32 : assert(width >= indent);
286 32 : std::stringstream out;
287 32 : size_t ptr = 0;
288 32 : size_t indented = 0;
289 98 : while (ptr < in.size())
290 : {
291 68 : size_t lineend = in.find_first_of('\n', ptr);
292 68 : if (lineend == std::string::npos) {
293 52 : lineend = in.size();
294 52 : }
295 68 : const size_t linelen = lineend - ptr;
296 68 : const size_t rem_width = width - indented;
297 68 : if (linelen <= rem_width) {
298 40 : out << in.substr(ptr, linelen + 1);
299 40 : ptr = lineend + 1;
300 40 : indented = 0;
301 40 : } else {
302 28 : size_t finalspace = in.find_last_of(" \n", ptr + rem_width);
303 28 : 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 26 : out << in.substr(ptr, finalspace - ptr) << "\n";
313 26 : if (in[finalspace] == '\n') {
314 2 : indented = 0;
315 26 : } else if (indent) {
316 8 : out << std::string(indent, ' ');
317 8 : indented = indent;
318 8 : }
319 26 : ptr = finalspace + 1;
320 : }
321 : }
322 32 : return out.str();
323 32 : }
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 1294 : static inline bool ProcessMantissaDigit(char ch, int64_t &mantissa, int &mantissa_tzeros)
337 : {
338 1294 : if(ch == '0')
339 775 : ++mantissa_tzeros;
340 : else {
341 1504 : for (int i=0; i<=mantissa_tzeros; ++i) {
342 1008 : if (mantissa > (UPPER_BOUND / 10LL))
343 23 : return false; /* overflow */
344 985 : mantissa *= 10;
345 985 : }
346 496 : mantissa += ch - '0';
347 496 : mantissa_tzeros = 0;
348 : }
349 1271 : return true;
350 1294 : }
351 :
352 126 : bool ParseFixedPoint(std::string_view val, int decimals, int64_t *amount_out)
353 : {
354 126 : int64_t mantissa = 0;
355 126 : int64_t exponent = 0;
356 126 : int mantissa_tzeros = 0;
357 126 : bool mantissa_sign = false;
358 126 : bool exponent_sign = false;
359 126 : int ptr = 0;
360 126 : int end = val.size();
361 126 : int point_ofs = 0;
362 :
363 126 : if (ptr < end && val[ptr] == '-') {
364 33 : mantissa_sign = true;
365 33 : ++ptr;
366 33 : }
367 126 : if (ptr < end)
368 : {
369 122 : if (val[ptr] == '0') {
370 : /* pass single 0 */
371 41 : ++ptr;
372 122 : } else if (val[ptr] >= '1' && val[ptr] <= '9') {
373 555 : while (ptr < end && IsDigit(val[ptr])) {
374 483 : if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros))
375 0 : return false; /* overflow */
376 483 : ++ptr;
377 : }
378 81 : } else return false; /* missing expected digit */
379 117 : } else return false; /* empty string or loose '-' */
380 113 : if (ptr < end && val[ptr] == '.')
381 : {
382 90 : ++ptr;
383 90 : if (ptr < end && IsDigit(val[ptr]))
384 : {
385 876 : while (ptr < end && IsDigit(val[ptr])) {
386 811 : if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros))
387 23 : return false; /* overflow */
388 788 : ++ptr;
389 788 : ++point_ofs;
390 : }
391 67 : } else return false; /* missing expected digit */
392 65 : }
393 88 : if (ptr < end && (val[ptr] == 'e' || val[ptr] == 'E'))
394 : {
395 20 : ++ptr;
396 20 : if (ptr < end && val[ptr] == '+')
397 3 : ++ptr;
398 17 : else if (ptr < end && val[ptr] == '-') {
399 11 : exponent_sign = true;
400 11 : ++ptr;
401 11 : }
402 20 : if (ptr < end && IsDigit(val[ptr])) {
403 37 : while (ptr < end && IsDigit(val[ptr])) {
404 21 : if (exponent > (UPPER_BOUND / 10LL))
405 0 : return false; /* overflow */
406 21 : exponent = exponent * 10 + val[ptr] - '0';
407 21 : ++ptr;
408 : }
409 20 : } else return false; /* missing expected digit */
410 16 : }
411 84 : if (ptr != end)
412 6 : return false; /* trailing garbage */
413 :
414 : /* finalize exponent */
415 78 : if (exponent_sign)
416 9 : exponent = -exponent;
417 78 : exponent = exponent - point_ofs + mantissa_tzeros;
418 :
419 : /* finalize mantissa */
420 78 : if (mantissa_sign)
421 13 : mantissa = -mantissa;
422 :
423 : /* convert to one 64-bit fixed-point value */
424 78 : exponent += decimals;
425 78 : if (exponent < 0)
426 17 : return false; /* cannot represent values smaller than 10^-decimals */
427 61 : if (exponent >= 18)
428 6 : return false; /* cannot represent values larger than or equal to 10^(18-decimals) */
429 :
430 296 : for (int i=0; i < exponent; ++i) {
431 242 : if (mantissa > (UPPER_BOUND / 10LL) || mantissa < -(UPPER_BOUND / 10LL))
432 1 : return false; /* overflow */
433 241 : mantissa *= 10;
434 241 : }
435 54 : if (mantissa > UPPER_BOUND || mantissa < -UPPER_BOUND)
436 0 : return false; /* overflow */
437 :
438 54 : if (amount_out)
439 54 : *amount_out = mantissa;
440 :
441 54 : return true;
442 126 : }
443 :
444 103 : std::string ToLower(std::string_view str)
445 : {
446 103 : std::string r;
447 103 : r.reserve(str.size());
448 1364 : for (auto ch : str) r += ToLower(ch);
449 103 : return r;
450 103 : }
451 :
452 10 : std::string ToUpper(std::string_view str)
453 : {
454 10 : std::string r;
455 10 : r.reserve(str.size());
456 44 : for (auto ch : str) r += ToUpper(ch);
457 10 : return r;
458 10 : }
459 :
460 8 : std::string Capitalize(std::string str)
461 : {
462 8 : if (str.empty()) return str;
463 6 : str[0] = ToUpper(str.front());
464 6 : return str;
465 8 : }
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 755967 : std::string HexStr(const Span<const uint8_t> s)
486 : {
487 755967 : 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 755967 : char* it = rv.data();
492 22525869 : for (uint8_t v : s) {
493 21769902 : std::memcpy(it, byte_to_hex[v].data(), 2);
494 21769902 : it += 2;
495 : }
496 :
497 755967 : assert(it == rv.data() + rv.size());
498 755967 : return rv;
499 755967 : }
500 :
501 21 : std::optional<uint64_t> ParseByteUnits(std::string_view str, ByteUnit default_multiplier)
502 : {
503 21 : if (str.empty()) {
504 1 : return std::nullopt;
505 : }
506 20 : auto multiplier = default_multiplier;
507 20 : char unit = str.back();
508 20 : 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 2 : multiplier = ByteUnit::M;
520 2 : 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 6 : unit = 0;
535 6 : break;
536 : }
537 :
538 20 : uint64_t unit_amount = static_cast<uint64_t>(multiplier);
539 20 : auto parsed_num = ToIntegral<uint64_t>(unit ? str.substr(0, str.size() - 1) : str);
540 20 : if (!parsed_num || parsed_num > std::numeric_limits<uint64_t>::max() / unit_amount) { // check overflow
541 8 : return std::nullopt;
542 : }
543 12 : return *parsed_num * unit_amount;
544 21 : }
|