Branch data Line data Source code
1 : : /*
2 : : * Copyright (C) 2026 by Thun Lu. All rights reserved.
3 : : * Author: Thun Lu <thun.lu@zohomail.cn>
4 : : * Repo: https://github.com/thun-res/vlink
5 : : * _ __ __ _ __
6 : : * | | / / / / (_) ____ / /__
7 : : * | | / / / / / / / __ \ / //_/
8 : : * | |/ / / /___ / / / / / / / ,<
9 : : * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10 : : *
11 : : * Licensed under the Apache License, Version 2.0 (the "License");
12 : : * you may not use this file except in compliance with the License.
13 : : * You may obtain a copy of the License at
14 : : *
15 : : * http://www.apache.org/licenses/LICENSE-2.0
16 : : *
17 : : * Unless required by applicable law or agreed to in writing, software
18 : : * distributed under the License is distributed on an "AS IS" BASIS,
19 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 : : * See the License for the specific language governing permissions and
21 : : * limitations under the License.
22 : : */
23 : :
24 : : #include "./base/helpers.h"
25 : :
26 : : #include <cctype>
27 : : #include <charconv>
28 : : #include <chrono>
29 : : #include <clocale>
30 : : #include <codecvt>
31 : : #include <cstdio>
32 : : #include <cwchar>
33 : : #include <filesystem>
34 : : #include <iomanip>
35 : : #include <locale>
36 : : #include <sstream>
37 : : #include <string>
38 : : #include <vector>
39 : :
40 : : #ifdef _WIN32
41 : : #include <Windows.h>
42 : : #endif
43 : :
44 : : namespace vlink {
45 : :
46 : : namespace Helpers {
47 : :
48 : 1081 : int to_int(const std::string& str, int dv) noexcept {
49 [ + + ]: 1081 : if (str.empty()) {
50 : 1028 : return dv;
51 : : }
52 : :
53 : : try {
54 : 53 : size_t pos = 0;
55 [ + + ]: 53 : auto value = std::stoi(str, &pos, 0);
56 : :
57 [ + + ]: 50 : if (pos != str.size()) {
58 : 2 : return dv;
59 : : }
60 : :
61 : 48 : return value;
62 [ - + ]: 3 : } catch (std::exception&) {
63 : 3 : return dv;
64 : 3 : }
65 : : }
66 : :
67 : 10 : int64_t to_long(const std::string& str, int64_t dv, int offset) noexcept {
68 [ + + ]: 10 : if (str.empty()) {
69 : 1 : return dv;
70 : : }
71 : :
72 : : try {
73 : 9 : size_t pos = 0;
74 [ + + ]: 9 : auto value = std::stoll(str, &pos, 0);
75 : :
76 [ + + ]: 7 : if (pos != str.size() - offset) {
77 : 1 : return dv;
78 : : }
79 : :
80 : 6 : return value;
81 [ - + ]: 2 : } catch (std::exception&) {
82 : 2 : return dv;
83 : 2 : }
84 : : }
85 : :
86 : 118 : std::string double_to_string(double value, int precision) noexcept {
87 : : // char buffer[64];
88 : :
89 : : // auto [ptr, ec] = std::to_chars(buffer, buffer + sizeof(buffer), value, std::chars_format::fixed, precision);
90 : :
91 : : // if (ec == std::errc()) {
92 : : // return std::string(buffer, ptr);
93 : : // }
94 : :
95 : : // return {};
96 : :
97 [ + + ]: 118 : thread_local std::ostringstream ss;
98 : 118 : ss.clear();
99 : 118 : ss.str("");
100 : :
101 : 118 : ss << std::fixed << std::setprecision(precision) << value;
102 : :
103 : 118 : return ss.str();
104 : : }
105 : :
106 : 5 : uint64_t hash_combine(uint64_t a, uint64_t b) noexcept {
107 : 5 : a ^= b + 0x9e3779b97f4a7c15ULL + (a << 6) + (a >> 2);
108 : 5 : return a;
109 : : }
110 : :
111 : 126 : void replace_string(std::string& str, const std::string& from, const std::string& to) noexcept {
112 [ + + + + : 126 : if (from.empty() || str.empty()) {
+ + ]
113 : 2 : return;
114 : : }
115 : :
116 : 124 : size_t pos = 0;
117 : :
118 [ + + ]: 249 : while ((pos = str.find(from, pos)) != std::string::npos) {
119 : 125 : str.replace(pos, from.length(), to);
120 : 125 : pos += to.length();
121 : : }
122 : : }
123 : :
124 : 1907 : std::string trim_string(const std::string& str) noexcept {
125 [ + + ]: 1907 : if (str.empty()) {
126 : 69 : return {};
127 : : }
128 : :
129 : 1838 : auto start = str.begin();
130 : :
131 [ + + + + : 1852 : while (start != str.end() && std::isspace(static_cast<unsigned char>(*start)) != 0) {
+ + ]
132 : 14 : ++start;
133 : : }
134 : :
135 [ + + ]: 1838 : if VUNLIKELY (start == str.end()) {
136 : 1 : return {};
137 : : }
138 : :
139 : 1837 : auto end = str.rbegin();
140 : :
141 [ + - + + : 1846 : while (end != str.rend() && std::isspace(static_cast<unsigned char>(*end)) != 0) {
+ + ]
142 : 9 : ++end;
143 : : }
144 : :
145 : 1837 : return std::string(start, end.base());
146 : : }
147 : :
148 : 246 : std::string_view trim_string_view(std::string_view str) noexcept {
149 [ + + ]: 246 : if (str.empty()) {
150 : 1 : return {};
151 : : }
152 : :
153 : 245 : size_t start = 0;
154 : :
155 [ + + + + : 263 : while (start < str.size() && std::isspace(static_cast<unsigned char>(str[start])) != 0) {
+ + ]
156 : 18 : ++start;
157 : : }
158 : :
159 [ + + ]: 245 : if VUNLIKELY (start == str.size()) {
160 : 2 : return {};
161 : : }
162 : :
163 : 243 : size_t end = str.size();
164 : :
165 [ + - + + : 253 : while (end > start && std::isspace(static_cast<unsigned char>(str[end - 1])) != 0) {
+ + ]
166 : 10 : --end;
167 : : }
168 : :
169 : 243 : return str.substr(start, end - start);
170 : : }
171 : :
172 : 2 : std::wstring string_to_wstring(const std::string& input) noexcept {
173 : : #ifdef _WIN32
174 : : std::wstring dest;
175 : : int length = ::MultiByteToWideChar(CP_UTF8, 0, input.c_str(), input.size(), nullptr, 0);
176 : :
177 : : if VUNLIKELY (length <= 0) {
178 : : return dest;
179 : : }
180 : :
181 : : auto* buffer = new (std::nothrow) WCHAR[length + 1];
182 : :
183 : : if VUNLIKELY (!buffer) {
184 : : return dest;
185 : : }
186 : :
187 : : ::MultiByteToWideChar(CP_UTF8, 0, input.c_str(), input.size(), buffer, length);
188 : :
189 : : buffer[length] = '\0';
190 : : dest.append(buffer);
191 : : delete[] buffer;
192 : :
193 : : return dest;
194 : : #else
195 : 2 : std::setlocale(LC_ALL, "en_US.UTF-8");
196 : :
197 : 2 : std::mbstate_t state = std::mbstate_t();
198 : 2 : const char* src = input.c_str();
199 : :
200 : 2 : size_t len = std::mbsrtowcs(nullptr, &src, 0, &state);
201 : :
202 [ - + ]: 2 : if VUNLIKELY (len == static_cast<size_t>(-1)) {
203 : : return std::wstring(); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
204 : : }
205 : :
206 : 2 : std::wstring dest(len, L'\0');
207 : 2 : std::mbsrtowcs(dest.data(), &src, len, &state);
208 : :
209 : 2 : return dest;
210 : : #endif
211 : 2 : }
212 : :
213 : 2 : std::string wstring_to_string(const std::wstring& input) noexcept {
214 : : #ifdef _WIN32
215 : : std::string dest;
216 : : int length = ::WideCharToMultiByte(CP_UTF8, 0, input.c_str(), input.size(), nullptr, 0, nullptr, nullptr);
217 : :
218 : : if VUNLIKELY (length <= 0) {
219 : : return dest;
220 : : }
221 : :
222 : : auto* buffer = new (std::nothrow) char[length + 1];
223 : :
224 : : if VUNLIKELY (!buffer) {
225 : : return dest;
226 : : }
227 : :
228 : : ::WideCharToMultiByte(CP_UTF8, 0, input.c_str(), input.size(), buffer, length, nullptr, nullptr);
229 : :
230 : : buffer[length] = '\0';
231 : : dest.append(buffer);
232 : : delete[] buffer;
233 : :
234 : : return dest;
235 : : #else
236 : 2 : std::setlocale(LC_ALL, "en_US.UTF-8");
237 : :
238 : 2 : std::mbstate_t state = std::mbstate_t();
239 : 2 : const wchar_t* src = input.c_str();
240 : :
241 : 2 : size_t len = std::wcsrtombs(nullptr, &src, 0, &state);
242 : :
243 [ - + ]: 2 : if VUNLIKELY (len == static_cast<size_t>(-1)) {
244 : : return std::string(); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
245 : : }
246 : :
247 : 2 : std::string dest(len, '\0');
248 : 2 : std::wcsrtombs(dest.data(), &src, len, &state);
249 : :
250 : 2 : return dest;
251 : : #endif
252 : 2 : }
253 : :
254 : 2 : std::string string_local_to_utf8(const std::string& local_str) noexcept {
255 : : #ifdef _WIN32
256 : :
257 : : if VUNLIKELY (local_str.empty()) {
258 : : return {};
259 : : }
260 : :
261 : : int wide_size = MultiByteToWideChar(CP_ACP, 0, local_str.c_str(), -1, nullptr, 0);
262 : :
263 : : if VUNLIKELY (wide_size <= 0) {
264 : : return {};
265 : : }
266 : :
267 : : std::wstring wide_str(wide_size, L'\0');
268 : : MultiByteToWideChar(CP_ACP, 0, local_str.c_str(), -1, wide_str.data(), wide_size);
269 : :
270 : : int utf8_size = WideCharToMultiByte(CP_UTF8, 0, wide_str.c_str(), -1, nullptr, 0, nullptr, nullptr);
271 : :
272 : : if VUNLIKELY (utf8_size <= 0) {
273 : : return {};
274 : : }
275 : :
276 : : std::string utf8_str(utf8_size - 1, '\0');
277 : : WideCharToMultiByte(CP_UTF8, 0, wide_str.c_str(), -1, utf8_str.data(), utf8_size, nullptr, nullptr);
278 : :
279 : : return utf8_str;
280 : : #else
281 : 2 : return local_str;
282 : : #endif
283 : : }
284 : :
285 : 2 : std::string string_utf8_to_local(const std::string& utf8_str) noexcept {
286 : : #ifdef _WIN32
287 : :
288 : : if VUNLIKELY (utf8_str.empty()) {
289 : : return {};
290 : : }
291 : :
292 : : int wide_size = MultiByteToWideChar(CP_UTF8, 0, utf8_str.c_str(), -1, nullptr, 0);
293 : :
294 : : if VUNLIKELY (wide_size <= 0) {
295 : : return {};
296 : : }
297 : :
298 : : std::wstring wide_str(wide_size, L'\0');
299 : : MultiByteToWideChar(CP_UTF8, 0, utf8_str.c_str(), -1, wide_str.data(), wide_size);
300 : :
301 : : int local_size = WideCharToMultiByte(CP_ACP, 0, wide_str.c_str(), -1, nullptr, 0, nullptr, nullptr);
302 : :
303 : : if VUNLIKELY (local_size <= 0) {
304 : : return {};
305 : : }
306 : :
307 : : std::string local_str(local_size - 1, '\0');
308 : : WideCharToMultiByte(CP_ACP, 0, wide_str.c_str(), -1, local_str.data(), local_size, nullptr, nullptr);
309 : :
310 : : return local_str;
311 : : #else
312 : 2 : return utf8_str;
313 : : #endif
314 : : }
315 : :
316 : 2 : std::string path_to_string(const std::filesystem::path& path) noexcept {
317 : : try {
318 : : #ifdef _WIN32
319 : : #if __cplusplus >= 202002L
320 : : auto u8str = path.u8string();
321 : : return std::string(u8str.begin(), u8str.end());
322 : : #else
323 : : return path.u8string();
324 : : #endif
325 : : #else
326 [ + - ]: 2 : return path.string();
327 : : #endif
328 : : } catch (std::filesystem::filesystem_error&) { // LCOV_EXCL_LINE GCOVR_EXCL_LINE
329 : : return std::string(); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
330 : : } // LCOV_EXCL_LINE GCOVR_EXCL_LINE
331 : : }
332 : :
333 : 10 : std::vector<std::string> split(const std::string& str, char f) noexcept {
334 [ + + ]: 10 : if (str.empty()) {
335 : 1 : return {};
336 : : }
337 : :
338 : 9 : std::vector<std::string> result;
339 : :
340 : 9 : size_t start = 0;
341 : 9 : size_t end = 0;
342 : :
343 [ + + ]: 22 : while ((end = str.find(f, start)) != std::string::npos) {
344 [ + + ]: 13 : if (end > start) {
345 : 7 : result.emplace_back(str.substr(start, end - start));
346 : : }
347 : :
348 : 13 : start = end + 1;
349 : : }
350 : :
351 [ + + ]: 9 : if (start < str.size()) {
352 : 6 : result.emplace_back(str.substr(start));
353 : : }
354 : :
355 : 9 : return result;
356 : 9 : }
357 : :
358 : 397 : std::vector<std::string_view> split_view(std::string_view str, char f) noexcept {
359 [ + + ]: 397 : if (str.empty()) {
360 : 1 : return {};
361 : : }
362 : :
363 : 396 : std::vector<std::string_view> result;
364 : :
365 : 396 : size_t start = 0;
366 : 396 : size_t end = 0;
367 : :
368 [ + + ]: 1342 : while ((end = str.find(f, start)) != std::string_view::npos) {
369 [ + + ]: 946 : if (end > start) {
370 : 945 : result.emplace_back(str.substr(start, end - start));
371 : : }
372 : :
373 : 946 : start = end + 1;
374 : : }
375 : :
376 [ + + ]: 396 : if (start < str.size()) {
377 : 272 : result.emplace_back(str.substr(start));
378 : : }
379 : :
380 : 396 : return result;
381 : 396 : }
382 : :
383 : 311 : std::vector<std::string> split_any(const std::string& str, std::string_view delimiters) noexcept {
384 : 311 : const auto views = split_any_view(str, delimiters);
385 : :
386 : 311 : std::vector<std::string> result;
387 : 311 : result.reserve(views.size());
388 : :
389 [ + + ]: 540 : for (const auto item : views) {
390 : 229 : result.emplace_back(item);
391 : : }
392 : :
393 : 311 : return result;
394 : 311 : }
395 : :
396 : 317 : std::vector<std::string_view> split_any_view(std::string_view str, std::string_view delimiters) noexcept {
397 [ + + ]: 317 : if (str.empty()) {
398 : 91 : return {};
399 : : }
400 : :
401 [ + + ]: 226 : if (delimiters.empty()) {
402 : 2 : auto token = trim_string_view(str);
403 : :
404 [ - + ]: 2 : if (token.empty()) {
405 : 0 : return {};
406 : : }
407 : :
408 : 2 : return {token};
409 : : }
410 : :
411 : 224 : std::vector<std::string_view> result;
412 : :
413 : 224 : size_t start = 0;
414 : 224 : size_t end = 0;
415 : :
416 [ + + ]: 251 : while ((end = str.find_first_of(delimiters, start)) != std::string_view::npos) {
417 [ + + ]: 27 : if (end > start) {
418 : 19 : auto token = trim_string_view(str.substr(start, end - start));
419 : :
420 [ + + ]: 19 : if (!token.empty()) {
421 : 18 : result.emplace_back(token);
422 : : }
423 : : }
424 : :
425 : 27 : start = end + 1;
426 : : }
427 : :
428 [ + + ]: 224 : if (start < str.size()) {
429 : 220 : auto token = trim_string_view(str.substr(start));
430 : :
431 [ + - ]: 220 : if (!token.empty()) {
432 : 220 : result.emplace_back(token);
433 : : }
434 : : }
435 : :
436 : 224 : return result;
437 : 224 : }
438 : :
439 : 1486 : std::string escape_field(std::string_view value) noexcept {
440 : : static constexpr char kHex[] = "0123456789ABCDEF";
441 : :
442 : 1486 : std::string out;
443 : 1486 : out.reserve(value.size());
444 : :
445 [ + + ]: 27573 : for (const unsigned char ch : value) {
446 [ + + + + : 26087 : if (ch == '%' || ch == ' ' || ch == ':' || ch == '\n' || ch == '\r') {
+ + + + +
+ ]
447 : 842 : out.push_back('%');
448 : 842 : out.push_back(kHex[(ch >> 4U) & 0x0FU]);
449 : 842 : out.push_back(kHex[ch & 0x0FU]);
450 : : } else {
451 : 25245 : out.push_back(static_cast<char>(ch));
452 : : }
453 : : }
454 : :
455 : 1486 : return out;
456 : : }
457 : :
458 : 539 : std::string unescape_field(std::string_view value) noexcept {
459 : 368 : auto from_hex = [](char ch) noexcept -> int {
460 [ + + + + ]: 368 : if (ch >= '0' && ch <= '9') {
461 : 187 : return ch - '0';
462 : : }
463 : :
464 [ + + + + ]: 181 : if (ch >= 'A' && ch <= 'F') {
465 : 177 : return ch - 'A' + 10;
466 : : }
467 : :
468 [ + + + - ]: 4 : if (ch >= 'a' && ch <= 'f') {
469 : 1 : return ch - 'a' + 10;
470 : : }
471 : :
472 : 3 : return -1;
473 : : };
474 : :
475 : 539 : std::string out;
476 : 539 : out.reserve(value.size());
477 : :
478 [ + + ]: 8628 : for (size_t i = 0; i < value.size(); ++i) {
479 [ + + + + : 8089 : if (value[i] == '%' && i + 2U < value.size()) {
+ + ]
480 : 184 : const int hi = from_hex(value[i + 1U]);
481 : 184 : const int lo = from_hex(value[i + 2U]);
482 : :
483 [ + + + + : 184 : if VLIKELY (hi >= 0 && lo >= 0) {
+ + ]
484 : 182 : out.push_back(static_cast<char>((hi << 4U) | lo));
485 : 182 : i += 2U;
486 : :
487 : 182 : continue;
488 : : }
489 : : }
490 : :
491 : 7907 : out.push_back(value[i]);
492 : : }
493 : :
494 : 539 : return out;
495 : : }
496 : :
497 : 532 : uint32_t get_hash_code(const std::string& str) noexcept {
498 [ + + ]: 532 : if (str.empty()) {
499 : 282 : return 0;
500 : : }
501 : :
502 : 250 : uint32_t value = 0;
503 : :
504 : 250 : auto [p, error] = std::from_chars(str.data(), str.data() + str.size(), value);
505 : :
506 [ + + ]: 250 : if (error == std::errc()) {
507 : 1 : return value;
508 : : }
509 : :
510 : 249 : value = static_cast<uint32_t>(std::hash<std::string>{}(str));
511 : :
512 : 249 : return value;
513 : : }
514 : :
515 : 5 : std::string format_milliseconds(int64_t milliseconds, bool show_millis) noexcept {
516 : : char buffer[32];
517 : :
518 [ - + ]: 5 : if (milliseconds < 0) {
519 : 0 : milliseconds += 24 * 60 * 60 * 1000;
520 : : }
521 : :
522 : 5 : int64_t hours = milliseconds / (1000 * 60 * 60);
523 : 5 : int64_t minutes = (milliseconds % (1000 * 60 * 60)) / (1000 * 60);
524 : 5 : int64_t seconds = ((milliseconds % (1000 * 60 * 60)) % (1000 * 60)) / 1000;
525 : :
526 : : // NOLINTBEGIN
527 : :
528 [ + + ]: 5 : if (show_millis) {
529 : 2 : int64_t millis = ((milliseconds % (1000 * 60 * 60)) % (1000 * 60)) % 1000;
530 : :
531 : 2 : std::snprintf(buffer, sizeof(buffer), "%02lld:%02lld:%02lld:%03lld", static_cast<long long>(hours),
532 : : static_cast<long long>(minutes), static_cast<long long>(seconds), static_cast<long long>(millis));
533 : :
534 : : } else {
535 : 3 : std::snprintf(buffer, sizeof(buffer), "%02lld:%02lld:%02lld", static_cast<long long>(hours),
536 : : static_cast<long long>(minutes), static_cast<long long>(seconds));
537 : : }
538 : :
539 : : // NOLINTEND
540 : :
541 : 5 : return buffer;
542 : : }
543 : :
544 : 3 : std::string format_date(int64_t nanoseconds_since_epoch) noexcept {
545 : : auto time_point = std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds>(
546 : 3 : std::chrono::nanoseconds(nanoseconds_since_epoch));
547 : :
548 : 3 : auto system_time = std::chrono::time_point_cast<std::chrono::milliseconds>(time_point);
549 : 3 : auto duration_since_epoch = system_time.time_since_epoch();
550 : 3 : auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration_since_epoch);
551 : 3 : auto milliseconds = (duration_since_epoch - seconds).count();
552 : :
553 : 3 : std::time_t time_t_seconds = seconds.count();
554 : 3 : std::tm tm_result{};
555 : 3 : std::tm* tm_ptr = nullptr;
556 : :
557 : : #if defined(_WIN32) || defined(_WIN64)
558 : :
559 : : if (gmtime_s(&tm_result, &time_t_seconds) == 0) {
560 : : tm_ptr = &tm_result;
561 : : }
562 : : #else
563 : :
564 [ + - ]: 3 : if (gmtime_r(&time_t_seconds, &tm_result) != nullptr) {
565 : 3 : tm_ptr = &tm_result;
566 : : }
567 : : #endif
568 : :
569 [ - + ]: 3 : if VUNLIKELY (!tm_ptr) {
570 : : return {}; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
571 : : }
572 : :
573 : : char buffer[32];
574 : 3 : std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", tm_ptr);
575 : :
576 : : char full_buffer[64];
577 : 3 : std::snprintf(full_buffer, sizeof(full_buffer), "%s.%03d", buffer, static_cast<int>(milliseconds));
578 : :
579 : 3 : return full_buffer;
580 : : }
581 : :
582 : 5 : std::string format_time_diff(int32_t milliseconds) noexcept {
583 : 5 : bool negative = milliseconds < 0;
584 : :
585 [ + + ]: 5 : int64_t abs_milliseconds = negative ? -static_cast<int64_t>(milliseconds) : static_cast<int64_t>(milliseconds);
586 : :
587 : 5 : int hours = static_cast<int>(abs_milliseconds / (1000 * 60 * 60));
588 : 5 : int minutes = static_cast<int>((abs_milliseconds % (1000 * 60 * 60)) / (1000 * 60));
589 : 5 : int seconds = static_cast<int>((abs_milliseconds % (1000 * 60)) / 1000);
590 : 5 : int millis = static_cast<int>(abs_milliseconds % 1000);
591 : :
592 : : char buffer[32];
593 : :
594 [ + + ]: 5 : if (negative) {
595 : 1 : std::snprintf(buffer, sizeof(buffer), "-%02d:%02d:%02d:%03d", hours, minutes, seconds, millis);
596 : : } else {
597 : 4 : std::snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d:%03d", hours, minutes, seconds, millis);
598 : : }
599 : :
600 : 5 : return buffer;
601 : : }
602 : :
603 : 4 : std::string format_hex_number(int64_t hex_number) noexcept {
604 : : char buffer[32];
605 : 4 : auto [ptr, ec] = std::to_chars(buffer, buffer + sizeof(buffer), hex_number, 16);
606 : :
607 [ + - ]: 4 : if (ec == std::errc()) {
608 : 4 : return "0x" + std::string(buffer, ptr);
609 : : }
610 : :
611 : : return "0x0"; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
612 : : }
613 : :
614 : 2 : std::string format_hex_number(uint64_t hex_number) noexcept {
615 : : char buffer[32];
616 : 2 : auto [ptr, ec] = std::to_chars(buffer, buffer + sizeof(buffer), hex_number, 16);
617 : :
618 [ + - ]: 2 : if (ec == std::errc()) {
619 : 2 : std::string result = "0x";
620 : :
621 [ + + ]: 8 : for (char* p = buffer; p < ptr; ++p) {
622 : 6 : result += static_cast<char>(std::toupper(*p)); // NOLINT(clang-analyzer-core.CallAndMessage)
623 : : }
624 : :
625 : 2 : return result;
626 : : } // LCOV_EXCL_LINE GCOVR_EXCL_LINE
627 : :
628 : : return "0x0"; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
629 : : }
630 : :
631 : 7 : std::string format_file_size(size_t size) noexcept {
632 : : char buffer[32];
633 : :
634 [ + + ]: 7 : if (size < 1024LL * 1024) {
635 : 4 : std::snprintf(buffer, sizeof(buffer), "%.2fKB", size / 1024.0);
636 [ + + ]: 3 : } else if (size < 1024LL * 1024 * 1024) {
637 : 2 : std::snprintf(buffer, sizeof(buffer), "%.2fMB", size / 1024.0 / 1024.0);
638 : : } else {
639 : 1 : std::snprintf(buffer, sizeof(buffer), "%.2fGB", size / 1024.0 / 1024.0 / 1024.0);
640 : : }
641 : :
642 : 7 : return buffer;
643 : : }
644 : :
645 : 5 : std::string format_rate_size(size_t size) noexcept {
646 : : char buffer[32];
647 : :
648 [ + + ]: 5 : if (size < 1024) {
649 : 2 : std::snprintf(buffer, sizeof(buffer), "%.2fB/s", static_cast<double>(size));
650 [ + + ]: 3 : } else if (size < 1024LL * 1024) {
651 : 1 : std::snprintf(buffer, sizeof(buffer), "%.2fKB/s", size / 1024.0);
652 [ + + ]: 2 : } else if (size < 1024LL * 1024 * 1024) {
653 : 1 : std::snprintf(buffer, sizeof(buffer), "%.2fMB/s", size / 1024.0 / 1024.0);
654 : : } else {
655 : 1 : std::snprintf(buffer, sizeof(buffer), "%.2fGB/s", size / 1024.0 / 1024.0 / 1024.0);
656 : : }
657 : :
658 : 5 : return buffer;
659 : : }
660 : :
661 : 9 : int64_t convert_date_to_timestamp(const std::string& date) noexcept {
662 : 9 : std::tm tm = {};
663 : : char delimiter_ms;
664 : 9 : int milliseconds = 0;
665 : :
666 [ + + ]: 9 : thread_local std::istringstream ss;
667 : 9 : ss.clear();
668 : 9 : ss.str(date);
669 : :
670 : 9 : ss >> std::get_time(&tm, "%Y/%m/%d %H:%M:%S");
671 : :
672 [ + + ]: 9 : if (ss.fail()) {
673 : 2 : return -1;
674 : : }
675 : :
676 [ + + ]: 7 : if (ss >> delimiter_ms >> milliseconds) {
677 [ + + + - : 6 : if (delimiter_ms != ':' || milliseconds < 0 || milliseconds >= 1000) {
+ + ]
678 : 2 : return -1;
679 : : }
680 : : }
681 : :
682 : 5 : auto tp = std::chrono::system_clock::from_time_t(std::mktime(&tm));
683 : 5 : auto ns = std::chrono::time_point_cast<std::chrono::nanoseconds>(tp).time_since_epoch().count();
684 : :
685 : 5 : return ns + (static_cast<int64_t>(milliseconds) * 1'000'000LL);
686 : : }
687 : :
688 : : } // namespace Helpers
689 : :
690 : : } // namespace vlink
|