| 1 | // Copyright (C) 2020 The Qt Company Ltd. |
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
| 3 | |
| 4 | #if 0 |
| 5 | // keep existing syncqt header working after the move of the class |
| 6 | // into qstringconverter_base |
| 7 | #pragma qt_class(QStringConverter) |
| 8 | #pragma qt_class(QStringConverterBase) |
| 9 | #endif |
| 10 | |
| 11 | #ifndef QSTRINGCONVERTER_H |
| 12 | #define QSTRINGCONVERTER_H |
| 13 | |
| 14 | #include <QtCore/qstringconverter_base.h> |
| 15 | #include <QtCore/qstring.h> |
| 16 | #include <QtCore/qstringbuilder.h> |
| 17 | |
| 18 | QT_BEGIN_NAMESPACE |
| 19 | |
| 20 | class QStringEncoder : public QStringConverter |
| 21 | { |
| 22 | protected: |
| 23 | constexpr explicit QStringEncoder(const Interface *i) noexcept |
| 24 | : QStringConverter(i) |
| 25 | {} |
| 26 | public: |
| 27 | constexpr QStringEncoder() noexcept |
| 28 | : QStringConverter() |
| 29 | {} |
| 30 | constexpr explicit QStringEncoder(Encoding encoding, Flags flags = Flag::Default) |
| 31 | : QStringConverter(encoding, flags) |
| 32 | {} |
| 33 | explicit QStringEncoder(QAnyStringView name, Flags flags = Flag::Default) |
| 34 | : QStringConverter(name, flags) |
| 35 | {} |
| 36 | |
| 37 | template<typename T> |
| 38 | struct DecodedData |
| 39 | { |
| 40 | QStringEncoder *encoder; |
| 41 | T data; |
| 42 | operator QByteArray() const { return encoder->encodeAsByteArray(in: data); } |
| 43 | }; |
| 44 | Q_WEAK_OVERLOAD |
| 45 | DecodedData<const QString &> operator()(const QString &str) |
| 46 | { return DecodedData<const QString &>{.encoder: this, .data: str}; } |
| 47 | DecodedData<QStringView> operator()(QStringView in) |
| 48 | { return DecodedData<QStringView>{.encoder: this, .data: in}; } |
| 49 | Q_WEAK_OVERLOAD |
| 50 | DecodedData<const QString &> encode(const QString &str) |
| 51 | { return DecodedData<const QString &>{.encoder: this, .data: str}; } |
| 52 | DecodedData<QStringView> encode(QStringView in) |
| 53 | { return DecodedData<QStringView>{.encoder: this, .data: in}; } |
| 54 | |
| 55 | qsizetype requiredSpace(qsizetype inputLength) const |
| 56 | { return iface ? iface->fromUtf16Len(inputLength) : 0; } |
| 57 | char *appendToBuffer(char *out, QStringView in) |
| 58 | { |
| 59 | if (!iface) { |
| 60 | state.invalidChars = 1; |
| 61 | return out; |
| 62 | } |
| 63 | return iface->fromUtf16(out, in, &state); |
| 64 | } |
| 65 | private: |
| 66 | QByteArray encodeAsByteArray(QStringView in) |
| 67 | { |
| 68 | if (!iface) { |
| 69 | // ensure that hasError returns true |
| 70 | state.invalidChars = 1; |
| 71 | return {}; |
| 72 | } |
| 73 | QByteArray result(iface->fromUtf16Len(in.size()), Qt::Uninitialized); |
| 74 | char *out = result.data(); |
| 75 | out = iface->fromUtf16(out, in, &state); |
| 76 | result.truncate(pos: out - result.constData()); |
| 77 | return result; |
| 78 | } |
| 79 | |
| 80 | }; |
| 81 | |
| 82 | class QStringDecoder : public QStringConverter |
| 83 | { |
| 84 | protected: |
| 85 | constexpr explicit QStringDecoder(const Interface *i) noexcept |
| 86 | : QStringConverter(i) |
| 87 | {} |
| 88 | public: |
| 89 | constexpr explicit QStringDecoder(Encoding encoding, Flags flags = Flag::Default) |
| 90 | : QStringConverter(encoding, flags) |
| 91 | {} |
| 92 | constexpr QStringDecoder() noexcept |
| 93 | : QStringConverter() |
| 94 | {} |
| 95 | explicit QStringDecoder(QAnyStringView name, Flags f = Flag::Default) |
| 96 | : QStringConverter(name, f) |
| 97 | {} |
| 98 | |
| 99 | template<typename T> |
| 100 | struct EncodedData |
| 101 | { |
| 102 | QStringDecoder *decoder; |
| 103 | T data; |
| 104 | operator QString() const { return decoder->decodeAsString(in: data); } |
| 105 | }; |
| 106 | Q_WEAK_OVERLOAD |
| 107 | EncodedData<const QByteArray &> operator()(const QByteArray &ba) |
| 108 | { return EncodedData<const QByteArray &>{.decoder: this, .data: ba}; } |
| 109 | EncodedData<QByteArrayView> operator()(QByteArrayView ba) |
| 110 | { return EncodedData<QByteArrayView>{.decoder: this, .data: ba}; } |
| 111 | Q_WEAK_OVERLOAD |
| 112 | EncodedData<const QByteArray &> decode(const QByteArray &ba) |
| 113 | { return EncodedData<const QByteArray &>{.decoder: this, .data: ba}; } |
| 114 | EncodedData<QByteArrayView> decode(QByteArrayView ba) |
| 115 | { return EncodedData<QByteArrayView>{.decoder: this, .data: ba}; } |
| 116 | |
| 117 | qsizetype requiredSpace(qsizetype inputLength) const |
| 118 | { return iface ? iface->toUtf16Len(inputLength) : 0; } |
| 119 | QChar *appendToBuffer(QChar *out, QByteArrayView ba) |
| 120 | { |
| 121 | if (!iface) { |
| 122 | state.invalidChars = 1; |
| 123 | return out; |
| 124 | } |
| 125 | return iface->toUtf16(out, ba, &state); |
| 126 | } |
| 127 | char16_t *appendToBuffer(char16_t *out, QByteArrayView ba) |
| 128 | { return reinterpret_cast<char16_t *>(appendToBuffer(out: reinterpret_cast<QChar *>(out), ba)); } |
| 129 | |
| 130 | Q_CORE_EXPORT static QStringDecoder decoderForHtml(QByteArrayView data); |
| 131 | |
| 132 | private: |
| 133 | QString decodeAsString(QByteArrayView in) |
| 134 | { |
| 135 | if (!iface) { |
| 136 | // ensure that hasError returns true |
| 137 | state.invalidChars = 1; |
| 138 | return {}; |
| 139 | } |
| 140 | QString result(iface->toUtf16Len(in.size()), Qt::Uninitialized); |
| 141 | const QChar *out = iface->toUtf16(result.data(), in, &state); |
| 142 | result.truncate(pos: out - result.constData()); |
| 143 | return result; |
| 144 | } |
| 145 | }; |
| 146 | |
| 147 | |
| 148 | #if defined(QT_USE_FAST_OPERATOR_PLUS) || defined(QT_USE_QSTRINGBUILDER) |
| 149 | template <typename T> |
| 150 | struct QConcatenable<QStringDecoder::EncodedData<T>> |
| 151 | : private QAbstractConcatenable |
| 152 | { |
| 153 | typedef QChar type; |
| 154 | typedef QString ConvertTo; |
| 155 | enum { ExactSize = false }; |
| 156 | static qsizetype size(const QStringDecoder::EncodedData<T> &s) { return s.decoder->requiredSpace(s.data.size()); } |
| 157 | static inline void appendTo(const QStringDecoder::EncodedData<T> &s, QChar *&out) |
| 158 | { |
| 159 | out = s.decoder->appendToBuffer(out, s.data); |
| 160 | } |
| 161 | }; |
| 162 | |
| 163 | template <typename T> |
| 164 | struct QConcatenable<QStringEncoder::DecodedData<T>> |
| 165 | : private QAbstractConcatenable |
| 166 | { |
| 167 | typedef char type; |
| 168 | typedef QByteArray ConvertTo; |
| 169 | enum { ExactSize = false }; |
| 170 | static qsizetype size(const QStringEncoder::DecodedData<T> &s) { return s.encoder->requiredSpace(s.data.size()); } |
| 171 | static inline void appendTo(const QStringEncoder::DecodedData<T> &s, char *&out) |
| 172 | { |
| 173 | out = s.encoder->appendToBuffer(out, s.data); |
| 174 | } |
| 175 | }; |
| 176 | |
| 177 | template <typename T> |
| 178 | QString &operator+=(QString &a, const QStringDecoder::EncodedData<T> &b) |
| 179 | { |
| 180 | qsizetype len = a.size() + QConcatenable<QStringDecoder::EncodedData<T>>::size(b); |
| 181 | a.reserve(asize: len); |
| 182 | QChar *it = a.data() + a.size(); |
| 183 | QConcatenable<QStringDecoder::EncodedData<T>>::appendTo(b, it); |
| 184 | a.resize(size: qsizetype(it - a.constData())); //may be smaller than len |
| 185 | return a; |
| 186 | } |
| 187 | |
| 188 | template <typename T> |
| 189 | QByteArray &operator+=(QByteArray &a, const QStringEncoder::DecodedData<T> &b) |
| 190 | { |
| 191 | qsizetype len = a.size() + QConcatenable<QStringEncoder::DecodedData<T>>::size(b); |
| 192 | a.reserve(asize: len); |
| 193 | char *it = a.data() + a.size(); |
| 194 | QConcatenable<QStringEncoder::DecodedData<T>>::appendTo(b, it); |
| 195 | a.resize(size: qsizetype(it - a.constData())); //may be smaller than len |
| 196 | return a; |
| 197 | } |
| 198 | #endif |
| 199 | |
| 200 | template <typename InputIterator> |
| 201 | void QString::assign_helper_char8(InputIterator first, InputIterator last) |
| 202 | { |
| 203 | static_assert(!QString::is_contiguous_iterator_v<InputIterator>, |
| 204 | "Internal error: Should have been handed over to the QAnyStringView overload." |
| 205 | ); |
| 206 | |
| 207 | using ValueType = typename std::iterator_traits<InputIterator>::value_type; |
| 208 | constexpr bool IsFwdIt = std::is_convertible_v< |
| 209 | typename std::iterator_traits<InputIterator>::iterator_category, |
| 210 | std::forward_iterator_tag |
| 211 | >; |
| 212 | |
| 213 | resize(size: 0); |
| 214 | // In case of not being shared, there is the possibility of having free space at begin |
| 215 | // even after the resize to zero. |
| 216 | if (const auto offset = d.freeSpaceAtBegin()) |
| 217 | d.setBegin(d.begin() - offset); |
| 218 | |
| 219 | if constexpr (IsFwdIt) |
| 220 | reserve(asize: static_cast<qsizetype>(std::distance(first, last))); |
| 221 | |
| 222 | auto toUtf16 = QStringDecoder(QStringDecoder::Utf8); |
| 223 | auto availableCapacity = d.constAllocatedCapacity(); |
| 224 | auto *dst = d.data(); |
| 225 | auto *dend = d.data() + availableCapacity; |
| 226 | |
| 227 | while (true) { |
| 228 | if (first == last) { // ran out of input elements |
| 229 | Q_ASSERT(!std::less<>{}(dend, dst)); |
| 230 | d.size = dst - d.begin(); |
| 231 | return; |
| 232 | } |
| 233 | const ValueType next = *first; // decays proxies, if any |
| 234 | const auto chunk = QUtf8StringView(&next, 1); |
| 235 | // UTF-8 characters can have a maximum size of 4 bytes and may result in a surrogate |
| 236 | // pair of UTF-16 code units. In the input-iterator case, we don't know the size |
| 237 | // and would need to always reserve space for 2 code units. To keep our promise |
| 238 | // of 'not allocating if it fits', we have to pre-check this condition. |
| 239 | // We know that it fits in the forward-iterator case. |
| 240 | if constexpr (!IsFwdIt) { |
| 241 | constexpr qsizetype Pair = 2; |
| 242 | char16_t buf[Pair]; |
| 243 | const qptrdiff n = toUtf16.appendToBuffer(out: buf, ba: chunk) - buf; |
| 244 | if (dend - dst < n) { // ran out of allocated memory |
| 245 | const auto offset = dst - d.begin(); |
| 246 | reallocData(alloc: d.constAllocatedCapacity() + Pair, option: QArrayData::Grow); |
| 247 | // update the pointers since we've re-allocated |
| 248 | availableCapacity = d.constAllocatedCapacity(); |
| 249 | dst = d.data() + offset; |
| 250 | dend = d.data() + availableCapacity; |
| 251 | } |
| 252 | dst = std::copy_n(first: buf, n: n, result: dst); |
| 253 | } else { // take the fast path |
| 254 | dst = toUtf16.appendToBuffer(out: dst, ba: chunk); |
| 255 | } |
| 256 | ++first; |
| 257 | } |
| 258 | } |
| 259 | |
| 260 | QT_END_NAMESPACE |
| 261 | |
| 262 | #endif |
| 263 | |