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

source code of qtbase/src/corelib/text/qstringconverter.h