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