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
18QT_BEGIN_NAMESPACE
19
20class QStringEncoder : public QStringConverter
21{
22protected:
23 constexpr explicit QStringEncoder(const Interface *i) noexcept
24 : QStringConverter(i)
25 {}
26public:
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(const char *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 }
65private:
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
82class QStringDecoder : public QStringConverter
83{
84protected:
85 constexpr explicit QStringDecoder(const Interface *i) noexcept
86 : QStringConverter(i)
87 {}
88public:
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(const char *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
132private:
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)
149template <typename T>
150struct 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
163template <typename T>
164struct 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
177template <typename T>
178QString &operator+=(QString &a, const QStringDecoder::EncodedData<T> &b)
179{
180 qsizetype len = a.size() + QConcatenable<QStringDecoder::EncodedData<T>>::size(b);
181 a.reserve(len);
182 QChar *it = a.data() + a.size();
183 QConcatenable<QStringDecoder::EncodedData<T>>::appendTo(b, it);
184 a.resize(qsizetype(it - a.constData())); //may be smaller than len
185 return a;
186}
187
188template <typename T>
189QByteArray &operator+=(QByteArray &a, const QStringEncoder::DecodedData<T> &b)
190{
191 qsizetype len = a.size() + QConcatenable<QStringEncoder::DecodedData<T>>::size(b);
192 a.reserve(len);
193 char *it = a.data() + a.size();
194 QConcatenable<QStringEncoder::DecodedData<T>>::appendTo(b, it);
195 a.resize(qsizetype(it - a.constData())); //may be smaller than len
196 return a;
197}
198#endif
199
200template <typename InputIterator>
201void 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
260QT_END_NAMESPACE
261
262#endif
263