diff --git a/src/wx/audio/internal/faudio.cpp b/src/wx/audio/internal/faudio.cpp index e9a7fcbf..f896f9b5 100644 --- a/src/wx/audio/internal/faudio.cpp +++ b/src/wx/audio/internal/faudio.cpp @@ -48,10 +48,7 @@ int FAGetDev(FAudio* fa) { if (hr != 0) { continue; } - const std::vector device_id_u8 = - config::utf16_to_utf8(reinterpret_cast(dd.DeviceID)); - const wxString device_id = wxString::FromUTF8( - reinterpret_cast(device_id_u8.data()), device_id_u8.size()); + const wxString device_id = config::utf16_to_utf8(dd.DeviceID); if (audio_device == device_id) { return i; } @@ -500,14 +497,8 @@ std::vector GetFAudioDevices() { } // Convert to UTF-8. - const std::vector display_name_u8 = - config::utf16_to_utf8(reinterpret_cast(dd.DisplayName)); - const std::vector device_id_u8 = - config::utf16_to_utf8(reinterpret_cast(dd.DeviceID)); - const wxString display_name = wxString::FromUTF8( - reinterpret_cast(display_name_u8.data()), display_name_u8.size()); - const wxString device_id = wxString::FromUTF8( - reinterpret_cast(device_id_u8.data()), device_id_u8.size()); + const wxString display_name = config::utf16_to_utf8(dd.DisplayName); + const wxString device_id = config::utf16_to_utf8(dd.DeviceID); devices.push_back({display_name, device_id}); } diff --git a/src/wx/config/strutils-test.cpp b/src/wx/config/strutils-test.cpp index 35da449f..45eb8109 100644 --- a/src/wx/config/strutils-test.cpp +++ b/src/wx/config/strutils-test.cpp @@ -96,8 +96,8 @@ TEST(StrSplitWithSepTest, MultipleSepTokens) { } TEST(UTF16ToUTF8Test, Basic) { - std::vector utf16 = {'f', 'o', 'o', 0}; - auto vec = config::utf16_to_utf8(utf16.data()); + const std::vector utf16 = {'f', 'o', 'o', 0}; + auto vec = config::utf16_to_utf8_vector(utf16.data()); ASSERT_EQ(vec.size(), 3); @@ -108,8 +108,8 @@ TEST(UTF16ToUTF8Test, Basic) { TEST(UTF16ToUTF8Test, MultiByte) { // U+20AC EURO SIGN. - std::vector utf16 = {0x20AC, 0}; - auto vec = config::utf16_to_utf8(utf16.data()); + const std::vector utf16 = {0x20AC, 0}; + auto vec = config::utf16_to_utf8_vector(utf16.data()); ASSERT_EQ(vec.size(), 3); @@ -118,10 +118,25 @@ TEST(UTF16ToUTF8Test, MultiByte) { EXPECT_EQ(vec[2], 0xAC); } +TEST(UTF16ToUTF8Test, DualMultiByte) { + // This is a variant of the above to test the buffer reset is done properly. + const std::vector utf16 = {0x20AC, 0x20AC, 0}; + auto vec = config::utf16_to_utf8_vector(utf16.data()); + + ASSERT_EQ(vec.size(), 6); + + EXPECT_EQ(vec[0], 0xE2); + EXPECT_EQ(vec[1], 0x82); + EXPECT_EQ(vec[2], 0xAC); + EXPECT_EQ(vec[3], 0xE2); + EXPECT_EQ(vec[4], 0x82); + EXPECT_EQ(vec[5], 0xAC); +} + TEST(UTF16ToUTF8Test, SurrogatePair) { // U+1F914 THINKING FACE. - std::vector utf16 = {0xD83E, 0xDD14, 0}; - auto vec = config::utf16_to_utf8(utf16.data()); + const std::vector utf16 = {0xD83E, 0xDD14, 0}; + auto vec = config::utf16_to_utf8_vector(utf16.data()); ASSERT_EQ(vec.size(), 4); @@ -133,26 +148,26 @@ TEST(UTF16ToUTF8Test, SurrogatePair) { TEST(UTF16ToUTF8Test, InvalidSurrogatePair) { // U+D800 HIGH SURROGATE. - std::vector utf16 = {0xD800, 0}; - EXPECT_DEATH(config::utf16_to_utf8(utf16.data()), ".*"); + const std::vector utf16 = {0xD800, 0}; + EXPECT_DEATH(config::utf16_to_utf8_vector(utf16.data()), ".*"); } TEST(UTF16ToUTF8Test, InvalidSurrogatePair2) { // U+D800 HIGH SURROGATE followed by U+0020 SPACE. - std::vector utf16 = {0xD800, 0x0020, 0}; - EXPECT_DEATH(config::utf16_to_utf8(utf16.data()), ".*"); + const std::vector utf16 = {0xD800, 0x0020, 0}; + EXPECT_DEATH(config::utf16_to_utf8_vector(utf16.data()), ".*"); } TEST(UTF16ToUTF8Test, InvalidSurrogatePair3) { // U+D800 HIGH SURROGATE followed by U+D800 HIGH SURROGATE. - std::vector utf16 = {0xD800, 0xD800, 0}; - EXPECT_DEATH(config::utf16_to_utf8(utf16.data()), ".*"); + const std::vector utf16 = {0xD800, 0xD800, 0}; + EXPECT_DEATH(config::utf16_to_utf8_vector(utf16.data()), ".*"); } TEST(UTF16ToUTF8Test, FullString) { // "foo€🤔" - std::vector utf16 = {'f', 'o', 'o', 0x20AC, 0xD83E, 0xDD14, 0}; - auto vec = config::utf16_to_utf8(utf16.data()); + const std::vector utf16 = {'f', 'o', 'o', 0x20AC, 0xD83E, 0xDD14, 0}; + auto vec = config::utf16_to_utf8_vector(utf16.data()); ASSERT_EQ(vec.size(), 10); diff --git a/src/wx/config/strutils.cpp b/src/wx/config/strutils.cpp index 0bf6227f..818ef168 100644 --- a/src/wx/config/strutils.cpp +++ b/src/wx/config/strutils.cpp @@ -41,10 +41,10 @@ wxArrayString str_split_with_sep(const wxString& text, const wxString& sep) return str_split(text, sep, true); } -std::vector utf16_to_utf8(const uint16_t* utf16) { +std::vector utf16_to_utf8_vector(const uint16_t* utf16) { std::vector out; for (size_t i = 0; utf16[i]; i++) { - uint16_t c = utf16[i]; + const uint16_t c = utf16[i]; if (c < 0x80) { out.push_back(c); } else if (c < 0x800) { @@ -77,4 +77,14 @@ std::vector utf16_to_utf8(const uint16_t* utf16) { return out; } +wxString utf16_to_utf8(const uint16_t* utf16) { + std::vector output_vector = utf16_to_utf8_vector(utf16); + return wxString::FromUTF8(reinterpret_cast(output_vector.data()), + output_vector.size()); +} + +wxString utf16_to_utf8(const int16_t* utf16) { + return utf16_to_utf8(reinterpret_cast(utf16)); +} + } // namespace config diff --git a/src/wx/config/strutils.h b/src/wx/config/strutils.h index 0a9f8308..839f6091 100644 --- a/src/wx/config/strutils.h +++ b/src/wx/config/strutils.h @@ -19,7 +19,11 @@ wxArrayString str_split_with_sep(const wxString& text, const wxString& sep); // Converts a null-terminated array of UTF-16 code units to a vector of UTF-8 code units. // This will assert if the input is not a valid UTF-16 string. -std::vector utf16_to_utf8(const uint16_t* utf16); +std::vector utf16_to_utf8_vector(const uint16_t* utf16); + +// Convenience functions to convert a null-terminated array of UTF-16 code units to a wxString. +wxString utf16_to_utf8(const uint16_t* utf16); +wxString utf16_to_utf8(const int16_t* utf16); } // namespace config