| Line |
Branch |
Exec |
Source |
| 1 |
|
|
#ifndef DETOURMODKIT_FORMAT_HPP |
| 2 |
|
|
#define DETOURMODKIT_FORMAT_HPP |
| 3 |
|
|
|
| 4 |
|
|
/** |
| 5 |
|
|
* @file format.hpp |
| 6 |
|
|
* @brief String trimming and hex formatting for addresses, integers, bytes, and virtual key codes. |
| 7 |
|
|
*/ |
| 8 |
|
|
|
| 9 |
|
|
#include <concepts> |
| 10 |
|
|
#include <cstddef> |
| 11 |
|
|
#include <cstdint> |
| 12 |
|
|
#include <format> |
| 13 |
|
|
#include <string> |
| 14 |
|
|
#include <string_view> |
| 15 |
|
|
#include <vector> |
| 16 |
|
|
|
| 17 |
|
|
namespace DetourModKit |
| 18 |
|
|
{ |
| 19 |
|
|
namespace string |
| 20 |
|
|
{ |
| 21 |
|
|
/** @brief Trims leading and trailing space, tab, newline, carriage return, form feed, and vertical tab. */ |
| 22 |
|
1456 |
[[nodiscard]] inline std::string trim(std::string_view s) |
| 23 |
|
|
{ |
| 24 |
|
1456 |
const char *whitespace_chars = " \t\n\r\f\v"; |
| 25 |
|
|
|
| 26 |
|
1456 |
const size_t first_non_whitespace = s.find_first_not_of(whitespace_chars); |
| 27 |
2/2
✓ Branch 3 → 4 taken 46 times.
✓ Branch 3 → 9 taken 1410 times.
|
1456 |
if (std::string_view::npos == first_non_whitespace) |
| 28 |
|
|
{ |
| 29 |
1/2
✓ Branch 6 → 7 taken 46 times.
✗ Branch 6 → 18 not taken.
|
92 |
return ""; |
| 30 |
|
|
} |
| 31 |
|
|
|
| 32 |
|
1410 |
const size_t last_non_whitespace = s.find_last_not_of(whitespace_chars); |
| 33 |
2/4
✓ Branch 12 → 13 taken 1410 times.
✗ Branch 12 → 21 not taken.
✓ Branch 13 → 14 taken 1410 times.
✗ Branch 13 → 21 not taken.
|
1410 |
return std::string(s.substr(first_non_whitespace, (last_non_whitespace - first_non_whitespace + 1))); |
| 34 |
|
|
} |
| 35 |
|
|
} // namespace string |
| 36 |
|
|
|
| 37 |
|
|
namespace format |
| 38 |
|
|
{ |
| 39 |
|
|
/** @brief Formats an address as full-width hex, for example "0x00007FFE12345678". */ |
| 40 |
|
13096 |
[[nodiscard]] inline std::string format_address(uintptr_t address) |
| 41 |
|
|
{ |
| 42 |
2/2
✓ Branch 2 → 3 taken 13092 times.
✓ Branch 2 → 5 taken 4 times.
|
13096 |
return std::format("0x{:0{}X}", address, sizeof(uintptr_t) * 2); |
| 43 |
|
|
} |
| 44 |
|
|
|
| 45 |
|
|
/** |
| 46 |
|
|
* @brief Formats the unsigned two's-complement bit pattern of @p value, for example -1 as "0xFFFFFFFF". |
| 47 |
|
|
* @param width Minimum digit count of the zero-padded hex part. 0 adds no padding. Every format_hex overload |
| 48 |
|
|
* keeps the "0x" prefix and any '-' outside the padded field. |
| 49 |
|
|
* @note For a leading '-' and the signed magnitude, pass a ptrdiff_t. |
| 50 |
|
|
*/ |
| 51 |
|
30 |
[[nodiscard]] inline std::string format_hex(int value, int width = 0) |
| 52 |
|
|
{ |
| 53 |
2/2
✓ Branch 2 → 3 taken 24 times.
✓ Branch 2 → 5 taken 6 times.
|
30 |
if (width > 0) |
| 54 |
1/2
✓ Branch 3 → 4 taken 24 times.
✗ Branch 3 → 8 not taken.
|
24 |
return std::format("0x{:0{}X}", static_cast<unsigned int>(value), width); |
| 55 |
1/2
✓ Branch 5 → 6 taken 6 times.
✗ Branch 5 → 10 not taken.
|
6 |
return std::format("0x{:X}", static_cast<unsigned int>(value)); |
| 56 |
|
|
} |
| 57 |
|
|
|
| 58 |
|
|
/** @brief Formats a long as the int overload does. HRESULT, LONG, LSTATUS, and NTSTATUS bind here exactly. */ |
| 59 |
|
9 |
[[nodiscard]] inline std::string format_hex(long value, int width = 0) |
| 60 |
|
|
{ |
| 61 |
2/2
✓ Branch 2 → 3 taken 2 times.
✓ Branch 2 → 5 taken 7 times.
|
9 |
if (width > 0) |
| 62 |
1/2
✓ Branch 3 → 4 taken 2 times.
✗ Branch 3 → 8 not taken.
|
2 |
return std::format("0x{:0{}X}", static_cast<unsigned long>(value), width); |
| 63 |
1/2
✓ Branch 5 → 6 taken 7 times.
✗ Branch 5 → 10 not taken.
|
7 |
return std::format("0x{:X}", static_cast<unsigned long>(value)); |
| 64 |
|
|
} |
| 65 |
|
|
|
| 66 |
|
|
/** @brief Formats the full unsigned value with no narrowing. @p width works as in the int overload. */ |
| 67 |
|
10 |
template <std::unsigned_integral T> [[nodiscard]] inline std::string format_hex(T value, int width = 0) |
| 68 |
|
|
{ |
| 69 |
5/6
std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > DetourModKit::format::format_hex<unsigned int>(unsigned int, int):
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 5 taken 1 time.
std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > DetourModKit::format::format_hex<unsigned long>(unsigned long, int):
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 5 taken 2 times.
std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > DetourModKit::format::format_hex<unsigned long long>(unsigned long long, int):
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 5 taken 5 times.
|
10 |
if (width > 0) |
| 70 |
2/6
std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > DetourModKit::format::format_hex<unsigned int>(unsigned int, int):
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 8 not taken.
std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > DetourModKit::format::format_hex<unsigned long>(unsigned long, int):
✗ Branch 3 → 4 not taken.
✗ Branch 3 → 8 not taken.
std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > DetourModKit::format::format_hex<unsigned long long>(unsigned long long, int):
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 8 not taken.
|
2 |
return std::format("0x{:0{}X}", value, width); |
| 71 |
3/6
std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > DetourModKit::format::format_hex<unsigned int>(unsigned int, int):
✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 9 not taken.
std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > DetourModKit::format::format_hex<unsigned long>(unsigned long, int):
✓ Branch 5 → 6 taken 2 times.
✗ Branch 5 → 9 not taken.
std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > DetourModKit::format::format_hex<unsigned long long>(unsigned long long, int):
✓ Branch 5 → 6 taken 5 times.
✗ Branch 5 → 9 not taken.
|
8 |
return std::format("0x{:X}", value); |
| 72 |
|
|
} |
| 73 |
|
|
|
| 74 |
|
|
/** |
| 75 |
|
|
* @brief Formats a signed 64-bit value. A negative value prints '-' and the magnitude, for example "-0x10". |
| 76 |
|
|
* @details LONG_PTR, SSIZE_T, and long long bind here on LLP64. @p width works as in the int overload. |
| 77 |
|
|
*/ |
| 78 |
|
14 |
[[nodiscard]] inline std::string format_hex(ptrdiff_t value, int width = 0) |
| 79 |
|
|
{ |
| 80 |
2/2
✓ Branch 2 → 3 taken 8 times.
✓ Branch 2 → 9 taken 6 times.
|
14 |
if (value < 0) |
| 81 |
|
|
{ |
| 82 |
|
|
// The unsigned negation avoids undefined behavior on PTRDIFF_MIN. |
| 83 |
|
8 |
const auto magnitude = static_cast<size_t>(~static_cast<size_t>(value) + 1u); |
| 84 |
2/2
✓ Branch 3 → 4 taken 2 times.
✓ Branch 3 → 6 taken 6 times.
|
8 |
if (width > 0) |
| 85 |
1/2
✓ Branch 4 → 5 taken 2 times.
✗ Branch 4 → 15 not taken.
|
2 |
return std::format("-0x{:0{}X}", magnitude, width); |
| 86 |
1/2
✓ Branch 6 → 7 taken 6 times.
✗ Branch 6 → 16 not taken.
|
6 |
return std::format("-0x{:X}", magnitude); |
| 87 |
|
|
} |
| 88 |
2/2
✓ Branch 9 → 10 taken 2 times.
✓ Branch 9 → 12 taken 4 times.
|
6 |
if (width > 0) |
| 89 |
1/2
✓ Branch 10 → 11 taken 2 times.
✗ Branch 10 → 18 not taken.
|
2 |
return std::format("0x{:0{}X}", static_cast<size_t>(value), width); |
| 90 |
1/2
✓ Branch 12 → 13 taken 4 times.
✗ Branch 12 → 20 not taken.
|
4 |
return std::format("0x{:X}", static_cast<size_t>(value)); |
| 91 |
|
|
} |
| 92 |
|
|
|
| 93 |
|
|
/** @brief Formats a byte as two hex digits, for example "0xCC". */ |
| 94 |
|
3 |
[[nodiscard]] inline std::string format_byte(std::byte b) |
| 95 |
|
|
{ |
| 96 |
1/2
✓ Branch 2 → 3 taken 3 times.
✗ Branch 2 → 5 not taken.
|
3 |
return std::format("0x{:02X}", static_cast<unsigned int>(b)); |
| 97 |
|
|
} |
| 98 |
|
|
|
| 99 |
|
|
/** @brief Formats integers as a comma-separated hex list, for example "[0x72, 0xA0, 0x20]". */ |
| 100 |
|
7 |
[[nodiscard]] inline std::string format_int_vector(const std::vector<int> &values) |
| 101 |
|
|
{ |
| 102 |
2/2
✓ Branch 3 → 4 taken 2 times.
✓ Branch 3 → 9 taken 5 times.
|
7 |
if (values.empty()) |
| 103 |
|
|
{ |
| 104 |
1/2
✓ Branch 6 → 7 taken 2 times.
✗ Branch 6 → 28 not taken.
|
4 |
return "[]"; |
| 105 |
|
|
} |
| 106 |
|
|
|
| 107 |
|
|
// Each entry is about 4 characters ("0x" and 2 or more hex digits) plus the ", " separator. |
| 108 |
|
5 |
std::string result; |
| 109 |
1/2
✓ Branch 11 → 12 taken 5 times.
✗ Branch 11 → 34 not taken.
|
5 |
result.reserve(1 + values.size() * 6 + 1); |
| 110 |
1/2
✓ Branch 12 → 13 taken 5 times.
✗ Branch 12 → 34 not taken.
|
5 |
result += '['; |
| 111 |
2/2
✓ Branch 22 → 14 taken 12 times.
✓ Branch 22 → 23 taken 5 times.
|
17 |
for (size_t i = 0; i < values.size(); ++i) |
| 112 |
|
|
{ |
| 113 |
2/2
✓ Branch 14 → 15 taken 7 times.
✓ Branch 14 → 16 taken 5 times.
|
12 |
if (i > 0) |
| 114 |
|
|
{ |
| 115 |
1/2
✓ Branch 15 → 16 taken 7 times.
✗ Branch 15 → 34 not taken.
|
7 |
result += ", "; |
| 116 |
|
|
} |
| 117 |
2/4
✓ Branch 17 → 18 taken 12 times.
✗ Branch 17 → 33 not taken.
✓ Branch 18 → 19 taken 12 times.
✗ Branch 18 → 31 not taken.
|
12 |
result += format_hex(values[i], 2); |
| 118 |
|
|
} |
| 119 |
1/2
✓ Branch 23 → 24 taken 5 times.
✗ Branch 23 → 34 not taken.
|
5 |
result += ']'; |
| 120 |
|
5 |
return result; |
| 121 |
|
5 |
} |
| 122 |
|
|
|
| 123 |
|
|
/** @brief Formats a virtual key code as two hex digits, for example "0x72". */ |
| 124 |
|
3 |
[[nodiscard]] inline std::string format_vkcode(int vk_code) |
| 125 |
|
|
{ |
| 126 |
|
3 |
return format_hex(vk_code, 2); |
| 127 |
|
|
} |
| 128 |
|
|
|
| 129 |
|
|
/** @brief Formats virtual key codes as format_int_vector does. */ |
| 130 |
|
3 |
[[nodiscard]] inline std::string format_vkcode_list(const std::vector<int> &keys) |
| 131 |
|
|
{ |
| 132 |
|
3 |
return format_int_vector(keys); |
| 133 |
|
|
} |
| 134 |
|
|
|
| 135 |
|
|
} // namespace format |
| 136 |
|
|
} // namespace DetourModKit |
| 137 |
|
|
|
| 138 |
|
|
#endif // DETOURMODKIT_FORMAT_HPP |
| 139 |
|
|
|