QLocal8Bit::convertToUnicode[win]: split out buffer growing

We will need to potentially grow the buffer before appending anything
to it, because if we pass in 0 as a size then the MultiByteToWideChar
just returns the size we would need. If we didn't intend to do so then
we would increment our output buffers even though nothing is written.

And when appending single characters (like the replacement character
for an invalid sequence) we need to grow the buffer as well.

We'll need this all in the next commit.

Pick-to: 6.6 6.5
Task-number: QTBUG-105105
Change-Id: I94b9a0f7d18a725da01a47398163e6d0f704eefc
Reviewed-by: Ievgenii Meshcheriakov <ievgenii.meshcheriakov@qt.io>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
(cherry picked from commit 1090d5dd4ae5be898d4566314eda43b0283709d9)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Mårten Nordheim 2023-11-06 17:13:39 +01:00
parent a7367e6a3c
commit 0c944d361a

View File

@ -1333,11 +1333,35 @@ QString QLocal8Bit::convertToUnicode_sys(QByteArrayView in, quint32 codePage,
Q_ASSERT(mblen > 0);
Q_ASSERT(!state || state->remainingChars == 0);
// Return a pointer to storage where we have enough space for `size`
const auto growOut = [&](qsizetype size) -> std::tuple<wchar_t *, qsizetype> {
if (outlen >= size)
return {out, outlen};
const bool wasStackBuffer = sp.isEmpty();
const auto begin = wasStackBuffer ? buf.data() : reinterpret_cast<wchar_t *>(sp.data());
const qsizetype offset = qsizetype(std::distance(begin, out));
qsizetype newSize = 0;
if (Q_UNLIKELY(qAddOverflow(offset, size, &newSize))) {
Q_CHECK_PTR(false);
return {nullptr, 0};
}
sp.resize(newSize);
auto it = reinterpret_cast<wchar_t *>(sp.data());
if (wasStackBuffer)
it = std::copy_n(buf.data(), offset, it);
else
it += offset;
return {it, size};
};
constexpr int MaxStep = std::numeric_limits<int>::max();
const char *end = mb + mblen;
while (mb != end) {
const int nextIn = int(std::min(qsizetype(mblen), qsizetype(MaxStep)));
const int nextOut = int(std::min(outlen, qsizetype(MaxStep)));
std::tie(out, outlen) = growOut(1); // Need space for at least one character
if (!out)
return {};
len = MultiByteToWideChar(codePage, MB_ERR_INVALID_CHARS, mb, nextIn, out, nextOut);
if (len) {
mb += nextIn;
@ -1349,14 +1373,9 @@ QString QLocal8Bit::convertToUnicode_sys(QByteArrayView in, quint32 codePage,
if (r == ERROR_INSUFFICIENT_BUFFER) {
Q_ASSERT(QtPrivate::q_points_into_range(out, buf.data(), buf.data() + buf.size()));
const int wclen = MultiByteToWideChar(codePage, 0, mb, nextIn, 0, 0);
auto begin = buf.data();
const qsizetype offset = qsizetype(std::distance(begin, out));
qsizetype newSize = offset + wclen;
sp.resize(newSize);
auto it = reinterpret_cast<wchar_t *>(sp.data());
it = std::copy_n(buf.data(), offset, it);
out = it;
outlen = wclen;
std::tie(out, outlen) = growOut(wclen);
if (!out)
return {};
} else if (r == ERROR_NO_UNICODE_TRANSLATION && state
&& state->remainingChars < q20::ssize(state->state_data)) {
++state->remainingChars;