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 <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 : 7758 : signed char HexDigit(char c)
58 : : {
59 : 7758 : 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 : 2354 : bool SplitHostPort(std::string_view in, uint16_t& portOut, std::string& hostOut)
102 : : {
103 : 2354 : bool valid = false;
104 : 2354 : 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 : 2354 : bool fHaveColon = colon != in.npos;
107 [ + + + + ]: 3119 : 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 [ + + + + ]: 2354 : bool fMultiColon{fHaveColon && colon != 0 && (in.find_last_of(':', colon - 1) != in.npos)};
109 [ + + + + : 2354 : if (fHaveColon && (colon == 0 || fBracketed || !fMultiColon)) {
+ + + + ]
110 : : uint16_t n;
111 [ + + ]: 725 : if (ParseUInt16(in.substr(colon + 1), &n)) {
112 : 208 : in = in.substr(0, colon);
113 : 208 : portOut = n;
114 : 208 : valid = (portOut != 0);
115 : 208 : }
116 : 725 : } else {
117 : 1629 : valid = true;
118 : : }
119 [ + + + + : 2354 : if (in.size() > 0 && in[0] == '[' && in[in.size() - 1] == ']') {
+ + ]
120 : 39 : hostOut = in.substr(1, in.size() - 2);
121 : 39 : } else {
122 : 2315 : hostOut = in;
123 : : }
124 : :
125 : 2354 : 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 : 3130 : std::string EncodeBase32(Span<const unsigned char> input, bool pad)
175 : : {
176 : : static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567";
177 : :
178 : 3130 : std::string str;
179 [ + - ]: 3130 : str.reserve(((input.size() + 4) / 5) * 8);
180 [ + - ]: 159030 : ConvertBits<8, 5, true>([&](int v) { str += pbase32[v]; }, input.begin(), input.end());
181 [ + + ]: 3130 : if (pad) {
182 [ - + ]: 3125 : while (str.size() % 8) {
183 [ # # ]: 0 : str += '=';
184 : : }
185 : 3125 : }
186 : 3130 : return str;
187 [ + - ]: 3130 : }
188 : :
189 : 0 : std::string EncodeBase32(std::string_view str, bool pad)
190 : : {
191 : 0 : return EncodeBase32(MakeUCharSpan(str), pad);
192 : : }
193 : :
194 : 1907 : 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 [ + + ]: 1907 : if (str.size() % 8 != 0) return {};
213 : : /* 1, 3, 4, or 6 padding '=' suffix characters are permitted. */
214 [ + - + + ]: 1791 : if (str.size() >= 1 && str.back() == '=') str.remove_suffix(1);
215 [ + - + + ]: 1791 : if (str.size() >= 2 && str.substr(str.size() - 2) == "==") str.remove_suffix(2);
216 [ + - + + ]: 1791 : if (str.size() >= 1 && str.back() == '=') str.remove_suffix(1);
217 [ + - + + ]: 1791 : if (str.size() >= 2 && str.substr(str.size() - 2) == "==") str.remove_suffix(2);
218 : :
219 : 1791 : std::vector<unsigned char> ret;
220 [ + - ]: 1791 : ret.reserve((str.size() * 5) / 8);
221 [ + - ]: 1791 : bool valid = ConvertBits<5, 8, false>(
222 : 58934 : [&](unsigned char c) { ret.push_back(c); },
223 : 1791 : str.begin(), str.end(),
224 : 91729 : [](char c) { return decode32_table[uint8_t(c)]; }
225 : : );
226 : :
227 [ + + ]: 1791 : if (!valid) return {};
228 : :
229 [ + - ]: 1558 : return ret;
230 : 1907 : }
231 : :
232 : : namespace {
233 : : template <typename T>
234 : 725 : 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 [ # # # # : 725 : if (str.length() >= 2 && str[0] == '+' && str[1] == '-') {
# # # # #
# # # # #
# # # # +
+ + + + +
# # # # #
# # # # #
# # ]
240 : 11 : return false;
241 : : }
242 [ # # # # : 714 : const std::optional<T> opt_int = ToIntegral<T>((!str.empty() && str[0] == '+') ? str.substr(1) : str);
# # # # #
# # # + +
+ + # # #
# # # #
# ]
243 [ # # # # : 714 : if (!opt_int) {
# # + + #
# # # ]
244 : 506 : return false;
245 : : }
246 [ # # # # : 208 : if (out != nullptr) {
# # + - #
# # # ]
247 : 208 : *out = *opt_int;
248 : 208 : }
249 : 208 : return true;
250 : 725 : }
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 : 725 : bool ParseUInt16(std::string_view str, uint16_t* out)
269 : : {
270 : 725 : 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 : 284 : std::string ToLower(std::string_view str)
445 : : {
446 : 284 : std::string r;
447 [ + + + - : 2556 : for (auto ch : str) r += ToLower(ch);
+ - ]
448 : 284 : return r;
449 [ + - ]: 284 : }
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 : 14 : std::string HexStr(const Span<const uint8_t> s)
484 : : {
485 [ + - ]: 14 : 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 : 14 : char* it = rv.data();
490 [ + + ]: 366 : for (uint8_t v : s) {
491 : 352 : std::memcpy(it, byte_to_hex[v].data(), 2);
492 : 352 : it += 2;
493 : : }
494 : :
495 [ - + ]: 14 : assert(it == rv.data() + rv.size());
496 : 14 : return rv;
497 [ + - ]: 14 : }
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 : }
|