1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 |
3 | |
4 | #include "proitems.h" |
5 | |
6 | #include <qfileinfo.h> |
7 | #include <qset.h> |
8 | #include <qstringlist.h> |
9 | #include <qtextstream.h> |
10 | #include <private/qduplicatetracker_p.h> |
11 | |
12 | QT_BEGIN_NAMESPACE |
13 | |
14 | // from qhash.cpp |
15 | size_t ProString::hash(const QChar *p, int n) |
16 | { |
17 | size_t h = 0; |
18 | |
19 | while (n--) { |
20 | h = (h << 4) + (*p++).unicode(); |
21 | h ^= (h & 0xf0000000) >> 23; |
22 | h &= 0x0fffffff; |
23 | } |
24 | return h; |
25 | } |
26 | |
27 | ProString::ProString() : |
28 | m_offset(0), m_length(0), m_file(0), m_hash(0x80000000) |
29 | { |
30 | } |
31 | |
32 | ProString::ProString(const ProString &other) : |
33 | m_string(other.m_string), m_offset(other.m_offset), m_length(other.m_length), m_file(other.m_file), m_hash(other.m_hash) |
34 | { |
35 | } |
36 | |
37 | ProString::ProString(const ProString &other, OmitPreHashing) : |
38 | m_string(other.m_string), m_offset(other.m_offset), m_length(other.m_length), m_file(other.m_file), m_hash(0x80000000) |
39 | { |
40 | } |
41 | |
42 | ProString::ProString(const QString &str, DoPreHashing) : |
43 | m_string(str), m_offset(0), m_length(str.size()), m_file(0) |
44 | { |
45 | updatedHash(); |
46 | } |
47 | |
48 | ProString::ProString(const QString &str) : |
49 | m_string(str), m_offset(0), m_length(str.size()), m_file(0), m_hash(0x80000000) |
50 | { |
51 | } |
52 | |
53 | ProString::ProString(QStringView str) : |
54 | m_string(str.toString()), m_offset(0), m_length(str.size()), m_file(0), m_hash(0x80000000) |
55 | { |
56 | } |
57 | |
58 | ProString::ProString(const char *str, DoPreHashing) : |
59 | m_string(QString::fromLatin1(ba: str)), m_offset(0), m_length(int(qstrlen(str))), m_file(0) |
60 | { |
61 | updatedHash(); |
62 | } |
63 | |
64 | ProString::ProString(const char *str) : |
65 | m_string(QString::fromLatin1(ba: str)), m_offset(0), m_length(int(qstrlen(str))), m_file(0), m_hash(0x80000000) |
66 | { |
67 | } |
68 | |
69 | ProString::ProString(const QString &str, int offset, int length, DoPreHashing) : |
70 | m_string(str), m_offset(offset), m_length(length), m_file(0) |
71 | { |
72 | updatedHash(); |
73 | } |
74 | |
75 | ProString::ProString(const QString &str, int offset, int length, uint hash) : |
76 | m_string(str), m_offset(offset), m_length(length), m_file(0), m_hash(hash) |
77 | { |
78 | } |
79 | |
80 | ProString::ProString(const QString &str, int offset, int length) : |
81 | m_string(str), m_offset(offset), m_length(length), m_file(0), m_hash(0x80000000) |
82 | { |
83 | } |
84 | |
85 | void ProString::setValue(const QString &str) |
86 | { |
87 | m_string = str, m_offset = 0, m_length = str.size(), m_hash = 0x80000000; |
88 | } |
89 | |
90 | size_t ProString::updatedHash() const |
91 | { |
92 | return (m_hash = hash(p: m_string.constData() + m_offset, n: m_length)); |
93 | } |
94 | |
95 | size_t qHash(const ProString &str) |
96 | { |
97 | if (!(str.m_hash & 0x80000000)) |
98 | return str.m_hash; |
99 | return str.updatedHash(); |
100 | } |
101 | |
102 | ProKey::ProKey(const QString &str) : |
103 | ProString(str, DoHash) |
104 | { |
105 | } |
106 | |
107 | ProKey::ProKey(const char *str) : |
108 | ProString(str, DoHash) |
109 | { |
110 | } |
111 | |
112 | ProKey::ProKey(const QString &str, int off, int len) : |
113 | ProString(str, off, len, DoHash) |
114 | { |
115 | } |
116 | |
117 | ProKey::ProKey(const QString &str, int off, int len, uint hash) : |
118 | ProString(str, off, len, hash) |
119 | { |
120 | } |
121 | |
122 | void ProKey::setValue(const QString &str) |
123 | { |
124 | m_string = str, m_offset = 0, m_length = str.size(); |
125 | updatedHash(); |
126 | } |
127 | |
128 | QString ProString::toQString() const |
129 | { |
130 | return m_string.mid(position: m_offset, n: m_length); |
131 | } |
132 | |
133 | QString &ProString::toQString(QString &tmp) const |
134 | { |
135 | tmp = m_string.mid(position: m_offset, n: m_length); |
136 | return tmp; |
137 | } |
138 | |
139 | ProString &ProString::prepend(const ProString &other) |
140 | { |
141 | if (other.m_length) { |
142 | if (!m_length) { |
143 | *this = other; |
144 | } else { |
145 | m_string = other.toQStringView() + toQStringView(); |
146 | m_offset = 0; |
147 | m_length = m_string.size(); |
148 | if (!m_file) |
149 | m_file = other.m_file; |
150 | m_hash = 0x80000000; |
151 | } |
152 | } |
153 | return *this; |
154 | } |
155 | |
156 | ProString &ProString::append(const QLatin1String other) |
157 | { |
158 | if (other.size()) { |
159 | if (m_length != m_string.size()) { |
160 | m_string = toQStringView() + other; |
161 | m_offset = 0; |
162 | m_length = m_string.size(); |
163 | } else { |
164 | Q_ASSERT(m_offset == 0); |
165 | m_string.append(s: other); |
166 | m_length += other.size(); |
167 | } |
168 | m_hash = 0x80000000; |
169 | } |
170 | return *this; |
171 | } |
172 | |
173 | ProString &ProString::append(QChar other) |
174 | { |
175 | if (m_length != m_string.size()) { |
176 | m_string = toQStringView() + other; |
177 | m_offset = 0; |
178 | m_length = m_string.size(); |
179 | } else { |
180 | Q_ASSERT(m_offset == 0); |
181 | m_string.append(c: other); |
182 | ++m_length; |
183 | } |
184 | m_hash = 0x80000000; |
185 | return *this; |
186 | } |
187 | |
188 | // If pending != 0, prefix with space if appending to non-empty non-pending |
189 | ProString &ProString::append(const ProString &other, bool *pending) |
190 | { |
191 | if (other.m_length) { |
192 | if (!m_length) { |
193 | *this = other; |
194 | } else { |
195 | if (m_length != m_string.size()) |
196 | m_string = toQString(); |
197 | if (pending && !*pending) { |
198 | m_string += QLatin1Char(' ') + other.toQStringView(); |
199 | } else { |
200 | m_string += other.toQStringView(); |
201 | } |
202 | m_length = m_string.size(); |
203 | m_offset = 0; |
204 | if (other.m_file) |
205 | m_file = other.m_file; |
206 | m_hash = 0x80000000; |
207 | } |
208 | if (pending) |
209 | *pending = true; |
210 | } |
211 | return *this; |
212 | } |
213 | |
214 | ProString &ProString::append(const ProStringList &other, bool *pending, bool skipEmpty1st) |
215 | { |
216 | if (const int sz = other.size()) { |
217 | int startIdx = 0; |
218 | if (pending && !*pending && skipEmpty1st && other.at(i: 0).isEmpty()) { |
219 | if (sz == 1) |
220 | return *this; |
221 | startIdx = 1; |
222 | } |
223 | if (!m_length && sz == startIdx + 1) { |
224 | *this = other.at(i: startIdx); |
225 | } else { |
226 | bool putSpace = false; |
227 | if (pending && !*pending && m_length) |
228 | putSpace = true; |
229 | |
230 | m_string = toQString(); |
231 | m_offset = 0; |
232 | for (int i = startIdx; i < sz; ++i) { |
233 | if (putSpace) |
234 | m_string += QLatin1Char(' '); |
235 | else |
236 | putSpace = true; |
237 | const ProString &str = other.at(i); |
238 | m_string += str.toQStringView(); |
239 | } |
240 | m_length = m_string.size(); |
241 | if (other.last().m_file) |
242 | m_file = other.last().m_file; |
243 | m_hash = 0x80000000; |
244 | } |
245 | if (pending) |
246 | *pending = true; |
247 | } |
248 | return *this; |
249 | } |
250 | |
251 | QString operator+(const ProString &one, const ProString &two) |
252 | { |
253 | if (two.m_length) { |
254 | if (!one.m_length) { |
255 | return two.toQString(); |
256 | } else { |
257 | QString neu(one.m_length + two.m_length, Qt::Uninitialized); |
258 | ushort *ptr = (ushort *)neu.constData(); |
259 | memcpy(dest: ptr, src: one.m_string.constData() + one.m_offset, n: one.m_length * 2); |
260 | memcpy(dest: ptr + one.m_length, src: two.m_string.constData() + two.m_offset, n: two.m_length * 2); |
261 | return neu; |
262 | } |
263 | } |
264 | return one.toQString(); |
265 | } |
266 | |
267 | |
268 | ProString ProString::mid(int off, int len) const |
269 | { |
270 | ProString ret(*this, NoHash); |
271 | if (off > m_length) |
272 | off = m_length; |
273 | ret.m_offset += off; |
274 | ret.m_length -= off; |
275 | if ((uint)ret.m_length > (uint)len) // Unsigned comparison to interpret < 0 as infinite |
276 | ret.m_length = len; |
277 | return ret; |
278 | } |
279 | |
280 | ProString ProString::trimmed() const |
281 | { |
282 | ProString ret(*this, NoHash); |
283 | int cur = m_offset; |
284 | int end = cur + m_length; |
285 | const QChar *data = m_string.constData(); |
286 | for (; cur < end; cur++) |
287 | if (!data[cur].isSpace()) { |
288 | // No underrun check - we know there is at least one non-whitespace |
289 | while (data[end - 1].isSpace()) |
290 | end--; |
291 | break; |
292 | } |
293 | ret.m_offset = cur; |
294 | ret.m_length = end - cur; |
295 | return ret; |
296 | } |
297 | |
298 | QTextStream &operator<<(QTextStream &t, const ProString &str) |
299 | { |
300 | t << str.toQStringView(); |
301 | return t; |
302 | } |
303 | |
304 | static QString ProStringList_join(const ProStringList &this_, const QChar *sep, const int sepSize) |
305 | { |
306 | int totalLength = 0; |
307 | const int sz = this_.size(); |
308 | |
309 | for (int i = 0; i < sz; ++i) |
310 | totalLength += this_.at(i).size(); |
311 | |
312 | if (sz) |
313 | totalLength += sepSize * (sz - 1); |
314 | |
315 | QString res(totalLength, Qt::Uninitialized); |
316 | QChar *ptr = (QChar *)res.constData(); |
317 | for (int i = 0; i < sz; ++i) { |
318 | if (i) { |
319 | memcpy(dest: ptr, src: sep, n: sepSize * sizeof(QChar)); |
320 | ptr += sepSize; |
321 | } |
322 | const ProString &str = this_.at(i); |
323 | memcpy(dest: ptr, src: str.constData(), n: str.size() * sizeof(QChar)); |
324 | ptr += str.size(); |
325 | } |
326 | return res; |
327 | } |
328 | |
329 | QString ProStringList::join(const ProString &sep) const |
330 | { |
331 | return ProStringList_join(this_: *this, sep: sep.constData(), sepSize: sep.size()); |
332 | } |
333 | |
334 | QString ProStringList::join(const QString &sep) const |
335 | { |
336 | return ProStringList_join(this_: *this, sep: sep.constData(), sepSize: sep.size()); |
337 | } |
338 | |
339 | QString ProStringList::join(QChar sep) const |
340 | { |
341 | return ProStringList_join(this_: *this, sep: &sep, sepSize: 1); |
342 | } |
343 | |
344 | void ProStringList::removeAll(const ProString &str) |
345 | { |
346 | for (int i = size(); --i >= 0; ) |
347 | if (at(i) == str) |
348 | remove(i); |
349 | } |
350 | |
351 | void ProStringList::removeAll(const char *str) |
352 | { |
353 | for (int i = size(); --i >= 0; ) |
354 | if (at(i) == str) |
355 | remove(i); |
356 | } |
357 | |
358 | void ProStringList::removeEach(const ProStringList &value) |
359 | { |
360 | for (const ProString &str : value) { |
361 | if (isEmpty()) |
362 | break; |
363 | if (!str.isEmpty()) |
364 | removeAll(str); |
365 | } |
366 | } |
367 | |
368 | void ProStringList::removeEmpty() |
369 | { |
370 | for (int i = size(); --i >= 0;) |
371 | if (at(i).isEmpty()) |
372 | remove(i); |
373 | } |
374 | |
375 | void ProStringList::removeDuplicates() |
376 | { |
377 | QDuplicateTracker<ProString> seen(size()); |
378 | removeIf(pred: [&](const ProString &s) { return seen.hasSeen(s); }); |
379 | } |
380 | |
381 | void ProStringList::insertUnique(const ProStringList &value) |
382 | { |
383 | for (const ProString &str : value) |
384 | if (!str.isEmpty() && !contains(str)) |
385 | append(t: str); |
386 | } |
387 | |
388 | ProStringList::ProStringList(const QStringList &list) |
389 | { |
390 | reserve(asize: list.size()); |
391 | for (const QString &str : list) |
392 | *this << ProString(str); |
393 | } |
394 | |
395 | QStringList ProStringList::toQStringList() const |
396 | { |
397 | QStringList ret; |
398 | ret.reserve(asize: size()); |
399 | for (const auto &e : *this) |
400 | ret.append(t: e.toQString()); |
401 | return ret; |
402 | } |
403 | |
404 | bool ProStringList::contains(const ProString &str, Qt::CaseSensitivity cs) const |
405 | { |
406 | for (int i = 0; i < size(); i++) |
407 | if (!at(i).compare(sub: str, cs)) |
408 | return true; |
409 | return false; |
410 | } |
411 | |
412 | bool ProStringList::contains(QStringView str, Qt::CaseSensitivity cs) const |
413 | { |
414 | for (int i = 0; i < size(); i++) |
415 | if (!at(i).toQStringView().compare(other: str, cs)) |
416 | return true; |
417 | return false; |
418 | } |
419 | |
420 | bool ProStringList::contains(const char *str, Qt::CaseSensitivity cs) const |
421 | { |
422 | for (int i = 0; i < size(); i++) |
423 | if (!at(i).compare(sub: str, cs)) |
424 | return true; |
425 | return false; |
426 | } |
427 | |
428 | ProFile::ProFile(int id, const QString &fileName) |
429 | : m_refCount(1), |
430 | m_fileName(fileName), |
431 | m_id(id), |
432 | m_ok(true), |
433 | m_hostBuild(false) |
434 | { |
435 | if (!fileName.startsWith(c: QLatin1Char('('))) |
436 | m_directoryName = QFileInfo( // qmake sickness: canonicalize only the directory! |
437 | fileName.left(n: fileName.lastIndexOf(c: QLatin1Char('/')))).canonicalFilePath(); |
438 | } |
439 | |
440 | ProFile::~ProFile() |
441 | { |
442 | } |
443 | |
444 | ProString ProFile::getStr(const ushort *&tPtr) |
445 | { |
446 | uint len = *tPtr++; |
447 | ProString ret(items(), tPtr - tokPtr(), len); |
448 | ret.setSource(m_id); |
449 | tPtr += len; |
450 | return ret; |
451 | } |
452 | |
453 | ProKey ProFile::getHashStr(const ushort *&tPtr) |
454 | { |
455 | uint hash = *tPtr++; |
456 | hash |= (uint)*tPtr++ << 16; |
457 | uint len = *tPtr++; |
458 | ProKey ret(items(), tPtr - tokPtr(), len, hash); |
459 | tPtr += len; |
460 | return ret; |
461 | } |
462 | |
463 | QDebug operator<<(QDebug debug, const ProString &str) |
464 | { |
465 | return debug << str.toQString(); |
466 | } |
467 | |
468 | QT_END_NAMESPACE |
469 | |