./
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
24 / 27
88.9%
Branches:
24 / 34
70.6%
Functions:
2 / 2
100.0%

src/internal/utf8_conversion.hpp
Line Branch Exec Source
1 #ifndef DETOURMODKIT_INTERNAL_UTF8_CONVERSION_HPP
2 #define DETOURMODKIT_INTERNAL_UTF8_CONVERSION_HPP
3
4 /**
5 * @file utf8_conversion.hpp
6 * @brief UTF-8 and UTF-16 conversion for paths and names. No conversion uses the ANSI code page.
7 */
8
9 #include <windows.h>
10
11 #include <climits>
12 #include <cstddef>
13 #include <string>
14 #include <string_view>
15
16 namespace DetourModKit
17 {
18 namespace detail
19 {
20 /**
21 * @brief Widens strict UTF-8 text to UTF-16.
22 * @details Reads exactly @c text.size() bytes, so a view without a terminator is safe.
23 * @param text The UTF-8 text.
24 * @return The UTF-16 text. The result is empty for empty input, an embedded NUL, a size over INT_MAX, or
25 * ill-formed UTF-8.
26 * @throws std::bad_alloc when the result cannot be allocated.
27 */
28 4483 [[nodiscard]] inline std::wstring widen_utf8(std::string_view text)
29 {
30
7/8
✓ Branch 3 → 4 taken 4479 times.
✓ Branch 3 → 8 taken 3 times.
✓ Branch 5 → 6 taken 4479 times.
✗ Branch 5 → 8 not taken.
✓ Branch 7 → 8 taken 4 times.
✓ Branch 7 → 9 taken 4475 times.
✓ Branch 10 → 11 taken 7 times.
✓ Branch 10 → 12 taken 4475 times.
8962 if (text.empty() || text.size() > static_cast<std::size_t>(INT_MAX) ||
31 4479 text.find('\0') != std::string_view::npos)
32 {
33 7 return std::wstring{};
34 }
35
36 4475 const int input_length = static_cast<int>(text.size());
37 const int wide_length =
38
1/2
✓ Branch 14 → 15 taken 4475 times.
✗ Branch 14 → 35 not taken.
4475 ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, text.data(), input_length, nullptr, 0);
39
2/2
✓ Branch 15 → 16 taken 13 times.
✓ Branch 15 → 17 taken 4462 times.
4475 if (wide_length <= 0)
40 {
41 13 return std::wstring{};
42 }
43
2/2
✓ Branch 19 → 20 taken 4460 times.
✓ Branch 19 → 30 taken 3 times.
4465 std::wstring wide(static_cast<std::size_t>(wide_length), L'\0');
44
1/2
✓ Branch 23 → 24 taken 4460 times.
✗ Branch 23 → 33 not taken.
4460 const int converted = ::MultiByteToWideChar(
45 CP_UTF8,
46 MB_ERR_INVALID_CHARS,
47 text.data(),
48 input_length,
49 wide.data(),
50 wide_length
51 );
52
1/2
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 26 taken 4460 times.
4460 if (converted != wide_length)
53 {
54 ✗ return std::wstring{};
55 }
56 4460 return wide;
57 4460 }
58
59 /**
60 * @brief Renders UTF-16 text as UTF-8.
61 * @details An unpaired surrogate becomes U+FFFD, so the conversion does not fail on content.
62 * @param wide The UTF-16 text.
63 * @return The UTF-8 text. The result is empty for empty input or a size over INT_MAX.
64 * @throws std::bad_alloc when the result cannot be allocated.
65 */
66 969 [[nodiscard]] inline std::string utf8_from_wide(std::wstring_view wide)
67 {
68
5/6
✓ Branch 3 → 4 taken 965 times.
✓ Branch 3 → 6 taken 4 times.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 965 times.
✓ Branch 8 → 9 taken 4 times.
✓ Branch 8 → 10 taken 965 times.
969 if (wide.empty() || wide.size() > static_cast<std::size_t>(INT_MAX))
69 {
70 4 return std::string{};
71 }
72
73 965 const int input_length = static_cast<int>(wide.size());
74 const int byte_length =
75
1/2
✓ Branch 12 → 13 taken 965 times.
✗ Branch 12 → 33 not taken.
965 ::WideCharToMultiByte(CP_UTF8, 0, wide.data(), input_length, nullptr, 0, nullptr, nullptr);
76
1/2
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 965 times.
965 if (byte_length <= 0)
77 {
78 ✗ return std::string{};
79 }
80
1/2
✓ Branch 17 → 18 taken 965 times.
✗ Branch 17 → 28 not taken.
965 std::string text(static_cast<std::size_t>(byte_length), '\0');
81
1/2
✓ Branch 21 → 22 taken 965 times.
✗ Branch 21 → 31 not taken.
965 const int converted = ::WideCharToMultiByte(
82 CP_UTF8,
83 0,
84 wide.data(),
85 input_length,
86 text.data(),
87 byte_length,
88 nullptr,
89 nullptr
90 );
91
1/2
✗ Branch 22 → 23 not taken.
✓ Branch 22 → 24 taken 965 times.
965 if (converted != byte_length)
92 {
93 ✗ return std::string{};
94 }
95 965 return text;
96 965 }
97 } // namespace detail
98 } // namespace DetourModKit
99
100 #endif // DETOURMODKIT_INTERNAL_UTF8_CONVERSION_HPP
101