GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 95.7% 22 / 0 / 23
Functions: 100.0% 3 / 0 / 3
Branches: 80.0% 16 / 0 / 20

src/internal/module_name.hpp
Line Branch Exec Source
1 #ifndef DETOURMODKIT_INTERNAL_MODULE_NAME_HPP
2 #define DETOURMODKIT_INTERNAL_MODULE_NAME_HPP
3
4 /**
5 * @file module_name.hpp
6 * @brief Shared bounded UTF-8 -> UTF-16 widening for module-name lookups.
7 */
8
9 #include <windows.h>
10
11 #include <climits>
12 #include <cstddef>
13 #include <new>
14 #include <string>
15 #include <string_view>
16
17 namespace DetourModKit
18 {
19 namespace detail
20 {
21 /**
22 * @brief Reports whether a module-name byte count fits the Win32 conversion API.
23 * @param length The byte count to test.
24 * @return True when @p length can be represented by the converter's signed count.
25 */
26 163 [[nodiscard]] constexpr bool module_name_length_fits_win32(std::size_t length) noexcept
27 {
28 163 return length <= static_cast<std::size_t>(INT_MAX);
29 }
30
31 /**
32 * @brief Widens a pointer/count UTF-8 module name after validating its Win32 length.
33 * @details The length check precedes construction of a view and every access through @p data. Embedded NUL and
34 * malformed UTF-8 are rejected because GetModuleHandleW would otherwise observe a different name.
35 * @param data The first name byte; may be null only when @p length is zero or oversized.
36 * @param length The number of name bytes.
37 * @return The UTF-16 name, or an empty string when validation, conversion, or allocation fails.
38 * @pre When @p length is nonzero and at most INT_MAX, @p data addresses @p length readable bytes.
39 */
40 164 [[nodiscard]] inline std::wstring widen_module_name_bytes(const char *data, std::size_t length) noexcept
41 {
42
7/8
✓ Branch 2 → 3 taken 163 times.
✓ Branch 2 → 6 taken 1 time.
✓ Branch 4 → 5 taken 162 times.
✓ Branch 4 → 6 taken 1 time.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 162 times.
✓ Branch 8 → 9 taken 2 times.
✓ Branch 8 → 10 taken 162 times.
164 if (length == 0 || !module_name_length_fits_win32(length) || data == nullptr)
43 {
44 2 return std::wstring{};
45 }
46
47 162 const std::string_view name{data, length};
48
2/2
✓ Branch 12 → 13 taken 3 times.
✓ Branch 12 → 14 taken 159 times.
162 if (name.find('\0') != std::string_view::npos)
49 {
50 3 return std::wstring{};
51 }
52
53 159 const int input_length = static_cast<int>(length);
54 const int wide_length =
55 159 ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, data, input_length, nullptr, 0);
56
2/2
✓ Branch 15 → 16 taken 1 time.
✓ Branch 15 → 17 taken 158 times.
159 if (wide_length <= 0)
57 {
58 1 return std::wstring{};
59 }
60 try
61 {
62
2/2
✓ Branch 19 → 20 taken 157 times.
✓ Branch 19 → 30 taken 1 time.
159 std::wstring wide_name(static_cast<std::size_t>(wide_length), L'\0');
63
1/2
✓ Branch 22 → 23 taken 157 times.
✗ Branch 22 → 33 not taken.
157 const int converted = ::MultiByteToWideChar(
64 CP_UTF8,
65 MB_ERR_INVALID_CHARS,
66 data,
67 input_length,
68 wide_name.data(),
69 wide_length
70 );
71
1/2
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 25 taken 157 times.
157 if (converted != wide_length)
72 {
73 return std::wstring{};
74 }
75 157 return wide_name;
76 157 }
77
1/2
✗ Branch 36 → 37 not taken.
✓ Branch 36 → 38 taken 1 time.
1 catch (const std::bad_alloc &)
78 {
79 1 return std::wstring{};
80 1 }
81 }
82
83 /**
84 * @brief Widens a UTF-8 module name to the UTF-16 the Win32 module APIs expect.
85 * @details Reads exactly @c name.size() bytes, so a non-NUL-terminated view is safe.
86 * @param name The bounded UTF-8 module name.
87 * @return The UTF-16 name, or an empty string when validation, conversion, or allocation fails.
88 */
89 163 [[nodiscard]] inline std::wstring widen_module_name(std::string_view name) noexcept
90 {
91 163 return widen_module_name_bytes(name.data(), name.size());
92 }
93 } // namespace detail
94 } // namespace DetourModKit
95
96 #endif // DETOURMODKIT_INTERNAL_MODULE_NAME_HPP
97