263 lines
9.0 KiB
C++
263 lines
9.0 KiB
C++
// Copyright (C) 2020 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
|
|
|
#if 0
|
|
// keep existing syncqt header working after the move of the class
|
|
// into qstringconverter_base
|
|
#pragma qt_class(QStringConverter)
|
|
#pragma qt_class(QStringConverterBase)
|
|
#endif
|
|
|
|
#ifndef QSTRINGCONVERTER_H
|
|
#define QSTRINGCONVERTER_H
|
|
|
|
#include <QtCore/qstringconverter_base.h>
|
|
#include <QtCore/qstring.h>
|
|
#include <QtCore/qstringbuilder.h>
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
class QStringEncoder : public QStringConverter
|
|
{
|
|
protected:
|
|
constexpr explicit QStringEncoder(const Interface *i) noexcept
|
|
: QStringConverter(i)
|
|
{}
|
|
public:
|
|
constexpr QStringEncoder() noexcept
|
|
: QStringConverter()
|
|
{}
|
|
constexpr explicit QStringEncoder(Encoding encoding, Flags flags = Flag::Default)
|
|
: QStringConverter(encoding, flags)
|
|
{}
|
|
explicit QStringEncoder(QAnyStringView name, Flags flags = Flag::Default)
|
|
: QStringConverter(name, flags)
|
|
{}
|
|
|
|
template<typename T>
|
|
struct DecodedData
|
|
{
|
|
QStringEncoder *encoder;
|
|
T data;
|
|
operator QByteArray() const { return encoder->encodeAsByteArray(data); }
|
|
};
|
|
Q_WEAK_OVERLOAD
|
|
DecodedData<const QString &> operator()(const QString &str)
|
|
{ return DecodedData<const QString &>{this, str}; }
|
|
DecodedData<QStringView> operator()(QStringView in)
|
|
{ return DecodedData<QStringView>{this, in}; }
|
|
Q_WEAK_OVERLOAD
|
|
DecodedData<const QString &> encode(const QString &str)
|
|
{ return DecodedData<const QString &>{this, str}; }
|
|
DecodedData<QStringView> encode(QStringView in)
|
|
{ return DecodedData<QStringView>{this, in}; }
|
|
|
|
qsizetype requiredSpace(qsizetype inputLength) const
|
|
{ return iface ? iface->fromUtf16Len(inputLength) : 0; }
|
|
char *appendToBuffer(char *out, QStringView in)
|
|
{
|
|
if (!iface) {
|
|
state.invalidChars = 1;
|
|
return out;
|
|
}
|
|
return iface->fromUtf16(out, in, &state);
|
|
}
|
|
private:
|
|
QByteArray encodeAsByteArray(QStringView in)
|
|
{
|
|
if (!iface) {
|
|
// ensure that hasError returns true
|
|
state.invalidChars = 1;
|
|
return {};
|
|
}
|
|
QByteArray result(iface->fromUtf16Len(in.size()), Qt::Uninitialized);
|
|
char *out = result.data();
|
|
out = iface->fromUtf16(out, in, &state);
|
|
result.truncate(out - result.constData());
|
|
return result;
|
|
}
|
|
|
|
};
|
|
|
|
class QStringDecoder : public QStringConverter
|
|
{
|
|
protected:
|
|
constexpr explicit QStringDecoder(const Interface *i) noexcept
|
|
: QStringConverter(i)
|
|
{}
|
|
public:
|
|
constexpr explicit QStringDecoder(Encoding encoding, Flags flags = Flag::Default)
|
|
: QStringConverter(encoding, flags)
|
|
{}
|
|
constexpr QStringDecoder() noexcept
|
|
: QStringConverter()
|
|
{}
|
|
explicit QStringDecoder(QAnyStringView name, Flags f = Flag::Default)
|
|
: QStringConverter(name, f)
|
|
{}
|
|
|
|
template<typename T>
|
|
struct EncodedData
|
|
{
|
|
QStringDecoder *decoder;
|
|
T data;
|
|
operator QString() const { return decoder->decodeAsString(data); }
|
|
};
|
|
Q_WEAK_OVERLOAD
|
|
EncodedData<const QByteArray &> operator()(const QByteArray &ba)
|
|
{ return EncodedData<const QByteArray &>{this, ba}; }
|
|
EncodedData<QByteArrayView> operator()(QByteArrayView ba)
|
|
{ return EncodedData<QByteArrayView>{this, ba}; }
|
|
Q_WEAK_OVERLOAD
|
|
EncodedData<const QByteArray &> decode(const QByteArray &ba)
|
|
{ return EncodedData<const QByteArray &>{this, ba}; }
|
|
EncodedData<QByteArrayView> decode(QByteArrayView ba)
|
|
{ return EncodedData<QByteArrayView>{this, ba}; }
|
|
|
|
qsizetype requiredSpace(qsizetype inputLength) const
|
|
{ return iface ? iface->toUtf16Len(inputLength) : 0; }
|
|
QChar *appendToBuffer(QChar *out, QByteArrayView ba)
|
|
{
|
|
if (!iface) {
|
|
state.invalidChars = 1;
|
|
return out;
|
|
}
|
|
return iface->toUtf16(out, ba, &state);
|
|
}
|
|
char16_t *appendToBuffer(char16_t *out, QByteArrayView ba)
|
|
{ return reinterpret_cast<char16_t *>(appendToBuffer(reinterpret_cast<QChar *>(out), ba)); }
|
|
|
|
Q_CORE_EXPORT static QStringDecoder decoderForHtml(QByteArrayView data);
|
|
|
|
private:
|
|
QString decodeAsString(QByteArrayView in)
|
|
{
|
|
if (!iface) {
|
|
// ensure that hasError returns true
|
|
state.invalidChars = 1;
|
|
return {};
|
|
}
|
|
QString result(iface->toUtf16Len(in.size()), Qt::Uninitialized);
|
|
const QChar *out = iface->toUtf16(result.data(), in, &state);
|
|
result.truncate(out - result.constData());
|
|
return result;
|
|
}
|
|
};
|
|
|
|
|
|
#if defined(QT_USE_FAST_OPERATOR_PLUS) || defined(QT_USE_QSTRINGBUILDER)
|
|
template <typename T>
|
|
struct QConcatenable<QStringDecoder::EncodedData<T>>
|
|
: private QAbstractConcatenable
|
|
{
|
|
typedef QChar type;
|
|
typedef QString ConvertTo;
|
|
enum { ExactSize = false };
|
|
static qsizetype size(const QStringDecoder::EncodedData<T> &s) { return s.decoder->requiredSpace(s.data.size()); }
|
|
static inline void appendTo(const QStringDecoder::EncodedData<T> &s, QChar *&out)
|
|
{
|
|
out = s.decoder->appendToBuffer(out, s.data);
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
struct QConcatenable<QStringEncoder::DecodedData<T>>
|
|
: private QAbstractConcatenable
|
|
{
|
|
typedef char type;
|
|
typedef QByteArray ConvertTo;
|
|
enum { ExactSize = false };
|
|
static qsizetype size(const QStringEncoder::DecodedData<T> &s) { return s.encoder->requiredSpace(s.data.size()); }
|
|
static inline void appendTo(const QStringEncoder::DecodedData<T> &s, char *&out)
|
|
{
|
|
out = s.encoder->appendToBuffer(out, s.data);
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
QString &operator+=(QString &a, const QStringDecoder::EncodedData<T> &b)
|
|
{
|
|
qsizetype len = a.size() + QConcatenable<QStringDecoder::EncodedData<T>>::size(b);
|
|
a.reserve(len);
|
|
QChar *it = a.data() + a.size();
|
|
QConcatenable<QStringDecoder::EncodedData<T>>::appendTo(b, it);
|
|
a.resize(qsizetype(it - a.constData())); //may be smaller than len
|
|
return a;
|
|
}
|
|
|
|
template <typename T>
|
|
QByteArray &operator+=(QByteArray &a, const QStringEncoder::DecodedData<T> &b)
|
|
{
|
|
qsizetype len = a.size() + QConcatenable<QStringEncoder::DecodedData<T>>::size(b);
|
|
a.reserve(len);
|
|
char *it = a.data() + a.size();
|
|
QConcatenable<QStringEncoder::DecodedData<T>>::appendTo(b, it);
|
|
a.resize(qsizetype(it - a.constData())); //may be smaller than len
|
|
return a;
|
|
}
|
|
#endif
|
|
|
|
template <typename InputIterator>
|
|
void QString::assign_helper_char8(InputIterator first, InputIterator last)
|
|
{
|
|
static_assert(!QString::is_contiguous_iterator_v<InputIterator>,
|
|
"Internal error: Should have been handed over to the QAnyStringView overload."
|
|
);
|
|
|
|
using ValueType = typename std::iterator_traits<InputIterator>::value_type;
|
|
constexpr bool IsFwdIt = std::is_convertible_v<
|
|
typename std::iterator_traits<InputIterator>::iterator_category,
|
|
std::forward_iterator_tag
|
|
>;
|
|
|
|
resize(0);
|
|
// In case of not being shared, there is the possibility of having free space at begin
|
|
// even after the resize to zero.
|
|
if (const auto offset = d.freeSpaceAtBegin())
|
|
d.setBegin(d.begin() - offset);
|
|
|
|
if constexpr (IsFwdIt)
|
|
reserve(static_cast<qsizetype>(std::distance(first, last)));
|
|
|
|
auto toUtf16 = QStringDecoder(QStringDecoder::Utf8);
|
|
auto availableCapacity = d.constAllocatedCapacity();
|
|
auto *dst = d.data();
|
|
auto *dend = d.data() + availableCapacity;
|
|
|
|
while (true) {
|
|
if (first == last) { // ran out of input elements
|
|
Q_ASSERT(!std::less<>{}(dend, dst));
|
|
d.size = dst - d.begin();
|
|
return;
|
|
}
|
|
const ValueType next = *first; // decays proxies, if any
|
|
const auto chunk = QUtf8StringView(&next, 1);
|
|
// UTF-8 characters can have a maximum size of 4 bytes and may result in a surrogate
|
|
// pair of UTF-16 code units. In the input-iterator case, we don't know the size
|
|
// and would need to always reserve space for 2 code units. To keep our promise
|
|
// of 'not allocating if it fits', we have to pre-check this condition.
|
|
// We know that it fits in the forward-iterator case.
|
|
if constexpr (!IsFwdIt) {
|
|
constexpr qsizetype Pair = 2;
|
|
char16_t buf[Pair];
|
|
const qptrdiff n = toUtf16.appendToBuffer(buf, chunk) - buf;
|
|
if (dend - dst < n) { // ran out of allocated memory
|
|
const auto offset = dst - d.begin();
|
|
reallocData(d.constAllocatedCapacity() + Pair, QArrayData::Grow);
|
|
// update the pointers since we've re-allocated
|
|
availableCapacity = d.constAllocatedCapacity();
|
|
dst = d.data() + offset;
|
|
dend = d.data() + availableCapacity;
|
|
}
|
|
dst = std::copy_n(buf, n, dst);
|
|
} else { // take the fast path
|
|
dst = toUtf16.appendToBuffer(dst, chunk);
|
|
}
|
|
++first;
|
|
}
|
|
}
|
|
|
|
QT_END_NAMESPACE
|
|
|
|
#endif
|