Merge branch 'mbconv-len-fix'

Return buffers of correct length from wxMBConv::cWC2MB() and cMB2WC().
This commit is contained in:
Vadim Zeitlin
2017-11-04 14:20:15 +01:00
4 changed files with 102 additions and 76 deletions

View File

@@ -71,11 +71,19 @@ public:
const wchar_t *src, size_t srcLen = wxNO_LEN) const; const wchar_t *src, size_t srcLen = wxNO_LEN) const;
// Convenience functions for translating NUL-terminated strings: returns // Convenience functions for translating NUL-terminated strings: return
// the buffer containing the converted string or NULL pointer if the // the buffer containing the converted string or empty buffer if the
// conversion failed. // conversion failed.
const wxWCharBuffer cMB2WC(const char *in) const; wxWCharBuffer cMB2WC(const char *in) const
const wxCharBuffer cWC2MB(const wchar_t *in) const; { return DoConvertMB2WC(in, wxNO_LEN); }
wxCharBuffer cWC2MB(const wchar_t *in) const
{ return DoConvertWC2MB(in, wxNO_LEN); }
wxWCharBuffer cMB2WC(const wxScopedCharBuffer& in) const
{ return DoConvertMB2WC(in, in.length()); }
wxCharBuffer cWC2MB(const wxScopedWCharBuffer& in) const
{ return DoConvertWC2MB(in, in.length()); }
// Convenience functions for converting strings which may contain embedded // Convenience functions for converting strings which may contain embedded
// NULs and don't have to be NUL-terminated. // NULs and don't have to be NUL-terminated.
@@ -92,28 +100,22 @@ public:
// number of characters converted, whether the last one of them was NUL or // number of characters converted, whether the last one of them was NUL or
// not. But if inLen == wxNO_LEN then outLen doesn't account for the last // not. But if inLen == wxNO_LEN then outLen doesn't account for the last
// NUL even though it is present. // NUL even though it is present.
const wxWCharBuffer wxWCharBuffer
cMB2WC(const char *in, size_t inLen, size_t *outLen) const; cMB2WC(const char *in, size_t inLen, size_t *outLen) const;
const wxCharBuffer wxCharBuffer
cWC2MB(const wchar_t *in, size_t inLen, size_t *outLen) const; cWC2MB(const wchar_t *in, size_t inLen, size_t *outLen) const;
// And yet more convenience functions for converting the entire buffers:
// these are the simplest and least error-prone as you never need to bother
// with lengths/sizes directly.
const wxWCharBuffer cMB2WC(const wxScopedCharBuffer& in) const;
const wxCharBuffer cWC2MB(const wxScopedWCharBuffer& in) const;
// convenience functions for converting MB or WC to/from wxWin default // convenience functions for converting MB or WC to/from wxWin default
#if wxUSE_UNICODE #if wxUSE_UNICODE
const wxWCharBuffer cMB2WX(const char *psz) const { return cMB2WC(psz); } wxWCharBuffer cMB2WX(const char *psz) const { return cMB2WC(psz); }
const wxCharBuffer cWX2MB(const wchar_t *psz) const { return cWC2MB(psz); } wxCharBuffer cWX2MB(const wchar_t *psz) const { return cWC2MB(psz); }
const wchar_t* cWC2WX(const wchar_t *psz) const { return psz; } const wchar_t* cWC2WX(const wchar_t *psz) const { return psz; }
const wchar_t* cWX2WC(const wchar_t *psz) const { return psz; } const wchar_t* cWX2WC(const wchar_t *psz) const { return psz; }
#else // ANSI #else // ANSI
const char* cMB2WX(const char *psz) const { return psz; } const char* cMB2WX(const char *psz) const { return psz; }
const char* cWX2MB(const char *psz) const { return psz; } const char* cWX2MB(const char *psz) const { return psz; }
const wxCharBuffer cWC2WX(const wchar_t *psz) const { return cWC2MB(psz); } wxCharBuffer cWC2WX(const wchar_t *psz) const { return cWC2MB(psz); }
const wxWCharBuffer cWX2WC(const char *psz) const { return cMB2WC(psz); } wxWCharBuffer cWX2WC(const char *psz) const { return cMB2WC(psz); }
#endif // Unicode/ANSI #endif // Unicode/ANSI
// this function is used in the implementation of cMB2WC() to distinguish // this function is used in the implementation of cMB2WC() to distinguish
@@ -162,7 +164,12 @@ public:
virtual wxMBConv *Clone() const = 0; virtual wxMBConv *Clone() const = 0;
// virtual dtor for any base class // virtual dtor for any base class
virtual ~wxMBConv(); virtual ~wxMBConv() { }
private:
// Common part of single argument cWC2MB() and cMB2WC() overloads above.
wxCharBuffer DoConvertWC2MB(const wchar_t* pwz, size_t srcLen) const;
wxWCharBuffer DoConvertMB2WC(const char* psz, size_t srcLen) const;
}; };
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View File

@@ -190,7 +190,7 @@ public:
invalid and @a outLen is set to 0 (and not @c wxCONV_FAILED for invalid and @a outLen is set to 0 (and not @c wxCONV_FAILED for
compatibility concerns). compatibility concerns).
*/ */
const wxWCharBuffer cMB2WC(const char* in, wxWCharBuffer cMB2WC(const char* in,
size_t inLen, size_t inLen,
size_t *outLen) const; size_t *outLen) const;
@@ -209,7 +209,7 @@ public:
@since 2.9.1 @since 2.9.1
*/ */
const wxWCharBuffer cMB2WC(const wxCharBuffer& buf) const; wxWCharBuffer cMB2WC(const wxCharBuffer& buf) const;
//@{ //@{
/** /**
@@ -221,7 +221,7 @@ public:
is defined as the correct return type (without const). is defined as the correct return type (without const).
*/ */
const char* cMB2WX(const char* psz) const; const char* cMB2WX(const char* psz) const;
const wxWCharBuffer cMB2WX(const char* psz) const; wxWCharBuffer cMB2WX(const char* psz) const;
//@} //@}
/** /**
@@ -234,7 +234,7 @@ public:
Its parameters have the same meaning as the corresponding parameters of Its parameters have the same meaning as the corresponding parameters of
FromWChar(), please see the description of cMB2WC() for more details. FromWChar(), please see the description of cMB2WC() for more details.
*/ */
const wxCharBuffer cWC2MB(const wchar_t* in, wxCharBuffer cWC2MB(const wchar_t* in,
size_t inLen, size_t inLen,
size_t *outLen) const; size_t *outLen) const;
@@ -253,7 +253,7 @@ public:
@since 2.9.1 @since 2.9.1
*/ */
const wxCharBuffer cWC2MB(const wxWCharBuffer& buf) const; wxCharBuffer cWC2MB(const wxWCharBuffer& buf) const;
//@{ //@{
/** /**
@@ -264,7 +264,7 @@ public:
defined as the correct return type (without const). defined as the correct return type (without const).
*/ */
const wchar_t* cWC2WX(const wchar_t* psz) const; const wchar_t* cWC2WX(const wchar_t* psz) const;
const wxCharBuffer cWC2WX(const wchar_t* psz) const; wxCharBuffer cWC2WX(const wchar_t* psz) const;
//@} //@}
//@{ //@{
@@ -276,7 +276,7 @@ public:
is defined as the correct return type (without const). is defined as the correct return type (without const).
*/ */
const char* cWX2MB(const wxChar* psz) const; const char* cWX2MB(const wxChar* psz) const;
const wxCharBuffer cWX2MB(const wxChar* psz) const; wxCharBuffer cWX2MB(const wxChar* psz) const;
//@} //@}
//@{ //@{
@@ -288,7 +288,7 @@ public:
defined as the correct return type (without const). defined as the correct return type (without const).
*/ */
const wchar_t* cWX2WC(const wxChar* psz) const; const wchar_t* cWX2WC(const wxChar* psz) const;
const wxWCharBuffer cWX2WC(const wxChar* psz) const; wxWCharBuffer cWX2WC(const wxChar* psz) const;
//@} //@}
/** /**

View File

@@ -394,48 +394,7 @@ size_t wxMBConv::WC2MB(char *outBuff, const wchar_t *inBuff, size_t outLen) cons
return rc; return rc;
} }
wxMBConv::~wxMBConv() wxWCharBuffer
{
// nothing to do here (necessary for Darwin linking probably)
}
const wxWCharBuffer wxMBConv::cMB2WC(const char *psz) const
{
if ( psz )
{
// calculate the length of the buffer needed first
const size_t nLen = ToWChar(NULL, 0, psz);
if ( nLen != wxCONV_FAILED )
{
// now do the actual conversion
wxWCharBuffer buf(nLen - 1 /* +1 added implicitly */);
// +1 for the trailing NULL
if ( ToWChar(buf.data(), nLen, psz) != wxCONV_FAILED )
return buf;
}
}
return wxWCharBuffer();
}
const wxCharBuffer wxMBConv::cWC2MB(const wchar_t *pwz) const
{
if ( pwz )
{
const size_t nLen = FromWChar(NULL, 0, pwz);
if ( nLen != wxCONV_FAILED )
{
wxCharBuffer buf(nLen - 1);
if ( FromWChar(buf.data(), nLen, pwz) != wxCONV_FAILED )
return buf;
}
}
return wxCharBuffer();
}
const wxWCharBuffer
wxMBConv::cMB2WC(const char *inBuff, size_t inLen, size_t *outLen) const wxMBConv::cMB2WC(const char *inBuff, size_t inLen, size_t *outLen) const
{ {
const size_t dstLen = ToWChar(NULL, 0, inBuff, inLen); const size_t dstLen = ToWChar(NULL, 0, inBuff, inLen);
@@ -470,7 +429,7 @@ wxMBConv::cMB2WC(const char *inBuff, size_t inLen, size_t *outLen) const
return wxWCharBuffer(); return wxWCharBuffer();
} }
const wxCharBuffer wxCharBuffer
wxMBConv::cWC2MB(const wchar_t *inBuff, size_t inLen, size_t *outLen) const wxMBConv::cWC2MB(const wchar_t *inBuff, size_t inLen, size_t *outLen) const
{ {
size_t dstLen = FromWChar(NULL, 0, inBuff, inLen); size_t dstLen = FromWChar(NULL, 0, inBuff, inLen);
@@ -511,10 +470,13 @@ wxMBConv::cWC2MB(const wchar_t *inBuff, size_t inLen, size_t *outLen) const
return wxCharBuffer(); return wxCharBuffer();
} }
const wxWCharBuffer wxMBConv::cMB2WC(const wxScopedCharBuffer& buf) const wxWCharBuffer wxMBConv::DoConvertMB2WC(const char* buf, size_t srcLen) const
{ {
const size_t srcLen = buf.length(); // Notice that converting NULL pointer should work, i.e. return an empty
if ( srcLen ) // buffer instead of crashing, so we need to check both the length and the
// pointer because length is wxNO_LEN if it's a raw pointer and doesn't
// come from wxScopedCharBuffer.
if ( srcLen && buf )
{ {
const size_t dstLen = ToWChar(NULL, 0, buf, srcLen); const size_t dstLen = ToWChar(NULL, 0, buf, srcLen);
if ( dstLen != wxCONV_FAILED ) if ( dstLen != wxCONV_FAILED )
@@ -522,17 +484,24 @@ const wxWCharBuffer wxMBConv::cMB2WC(const wxScopedCharBuffer& buf) const
wxWCharBuffer wbuf(dstLen); wxWCharBuffer wbuf(dstLen);
wbuf.data()[dstLen] = L'\0'; wbuf.data()[dstLen] = L'\0';
if ( ToWChar(wbuf.data(), dstLen, buf, srcLen) != wxCONV_FAILED ) if ( ToWChar(wbuf.data(), dstLen, buf, srcLen) != wxCONV_FAILED )
{
// If the input string was NUL-terminated, we shouldn't include
// the length of the trailing NUL into the length of the return
// value.
if ( srcLen == wxNO_LEN )
wbuf.shrink(dstLen - 1);
return wbuf; return wbuf;
}
} }
} }
return wxScopedWCharBuffer::CreateNonOwned(L"", 0); return wxWCharBuffer();
} }
const wxCharBuffer wxMBConv::cWC2MB(const wxScopedWCharBuffer& wbuf) const wxCharBuffer wxMBConv::DoConvertWC2MB(const wchar_t* wbuf, size_t srcLen) const
{ {
const size_t srcLen = wbuf.length(); if ( srcLen && wbuf )
if ( srcLen )
{ {
const size_t dstLen = FromWChar(NULL, 0, wbuf, srcLen); const size_t dstLen = FromWChar(NULL, 0, wbuf, srcLen);
if ( dstLen != wxCONV_FAILED ) if ( dstLen != wxCONV_FAILED )
@@ -540,11 +509,18 @@ const wxCharBuffer wxMBConv::cWC2MB(const wxScopedWCharBuffer& wbuf) const
wxCharBuffer buf(dstLen); wxCharBuffer buf(dstLen);
buf.data()[dstLen] = '\0'; buf.data()[dstLen] = '\0';
if ( FromWChar(buf.data(), dstLen, wbuf, srcLen) != wxCONV_FAILED ) if ( FromWChar(buf.data(), dstLen, wbuf, srcLen) != wxCONV_FAILED )
{
// As above, in DoConvertMB2WC(), except that the length of the
// trailing NUL is variable in this case.
if ( srcLen == wxNO_LEN )
buf.shrink(dstLen - GetMBNulLen());
return buf; return buf;
}
} }
} }
return wxScopedCharBuffer::CreateNonOwned("", 0); return wxCharBuffer();
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View File

@@ -874,6 +874,16 @@ void MBConvTestCase::BufSize()
CPPUNIT_ASSERT( CPPUNIT_ASSERT(
convUTF16.WC2MB(buf.data(), utf16text, lenMB + 3) != wxCONV_FAILED ); convUTF16.WC2MB(buf.data(), utf16text, lenMB + 3) != wxCONV_FAILED );
CPPUNIT_ASSERT_EQUAL( '?', buf[lenMB + 2] ); CPPUNIT_ASSERT_EQUAL( '?', buf[lenMB + 2] );
// Test cWC2MB() too.
const wxCharBuffer buf2 = convUTF16.cWC2MB(utf16text);
CHECK( buf2.length() == lenMB );
CHECK( memcmp(buf, buf2, lenMB) == 0 );
const wxWCharBuffer utf16buf = wxWCharBuffer::CreateNonOwned(utf16text);
const wxCharBuffer buf3 = convUTF16.cWC2MB(utf16buf);
CHECK( buf3.length() == lenMB );
CHECK( memcmp(buf, buf3, lenMB) == 0 );
} }
void MBConvTestCase::FromWCharTests() void MBConvTestCase::FromWCharTests()
@@ -1448,3 +1458,36 @@ void MBConvTestCase::UTF8(const char *charSequence,
} }
#endif // HAVE_WCHAR_H #endif // HAVE_WCHAR_H
TEST_CASE("wxMBConv::cWC2MB", "[mbconv][wc2mb]")
{
wxMBConvUTF16 convUTF16;
CHECK( convUTF16.cWC2MB(L"").length() == 0 );
CHECK( convUTF16.cWC2MB(wxWCharBuffer()).length() == 0 );
CHECK( convUTF16.cWC2MB(L"Hi").length() == 4 );
CHECK( convUTF16.cWC2MB(wxWCharBuffer::CreateNonOwned(L"Hi")).length() == 4 );
CHECK( wxConvUTF7.cWC2MB(L"").length() == 0 );
CHECK( wxConvUTF7.cWC2MB(wxWCharBuffer()).length() == 0 );
CHECK( wxConvUTF7.cWC2MB(L"\xa3").length() == 5 );
// This test currently fails, the returned value is 3 because the
// conversion object doesn't return to its unshifted state -- which is
// probably a bug in wxMBConvUTF7.
// TODO: fix it there and reenable the test.
CHECK_NOFAIL( wxConvUTF7.cWC2MB(wxWCharBuffer::CreateNonOwned(L"\xa3")).length() == 5 );
}
TEST_CASE("wxMBConv::cMB2WC", "[mbconv][mb2wc]")
{
wxMBConvUTF16 convUTF16;
CHECK( convUTF16.cMB2WC("\0").length() == 0 );
CHECK( convUTF16.cMB2WC(wxCharBuffer()).length() == 0 );
CHECK( convUTF16.cMB2WC("H\0i\0").length() == 2 );
CHECK( convUTF16.cMB2WC(wxCharBuffer::CreateNonOwned("H\0i\0", 4)).length() == 2 );
CHECK( wxConvUTF7.cMB2WC("").length() == 0 );
CHECK( wxConvUTF7.cMB2WC(wxCharBuffer()).length() == 0 );
CHECK( wxConvUTF7.cMB2WC("+AKM-").length() == 1 );
}