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 | #include <QtCore/qstring.h> |
5 | |
6 | #ifndef QSTRINGBUILDER_H |
7 | #define QSTRINGBUILDER_H |
8 | |
9 | #if 0 |
10 | // syncqt can not handle the templates in this file, and it doesn't need to |
11 | // process them anyway because they are internal. |
12 | #pragma qt_class(QStringBuilder) |
13 | #pragma qt_sync_stop_processing |
14 | #endif |
15 | |
16 | #include <QtCore/qbytearray.h> |
17 | |
18 | #include <string.h> |
19 | |
20 | QT_BEGIN_NAMESPACE |
21 | |
22 | |
23 | struct Q_CORE_EXPORT QAbstractConcatenable |
24 | { |
25 | protected: |
26 | static void convertFromUtf8(QByteArrayView in, QChar *&out) noexcept; |
27 | static inline void convertFromAscii(char a, QChar *&out) noexcept |
28 | { |
29 | *out++ = QLatin1Char(a); |
30 | } |
31 | static void appendLatin1To(QLatin1StringView in, QChar *out) noexcept; |
32 | }; |
33 | |
34 | template <typename T> struct QConcatenable {}; |
35 | |
36 | namespace QtStringBuilder { |
37 | template <typename A, typename B> struct ConvertToTypeHelper |
38 | { typedef A ConvertTo; }; |
39 | template <typename T> struct ConvertToTypeHelper<T, QString> |
40 | { typedef QString ConvertTo; }; |
41 | } |
42 | |
43 | template<typename Builder, typename T> |
44 | struct QStringBuilderCommon |
45 | { |
46 | T toUpper() const { return resolved().toUpper(); } |
47 | T toLower() const { return resolved().toLower(); } |
48 | |
49 | protected: |
50 | T resolved() const { return *static_cast<const Builder*>(this); } |
51 | }; |
52 | |
53 | template<typename Builder, typename T> |
54 | struct QStringBuilderBase : public QStringBuilderCommon<Builder, T> |
55 | { |
56 | }; |
57 | |
58 | template<typename Builder> |
59 | struct QStringBuilderBase<Builder, QString> : public QStringBuilderCommon<Builder, QString> |
60 | { |
61 | QByteArray toLatin1() const { return this->resolved().toLatin1(); } |
62 | QByteArray toUtf8() const { return this->resolved().toUtf8(); } |
63 | QByteArray toLocal8Bit() const { return this->resolved().toLocal8Bit(); } |
64 | }; |
65 | |
66 | template <typename A, typename B> |
67 | class QStringBuilder : public QStringBuilderBase<QStringBuilder<A, B>, typename QtStringBuilder::ConvertToTypeHelper<typename QConcatenable<A>::ConvertTo, typename QConcatenable<B>::ConvertTo>::ConvertTo> |
68 | { |
69 | public: |
70 | QStringBuilder(const A &a_, const B &b_) : a(a_), b(b_) {} |
71 | private: |
72 | friend class QByteArray; |
73 | friend class QString; |
74 | template <typename T> T convertTo() const |
75 | { |
76 | const qsizetype len = QConcatenable< QStringBuilder<A, B> >::size(*this); |
77 | T s(len, Qt::Uninitialized); |
78 | |
79 | // we abuse const_cast / constData here because we know we've just |
80 | // allocated the data and we're the only reference count |
81 | typename T::iterator d = const_cast<typename T::iterator>(s.constData()); |
82 | typename T::const_iterator const start = d; |
83 | QConcatenable< QStringBuilder<A, B> >::appendTo(*this, d); |
84 | |
85 | if (!QConcatenable< QStringBuilder<A, B> >::ExactSize && len != d - start) { |
86 | // this resize is necessary since we allocate a bit too much |
87 | // when dealing with variable sized 8-bit encodings |
88 | s.resize(d - start); |
89 | } |
90 | return s; |
91 | } |
92 | |
93 | typedef QConcatenable<QStringBuilder<A, B> > Concatenable; |
94 | public: |
95 | typedef typename Concatenable::ConvertTo ConvertTo; |
96 | operator ConvertTo() const { return convertTo<ConvertTo>(); } |
97 | |
98 | qsizetype size() const { return Concatenable::size(*this); } |
99 | |
100 | const A &a; |
101 | const B &b; |
102 | }; |
103 | |
104 | // This specialization is here for backwards compatibility: appending |
105 | // two null strings must give back a null string, so we're special |
106 | // casing this one out. |
107 | template <> |
108 | class QStringBuilder <QString, QString> : public QStringBuilderBase<QStringBuilder<QString, QString>, QString> |
109 | { |
110 | public: |
111 | QStringBuilder(const QString &a_, const QString &b_) : a(a_), b(b_) {} |
112 | QStringBuilder(const QStringBuilder &other) : a(other.a), b(other.b) {} |
113 | |
114 | operator QString() const |
115 | { QString r(a); r += b; return r; } |
116 | |
117 | const QString &a; |
118 | const QString &b; |
119 | |
120 | private: |
121 | QStringBuilder &operator=(const QStringBuilder &) = delete; |
122 | }; |
123 | |
124 | // Ditto, but see QTBUG-114238 |
125 | template <> |
126 | class QStringBuilder <QByteArray, QByteArray> : public QStringBuilderBase<QStringBuilder<QByteArray, QByteArray>, QByteArray> |
127 | { |
128 | public: |
129 | QStringBuilder(const QByteArray &a_, const QByteArray &b_) : a(a_), b(b_) {} |
130 | QStringBuilder(const QStringBuilder &other) : a(other.a), b(other.b) {} |
131 | |
132 | operator QByteArray() const |
133 | { QByteArray r(a); r += b; return r; } |
134 | |
135 | const QByteArray &a; |
136 | const QByteArray &b; |
137 | |
138 | private: |
139 | QStringBuilder &operator=(const QStringBuilder &) = delete; |
140 | }; |
141 | |
142 | |
143 | template <> struct QConcatenable<char> : private QAbstractConcatenable |
144 | { |
145 | typedef char type; |
146 | typedef QByteArray ConvertTo; |
147 | enum { ExactSize = true }; |
148 | static qsizetype size(const char) { return 1; } |
149 | #ifndef QT_NO_CAST_FROM_ASCII |
150 | QT_ASCII_CAST_WARN static inline void appendTo(const char c, QChar *&out) |
151 | { |
152 | QAbstractConcatenable::convertFromAscii(a: c, out); |
153 | } |
154 | #endif |
155 | static inline void appendTo(const char c, char *&out) |
156 | { *out++ = c; } |
157 | }; |
158 | |
159 | template <> struct QConcatenable<QByteArrayView> : private QAbstractConcatenable |
160 | { |
161 | typedef QByteArrayView type; |
162 | typedef QByteArray ConvertTo; |
163 | enum { ExactSize = true }; |
164 | static qsizetype size(QByteArrayView bav) { return bav.size(); } |
165 | #ifndef QT_NO_CAST_FROM_ASCII |
166 | QT_ASCII_CAST_WARN static inline void appendTo(QByteArrayView bav, QChar *&out) |
167 | { |
168 | QAbstractConcatenable::convertFromUtf8(in: bav, out); |
169 | } |
170 | #endif |
171 | static inline void appendTo(QByteArrayView bav, char *&out) |
172 | { |
173 | qsizetype n = bav.size(); |
174 | if (n) |
175 | memcpy(dest: out, src: bav.data(), n: n); |
176 | out += n; |
177 | } |
178 | }; |
179 | |
180 | template <> struct QConcatenable<char16_t> : private QAbstractConcatenable |
181 | { |
182 | typedef char16_t type; |
183 | typedef QString ConvertTo; |
184 | enum { ExactSize = true }; |
185 | static constexpr qsizetype size(char16_t) { return 1; } |
186 | static inline void appendTo(char16_t c, QChar *&out) |
187 | { *out++ = c; } |
188 | }; |
189 | |
190 | template <> struct QConcatenable<QLatin1Char> |
191 | { |
192 | typedef QLatin1Char type; |
193 | typedef QString ConvertTo; |
194 | enum { ExactSize = true }; |
195 | static qsizetype size(const QLatin1Char) { return 1; } |
196 | static inline void appendTo(const QLatin1Char c, QChar *&out) |
197 | { *out++ = c; } |
198 | static inline void appendTo(const QLatin1Char c, char *&out) |
199 | { *out++ = c.toLatin1(); } |
200 | }; |
201 | |
202 | template <> struct QConcatenable<QChar> : private QAbstractConcatenable |
203 | { |
204 | typedef QChar type; |
205 | typedef QString ConvertTo; |
206 | enum { ExactSize = true }; |
207 | static qsizetype size(const QChar) { return 1; } |
208 | static inline void appendTo(const QChar c, QChar *&out) |
209 | { *out++ = c; } |
210 | }; |
211 | |
212 | template <> struct QConcatenable<QChar::SpecialCharacter> : private QAbstractConcatenable |
213 | { |
214 | typedef QChar::SpecialCharacter type; |
215 | typedef QString ConvertTo; |
216 | enum { ExactSize = true }; |
217 | static qsizetype size(const QChar::SpecialCharacter) { return 1; } |
218 | static inline void appendTo(const QChar::SpecialCharacter c, QChar *&out) |
219 | { *out++ = c; } |
220 | }; |
221 | |
222 | template <> struct QConcatenable<QLatin1StringView> : private QAbstractConcatenable |
223 | { |
224 | typedef QLatin1StringView type; |
225 | typedef QString ConvertTo; |
226 | enum { ExactSize = true }; |
227 | static qsizetype size(const QLatin1StringView a) { return a.size(); } |
228 | static inline void appendTo(const QLatin1StringView a, QChar *&out) |
229 | { |
230 | appendLatin1To(in: a, out); |
231 | out += a.size(); |
232 | } |
233 | static inline void appendTo(const QLatin1StringView a, char *&out) |
234 | { |
235 | if (const char *data = a.data()) { |
236 | memcpy(dest: out, src: data, n: a.size()); |
237 | out += a.size(); |
238 | } |
239 | } |
240 | }; |
241 | |
242 | template <> struct QConcatenable<QString> : private QAbstractConcatenable |
243 | { |
244 | typedef QString type; |
245 | typedef QString ConvertTo; |
246 | enum { ExactSize = true }; |
247 | static qsizetype size(const QString &a) { return a.size(); } |
248 | static inline void appendTo(const QString &a, QChar *&out) |
249 | { |
250 | const qsizetype n = a.size(); |
251 | if (n) |
252 | memcpy(dest: out, src: reinterpret_cast<const char*>(a.constData()), n: sizeof(QChar) * n); |
253 | out += n; |
254 | } |
255 | }; |
256 | |
257 | template <> struct QConcatenable<QStringView> : private QAbstractConcatenable |
258 | { |
259 | typedef QStringView type; |
260 | typedef QString ConvertTo; |
261 | enum { ExactSize = true }; |
262 | static qsizetype size(QStringView a) { return a.size(); } |
263 | static inline void appendTo(QStringView a, QChar *&out) |
264 | { |
265 | const auto n = a.size(); |
266 | if (n) |
267 | memcpy(dest: out, src: a.data(), n: sizeof(QChar) * n); |
268 | out += n; |
269 | } |
270 | }; |
271 | |
272 | template <qsizetype N> struct QConcatenable<const char[N]> : private QAbstractConcatenable |
273 | { |
274 | typedef const char type[N]; |
275 | typedef QByteArray ConvertTo; |
276 | enum { ExactSize = false }; |
277 | static qsizetype size(const char[N]) { return N - 1; } |
278 | #ifndef QT_NO_CAST_FROM_ASCII |
279 | QT_ASCII_CAST_WARN static inline void appendTo(const char a[N], QChar *&out) |
280 | { |
281 | QAbstractConcatenable::convertFromUtf8(in: QByteArrayView(a, N - 1), out); |
282 | } |
283 | #endif |
284 | static inline void appendTo(const char a[N], char *&out) |
285 | { |
286 | while (*a) |
287 | *out++ = *a++; |
288 | } |
289 | }; |
290 | |
291 | template <qsizetype N> struct QConcatenable<char[N]> : QConcatenable<const char[N]> |
292 | { |
293 | typedef char type[N]; |
294 | }; |
295 | |
296 | template <> struct QConcatenable<const char *> : private QAbstractConcatenable |
297 | { |
298 | typedef const char *type; |
299 | typedef QByteArray ConvertTo; |
300 | enum { ExactSize = false }; |
301 | static qsizetype size(const char *a) { return qstrlen(str: a); } |
302 | #ifndef QT_NO_CAST_FROM_ASCII |
303 | QT_ASCII_CAST_WARN static inline void appendTo(const char *a, QChar *&out) |
304 | { QAbstractConcatenable::convertFromUtf8(in: QByteArrayView(a), out); } |
305 | #endif |
306 | static inline void appendTo(const char *a, char *&out) |
307 | { |
308 | if (!a) |
309 | return; |
310 | while (*a) |
311 | *out++ = *a++; |
312 | } |
313 | }; |
314 | |
315 | template <> struct QConcatenable<char *> : QConcatenable<const char*> |
316 | { |
317 | typedef char *type; |
318 | }; |
319 | |
320 | template <qsizetype N> struct QConcatenable<const char16_t[N]> : private QAbstractConcatenable |
321 | { |
322 | using type = const char16_t[N]; |
323 | using ConvertTo = QString; |
324 | enum { ExactSize = true }; |
325 | static qsizetype size(const char16_t[N]) { return N - 1; } |
326 | static void appendTo(const char16_t a[N], QChar *&out) |
327 | { |
328 | memcpy(static_cast<void *>(out), a, (N - 1) * sizeof(char16_t)); |
329 | out += N - 1; |
330 | } |
331 | }; |
332 | |
333 | template <qsizetype N> struct QConcatenable<char16_t[N]> : QConcatenable<const char16_t[N]> |
334 | { |
335 | using type = char16_t[N]; |
336 | }; |
337 | |
338 | template <> struct QConcatenable<const char16_t *> : private QAbstractConcatenable |
339 | { |
340 | using type = const char16_t *; |
341 | using ConvertTo = QString; |
342 | enum { ExactSize = true }; |
343 | static qsizetype size(const char16_t *a) { return QStringView(a).size(); } |
344 | QT_ASCII_CAST_WARN static inline void appendTo(const char16_t *a, QChar *&out) |
345 | { |
346 | if (!a) |
347 | return; |
348 | while (*a) |
349 | *out++ = *a++; |
350 | } |
351 | }; |
352 | |
353 | template <> struct QConcatenable<char16_t *> : QConcatenable<const char16_t*> |
354 | { |
355 | typedef char16_t *type; |
356 | }; |
357 | |
358 | template <> struct QConcatenable<QByteArray> : private QAbstractConcatenable |
359 | { |
360 | typedef QByteArray type; |
361 | typedef QByteArray ConvertTo; |
362 | enum { ExactSize = false }; |
363 | static qsizetype size(const QByteArray &ba) { return ba.size(); } |
364 | #ifndef QT_NO_CAST_FROM_ASCII |
365 | QT_ASCII_CAST_WARN static inline void appendTo(const QByteArray &ba, QChar *&out) |
366 | { |
367 | QAbstractConcatenable::convertFromUtf8(in: ba, out); |
368 | } |
369 | #endif |
370 | static inline void appendTo(const QByteArray &ba, char *&out) |
371 | { |
372 | const char *a = ba.constData(); |
373 | const char * const end = ba.end(); |
374 | while (a != end) |
375 | *out++ = *a++; |
376 | } |
377 | }; |
378 | |
379 | |
380 | template <typename A, typename B> |
381 | struct QConcatenable< QStringBuilder<A, B> > |
382 | { |
383 | typedef QStringBuilder<A, B> type; |
384 | typedef typename QtStringBuilder::ConvertToTypeHelper<typename QConcatenable<A>::ConvertTo, typename QConcatenable<B>::ConvertTo>::ConvertTo ConvertTo; |
385 | enum { ExactSize = QConcatenable<A>::ExactSize && QConcatenable<B>::ExactSize }; |
386 | static qsizetype size(const type &p) |
387 | { |
388 | return QConcatenable<A>::size(p.a) + QConcatenable<B>::size(p.b); |
389 | } |
390 | template<typename T> static inline void appendTo(const type &p, T *&out) |
391 | { |
392 | QConcatenable<A>::appendTo(p.a, out); |
393 | QConcatenable<B>::appendTo(p.b, out); |
394 | } |
395 | }; |
396 | |
397 | template <typename A, typename B> |
398 | QStringBuilder<typename QConcatenable<A>::type, typename QConcatenable<B>::type> |
399 | operator%(const A &a, const B &b) |
400 | { |
401 | return QStringBuilder<typename QConcatenable<A>::type, typename QConcatenable<B>::type>(a, b); |
402 | } |
403 | |
404 | // QT_USE_FAST_OPERATOR_PLUS was introduced in 4.7, QT_USE_QSTRINGBUILDER is to be used from 4.8 onwards |
405 | // QT_USE_FAST_OPERATOR_PLUS does not remove the normal operator+ for QByteArray |
406 | #if defined(QT_USE_FAST_OPERATOR_PLUS) || defined(QT_USE_QSTRINGBUILDER) |
407 | template <typename A, typename B> |
408 | QStringBuilder<typename QConcatenable<A>::type, typename QConcatenable<B>::type> |
409 | operator+(const A &a, const B &b) |
410 | { |
411 | return QStringBuilder<typename QConcatenable<A>::type, typename QConcatenable<B>::type>(a, b); |
412 | } |
413 | #endif |
414 | |
415 | namespace QtStringBuilder { |
416 | template <typename A, typename B> |
417 | QByteArray &appendToByteArray(QByteArray &a, const QStringBuilder<A, B> &b, char) |
418 | { |
419 | // append 8-bit data to a byte array |
420 | qsizetype len = a.size() + QConcatenable< QStringBuilder<A, B> >::size(b); |
421 | a.detach(); // a detach() in a.data() could reset a.capacity() to a.size() |
422 | if (len > a.data_ptr().freeSpaceAtEnd()) // capacity() was broken when prepend()-optimization landed |
423 | a.reserve(asize: qMax(a: len, b: 2 * a.capacity())); |
424 | char *it = a.data() + a.size(); |
425 | QConcatenable< QStringBuilder<A, B> >::appendTo(b, it); |
426 | a.resize(size: len); //we need to resize after the appendTo for the case str+=foo+str |
427 | return a; |
428 | } |
429 | |
430 | #ifndef QT_NO_CAST_TO_ASCII |
431 | template <typename A, typename B> |
432 | QByteArray &appendToByteArray(QByteArray &a, const QStringBuilder<A, B> &b, QChar) |
433 | { |
434 | return a += QString(b).toUtf8(); |
435 | } |
436 | #endif |
437 | } |
438 | |
439 | template <typename A, typename B> |
440 | QByteArray &operator+=(QByteArray &a, const QStringBuilder<A, B> &b) |
441 | { |
442 | return QtStringBuilder::appendToByteArray(a, b, |
443 | typename QConcatenable< QStringBuilder<A, B> >::ConvertTo::value_type()); |
444 | } |
445 | |
446 | template <typename A, typename B> |
447 | QString &operator+=(QString &a, const QStringBuilder<A, B> &b) |
448 | { |
449 | qsizetype len = a.size() + QConcatenable< QStringBuilder<A, B> >::size(b); |
450 | a.detach(); // a detach() in a.data() could reset a.capacity() to a.size() |
451 | if (len > a.data_ptr().freeSpaceAtEnd()) // capacity() was broken when prepend()-optimization landed |
452 | a.reserve(asize: qMax(a: len, b: 2 * a.capacity())); |
453 | QChar *it = a.data() + a.size(); |
454 | QConcatenable< QStringBuilder<A, B> >::appendTo(b, it); |
455 | // we need to resize after the appendTo for the case str+=foo+str |
456 | a.resize(size: it - a.constData()); //may be smaller than len if there was conversion from utf8 |
457 | return a; |
458 | } |
459 | |
460 | QT_END_NAMESPACE |
461 | |
462 | #endif // QSTRINGBUILDER_H |
463 | |