diff --git a/include/wx/string.h b/include/wx/string.h index 95e9a4a3bf..625be23253 100644 --- a/include/wx/string.h +++ b/include/wx/string.h @@ -1745,6 +1745,20 @@ public: return FromImpl(std::move(utf8)); } + void AssignFromUTF8Unchecked(const char *utf8, size_t len = npos) + { + m_impl.assign(utf8, len == npos ? strlen(utf8) : len); + } + void AssignFromUTF8(const char *utf8, size_t len = npos) + { + if ( !utf8 || !wxStringOperations::IsValidUtf8String(utf8) ) + { + clear(); + return; + } + AssignFromUTF8Unchecked(utf8, len); + } + std::string utf8_string() const { return m_impl; } const wxScopedCharBuffer utf8_str() const @@ -1768,6 +1782,56 @@ public: static wxString FromUTF8Unchecked(const std::string& utf8) { return FromUTF8Unchecked(utf8.c_str(), utf8.length()); } + void AssignFromUTF8Unchecked(const char *utf8, size_t len = npos) + { + if ( len == npos ) + len = strlen(utf8); + + wxMBConvStrictUTF8 conv; + if ( m_impl.size() > len ) + { + // We can be sure that the conversion result fits into the + // existing buffer, so use it directly. + m_impl.resize(conv.ToWChar(ImplData(), m_impl.size(), utf8, len)); + } + else + { + // We can't be sure that the conversion result fits into the + // existing buffer, so compute the length we need. + m_impl.resize(conv.ToWChar(nullptr, 0, utf8, len)); + conv.ToWChar(ImplData(), m_impl.size(), utf8, len); + } + } + void AssignFromUTF8(const char *utf8, size_t len = npos) + { + if ( !utf8 ) + { + clear(); + return; + } + + if ( len == npos ) + len = strlen(utf8); + + wxMBConvStrictUTF8 conv; + if ( m_impl.size() > len ) + { + m_impl.resize(conv.ToWChar(ImplData(), m_impl.size(), utf8, len)); + } + else + { + const auto needed = conv.ToWChar(nullptr, 0, utf8, len); + if ( needed == wxCONV_FAILED ) + { + clear(); + return; + } + + m_impl.resize(needed); + conv.ToWChar(ImplData(), m_impl.size(), utf8, len); + } + } + std::string utf8_string() const { return ToStdString(wxMBConvUTF8()); } const wxScopedCharBuffer utf8_str() const { return mb_str(wxMBConvUTF8()); } #endif // wxUSE_UNICODE_UTF8/wxUSE_UNICODE_WCHAR @@ -3656,6 +3720,14 @@ private: private: wxStringImpl m_impl; + // Get access to the string buffer: we assume that we can always rely on + // C++17 semantics of data(), even when not using C++17, which seems + // reasonable as C++17 mostly standardized existing practice. + wxStringCharType* ImplData() + { + return const_cast(m_impl.data()); + } + // buffers for compatibility conversion from (char*)c_str() and // (wchar_t*)c_str(): the pointers returned by these functions should remain // valid until the string itself is modified for compatibility with the diff --git a/interface/wx/string.h b/interface/wx/string.h index 3f1cf6f3ff..afc3c0d4dd 100644 --- a/interface/wx/string.h +++ b/interface/wx/string.h @@ -527,6 +527,34 @@ public: */ wxString operator =(wxUniChar c); + /** + Assignment from UTF-8 string. + + Calling `s.AssignFromUTF8(utf8, len) is equivalent to doing + `s = wxString::FromUTF8(utf8, len)` but may be more efficient as it can + reuse the existing string buffer instead of always having to allocate a + new one. + + This function can be useful in performance-sensitive loops or with + static variables (retaining their buffer between calls) in often called + functions. + + @since 3.3.2 + */ + void AssignFromUTF8(const char *utf8, size_t len = npos); + + /** + Assignment from UTF-8 string. + + This function is the same as AssignFromUTF8() but doesn't check that + @a utf8 is a valid pointer to a valid UTF-8 string. It must not be + called if @a utf8 is @NULL or its contents is not already known to be + correct UTF-8. + + @since 3.3.2 + */ + void AssignFromUTF8Unchecked(const char *utf8, size_t len = npos); + ///@} @@ -2018,6 +2046,8 @@ public: The overload taking @c std::string_view is only available starting with wxWidgets 3.3.0 and requires the consumer application to use C++17. + @see AssignFromUTF8() + @since 2.8.4 */ static wxString FromUTF8(const char* s); @@ -2046,6 +2076,8 @@ public: The overload taking @c std::string_view is only available starting with wxWidgets 3.3.0 and requires the consumer application to use C++17. + @see AssignFromUTF8Unchecked() + @since 2.8.9 */ static wxString FromUTF8Unchecked(const char* s); diff --git a/tests/strings/strings.cpp b/tests/strings/strings.cpp index 94f77a19b5..7e0d2466b4 100644 --- a/tests/strings/strings.cpp +++ b/tests/strings/strings.cpp @@ -192,6 +192,22 @@ TEST_CASE("StringStaticConstructors", "[wxString]") //CHECK( wxString::FromUTF8("", 1).length() == 1 ); } +TEST_CASE("StringAssignUTF8", "[wxString]") +{ + wxString s; + s.AssignFromUTF8("Oberfläche"); + CHECK( s == wxString::FromUTF8("Oberfläche") ); + + s.AssignFromUTF8("fläche"); + CHECK( s == wxString::FromUTF8("fläche") ); + + s.AssignFromUTF8("Even longer than Oberfläche"); + CHECK( s == wxString::FromUTF8("Even longer than Oberfläche") ); + + s.AssignFromUTF8(nullptr); + CHECK( s == wxString() ); +} + TEST_CASE("StringExtraction", "[wxString]") { wxString s(wxT("Hello, world!"));