1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29
30#include <QtTest/QtTest>
31#include <QtCore/QString>
32#include <QtCore/qarraydata.h>
33
34#include "simplevector.h"
35
36struct SharedNullVerifier
37{
38 SharedNullVerifier()
39 {
40 Q_ASSERT(QArrayData::shared_null[0].ref.isStatic());
41 Q_ASSERT(QArrayData::shared_null[0].ref.isShared());
42#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
43 Q_ASSERT(QArrayData::shared_null[0].ref.isSharable());
44#endif
45 }
46};
47
48// This is meant to verify/ensure that shared_null is not being dynamically
49// initialized and stays away from the order-of-static-initialization fiasco.
50//
51// Of course, if this was to fail, qmake and the build should have crashed and
52// burned before we ever got to this point :-)
53SharedNullVerifier globalInit;
54
55class tst_QArrayData : public QObject
56{
57 Q_OBJECT
58
59private slots:
60 void referenceCounting();
61 void sharedNullEmpty();
62 void staticData();
63 void simpleVector();
64 void simpleVectorReserve_data();
65 void simpleVectorReserve();
66 void allocate_data();
67 void allocate();
68 void reallocate_data() { allocate_data(); }
69 void reallocate();
70 void alignment_data();
71 void alignment();
72 void typedData();
73 void gccBug43247();
74 void arrayOps();
75 void arrayOps2();
76 void setSharable_data();
77 void setSharable();
78 void fromRawData_data();
79 void fromRawData();
80 void literals();
81 void variadicLiterals();
82 void rValueReferences();
83 void grow();
84};
85
86template <class T> const T &const_(const T &t) { return t; }
87
88void tst_QArrayData::referenceCounting()
89{
90 {
91 // Reference counting initialized to 1 (owned)
92 QArrayData array = { .ref: { Q_BASIC_ATOMIC_INITIALIZER(1) }, .size: 0, .alloc: 0, .capacityReserved: 0, .offset: 0 };
93
94 QCOMPARE(array.ref.atomic.loadRelaxed(), 1);
95
96 QVERIFY(!array.ref.isStatic());
97#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
98 QVERIFY(array.ref.isSharable());
99#endif
100
101 QVERIFY(array.ref.ref());
102 QCOMPARE(array.ref.atomic.loadRelaxed(), 2);
103
104 QVERIFY(array.ref.deref());
105 QCOMPARE(array.ref.atomic.loadRelaxed(), 1);
106
107 QVERIFY(array.ref.ref());
108 QCOMPARE(array.ref.atomic.loadRelaxed(), 2);
109
110 QVERIFY(array.ref.deref());
111 QCOMPARE(array.ref.atomic.loadRelaxed(), 1);
112
113 QVERIFY(!array.ref.deref());
114 QCOMPARE(array.ref.atomic.loadRelaxed(), 0);
115
116 // Now would be a good time to free/release allocated data
117 }
118
119#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
120 {
121 // Reference counting initialized to 0 (non-sharable)
122 QArrayData array = { .ref: { Q_BASIC_ATOMIC_INITIALIZER(0) }, .size: 0, .alloc: 0, .capacityReserved: 0, .offset: 0 };
123
124 QCOMPARE(array.ref.atomic.loadRelaxed(), 0);
125
126 QVERIFY(!array.ref.isStatic());
127 QVERIFY(!array.ref.isSharable());
128
129 QVERIFY(!array.ref.ref());
130 // Reference counting fails, data should be copied
131 QCOMPARE(array.ref.atomic.loadRelaxed(), 0);
132
133 QVERIFY(!array.ref.deref());
134 QCOMPARE(array.ref.atomic.loadRelaxed(), 0);
135
136 // Free/release data
137 }
138#endif
139
140 {
141 // Reference counting initialized to -1 (static read-only data)
142 QArrayData array = { Q_REFCOUNT_INITIALIZE_STATIC, .size: 0, .alloc: 0, .capacityReserved: 0, .offset: 0 };
143
144 QCOMPARE(array.ref.atomic.loadRelaxed(), -1);
145
146 QVERIFY(array.ref.isStatic());
147#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
148 QVERIFY(array.ref.isSharable());
149#endif
150
151 QVERIFY(array.ref.ref());
152 QCOMPARE(array.ref.atomic.loadRelaxed(), -1);
153
154 QVERIFY(array.ref.deref());
155 QCOMPARE(array.ref.atomic.loadRelaxed(), -1);
156
157 }
158}
159
160void tst_QArrayData::sharedNullEmpty()
161{
162 QArrayData *null = const_cast<QArrayData *>(QArrayData::shared_null);
163 QArrayData *empty = QArrayData::allocate(objectSize: 1, Q_ALIGNOF(QArrayData), capacity: 0);
164
165 QVERIFY(null->ref.isStatic());
166 QVERIFY(null->ref.isShared());
167
168 QVERIFY(empty->ref.isStatic());
169 QVERIFY(empty->ref.isShared());
170
171 QCOMPARE(null->ref.atomic.loadRelaxed(), -1);
172 QCOMPARE(empty->ref.atomic.loadRelaxed(), -1);
173
174#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
175 QVERIFY(null->ref.isSharable());
176 QVERIFY(empty->ref.isSharable());
177#endif
178
179 QVERIFY(null->ref.ref());
180 QVERIFY(empty->ref.ref());
181
182 QCOMPARE(null->ref.atomic.loadRelaxed(), -1);
183 QCOMPARE(empty->ref.atomic.loadRelaxed(), -1);
184
185 QVERIFY(null->ref.deref());
186 QVERIFY(empty->ref.deref());
187
188 QCOMPARE(null->ref.atomic.loadRelaxed(), -1);
189 QCOMPARE(empty->ref.atomic.loadRelaxed(), -1);
190
191 QVERIFY(null != empty);
192
193 QCOMPARE(null->size, 0);
194 QCOMPARE(null->alloc, 0u);
195 QCOMPARE(null->capacityReserved, 0u);
196
197 QCOMPARE(empty->size, 0);
198 QCOMPARE(empty->alloc, 0u);
199 QCOMPARE(empty->capacityReserved, 0u);
200}
201
202void tst_QArrayData::staticData()
203{
204 QStaticArrayData<char, 10> charArray = {
205 Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(char, 10),
206 .data: { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' }
207 };
208 QStaticArrayData<int, 10> intArray = {
209 Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(int, 10),
210 .data: { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }
211 };
212 QStaticArrayData<double, 10> doubleArray = {
213 Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(double, 10),
214 .data: { 0.f, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f }
215 };
216
217 QCOMPARE(charArray.header.size, 10);
218 QCOMPARE(intArray.header.size, 10);
219 QCOMPARE(doubleArray.header.size, 10);
220
221 QCOMPARE(charArray.header.data(), reinterpret_cast<void *>(&charArray.data));
222 QCOMPARE(intArray.header.data(), reinterpret_cast<void *>(&intArray.data));
223 QCOMPARE(doubleArray.header.data(), reinterpret_cast<void *>(&doubleArray.data));
224}
225
226void tst_QArrayData::simpleVector()
227{
228 QArrayData data0 = { Q_REFCOUNT_INITIALIZE_STATIC, .size: 0, .alloc: 0, .capacityReserved: 0, .offset: 0 };
229 QStaticArrayData<int, 7> data1 = {
230 Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(int, 7),
231 .data: { 0, 1, 2, 3, 4, 5, 6 }
232 };
233
234 int array[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
235
236 SimpleVector<int> v1;
237 SimpleVector<int> v2(v1);
238 SimpleVector<int> v3(static_cast<QTypedArrayData<int> *>(&data0));
239 SimpleVector<int> v4(static_cast<QTypedArrayData<int> *>(&data1.header));
240 SimpleVector<int> v5(static_cast<QTypedArrayData<int> *>(&data0));
241 SimpleVector<int> v6(static_cast<QTypedArrayData<int> *>(&data1.header));
242 SimpleVector<int> v7(10, 5);
243 SimpleVector<int> v8(array, array + sizeof(array)/sizeof(*array));
244
245 v3 = v1;
246 v1.swap(other&: v3);
247 v4.clear();
248
249 QVERIFY(v1.isNull());
250 QVERIFY(v2.isNull());
251 QVERIFY(v3.isNull());
252 QVERIFY(v4.isNull());
253 QVERIFY(!v5.isNull());
254 QVERIFY(!v6.isNull());
255 QVERIFY(!v7.isNull());
256 QVERIFY(!v8.isNull());
257
258 QVERIFY(v1.isEmpty());
259 QVERIFY(v2.isEmpty());
260 QVERIFY(v3.isEmpty());
261 QVERIFY(v4.isEmpty());
262 QVERIFY(v5.isEmpty());
263 QVERIFY(!v6.isEmpty());
264 QVERIFY(!v7.isEmpty());
265 QVERIFY(!v8.isEmpty());
266
267 QCOMPARE(v1.size(), size_t(0));
268 QCOMPARE(v2.size(), size_t(0));
269 QCOMPARE(v3.size(), size_t(0));
270 QCOMPARE(v4.size(), size_t(0));
271 QCOMPARE(v5.size(), size_t(0));
272 QCOMPARE(v6.size(), size_t(7));
273 QCOMPARE(v7.size(), size_t(10));
274 QCOMPARE(v8.size(), size_t(10));
275
276 QCOMPARE(v1.capacity(), size_t(0));
277 QCOMPARE(v2.capacity(), size_t(0));
278 QCOMPARE(v3.capacity(), size_t(0));
279 QCOMPARE(v4.capacity(), size_t(0));
280 QCOMPARE(v5.capacity(), size_t(0));
281 // v6.capacity() is unspecified, for now
282 QVERIFY(v7.capacity() >= size_t(10));
283 QVERIFY(v8.capacity() >= size_t(10));
284
285 QVERIFY(v1.isStatic());
286 QVERIFY(v2.isStatic());
287 QVERIFY(v3.isStatic());
288 QVERIFY(v4.isStatic());
289 QVERIFY(v5.isStatic());
290 QVERIFY(v6.isStatic());
291 QVERIFY(!v7.isStatic());
292 QVERIFY(!v8.isStatic());
293
294 QVERIFY(v1.isShared());
295 QVERIFY(v2.isShared());
296 QVERIFY(v3.isShared());
297 QVERIFY(v4.isShared());
298 QVERIFY(v5.isShared());
299 QVERIFY(v6.isShared());
300 QVERIFY(!v7.isShared());
301 QVERIFY((SimpleVector<int>(v7), v7.isShared()));
302 QVERIFY(!v7.isShared());
303 QVERIFY(!v8.isShared());
304
305#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
306 QVERIFY(v1.isSharable());
307 QVERIFY(v2.isSharable());
308 QVERIFY(v3.isSharable());
309 QVERIFY(v4.isSharable());
310 QVERIFY(v5.isSharable());
311 QVERIFY(v6.isSharable());
312 QVERIFY(v7.isSharable());
313 QVERIFY(v8.isSharable());
314#endif
315
316 QVERIFY(v1.isSharedWith(v2));
317 QVERIFY(v1.isSharedWith(v3));
318 QVERIFY(v1.isSharedWith(v4));
319 QVERIFY(!v1.isSharedWith(v5));
320 QVERIFY(!v1.isSharedWith(v6));
321
322 QCOMPARE(v1.constBegin(), v1.constEnd());
323 QCOMPARE(v4.constBegin(), v4.constEnd());
324 QCOMPARE((v6.constBegin() + v6.size()), v6.constEnd());
325 QCOMPARE((v7.constBegin() + v7.size()), v7.constEnd());
326 QCOMPARE((v8.constBegin() + v8.size()), v8.constEnd());
327
328 QVERIFY(v1 == v2);
329 QVERIFY(v1 == v3);
330 QVERIFY(v1 == v4);
331 QVERIFY(v1 == v5);
332 QVERIFY(!(v1 == v6));
333
334 QVERIFY(v1 != v6);
335 QVERIFY(v4 != v6);
336 QVERIFY(v5 != v6);
337 QVERIFY(!(v1 != v5));
338
339 QVERIFY(v1 < v6);
340 QVERIFY(!(v6 < v1));
341 QVERIFY(v6 > v1);
342 QVERIFY(!(v1 > v6));
343 QVERIFY(v1 <= v6);
344 QVERIFY(!(v6 <= v1));
345 QVERIFY(v6 >= v1);
346 QVERIFY(!(v1 >= v6));
347
348 {
349 SimpleVector<int> temp(v6);
350
351 QCOMPARE(const_(v6).front(), 0);
352 QCOMPARE(const_(v6).back(), 6);
353
354 QVERIFY(temp.isShared());
355 QVERIFY(temp.isSharedWith(v6));
356
357 QCOMPARE(temp.front(), 0);
358 QCOMPARE(temp.back(), 6);
359
360 // Detached
361 QVERIFY(!temp.isShared());
362 const int *const tempBegin = temp.begin();
363
364 for (size_t i = 0; i < v6.size(); ++i) {
365 QCOMPARE(const_(v6)[i], int(i));
366 QCOMPARE(const_(v6).at(i), int(i));
367 QCOMPARE(&const_(v6)[i], &const_(v6).at(i));
368
369 QCOMPARE(const_(v8)[i], const_(v6)[i]);
370
371 QCOMPARE(temp[i], int(i));
372 QCOMPARE(temp.at(i), int(i));
373 QCOMPARE(&temp[i], &temp.at(i));
374 }
375
376 // A single detach should do
377 QCOMPARE((const int *)temp.begin(), tempBegin);
378 }
379
380 {
381 int count = 0;
382 Q_FOREACH (int value, v7) {
383 QCOMPARE(value, 5);
384 ++count;
385 }
386
387 QCOMPARE(count, 10);
388 }
389
390 {
391 int count = 0;
392 Q_FOREACH (int value, v8) {
393 QCOMPARE(value, count);
394 ++count;
395 }
396
397 QCOMPARE(count, 10);
398 }
399
400 v5 = v6;
401 QVERIFY(v5.isSharedWith(v6));
402 QVERIFY(!v1.isSharedWith(v5));
403
404 v1.swap(other&: v6);
405 QVERIFY(v6.isNull());
406 QVERIFY(v1.isSharedWith(v5));
407
408 {
409 using std::swap;
410 swap(v1, v2&: v6);
411 QVERIFY(v5.isSharedWith(v6));
412 QVERIFY(!v1.isSharedWith(v5));
413 }
414
415 v1.prepend(first: array, last: array + sizeof(array)/sizeof(array[0]));
416 QCOMPARE(v1.size(), size_t(10));
417 QVERIFY(v1 == v8);
418
419 v6 = v1;
420 QVERIFY(v1.isSharedWith(v6));
421
422 v1.prepend(first: array, last: array + sizeof(array)/sizeof(array[0]));
423 QVERIFY(!v1.isSharedWith(v6));
424 QCOMPARE(v1.size(), size_t(20));
425 QCOMPARE(v6.size(), size_t(10));
426
427 for (int i = 0; i < 20; ++i)
428 QCOMPARE(v1[i], v6[i % 10]);
429
430 v1.clear();
431
432 v1.append(first: array, last: array + sizeof(array)/sizeof(array[0]));
433 // v1 is now [0 .. 9]
434 QCOMPARE(v1.size(), size_t(10));
435 QVERIFY(v1 == v8);
436
437 v6 = v1;
438 QVERIFY(v1.isSharedWith(v6));
439
440 v1.append(first: array, last: array + sizeof(array)/sizeof(array[0]));
441 // v1 is now [0 .. 9, 0 .. 9]
442 QVERIFY(!v1.isSharedWith(v6));
443 QCOMPARE(v1.size(), size_t(20));
444 QCOMPARE(v6.size(), size_t(10));
445
446 for (int i = 0; i < 20; ++i)
447 QCOMPARE(v1[i], v6[i % 10]);
448
449 v1.insert(position: 0, first: v6.constBegin(), last: v6.constEnd());
450 // v1 is now [ 0 .. 9, 0 .. 9, 0 .. 9]
451 QCOMPARE(v1.size(), size_t(30));
452
453 for (int i = 0; i < 30; ++i)
454 QCOMPARE(v1[i], v8[i % 10]);
455
456 v6 = v1;
457 QVERIFY(v1.isSharedWith(v6));
458
459 v1.insert(position: 10, first: v6.constBegin(), last: v6.constEnd());
460 // v1 is now [ 0..9, <new data>0..9, 0..9, 0..9</new data>, 0..9, 0..9 ]
461 QVERIFY(!v1.isSharedWith(v6));
462 QCOMPARE(v1.size(), size_t(60));
463 QCOMPARE(v6.size(), size_t(30));
464
465 for (int i = 0; i < 30; ++i)
466 QCOMPARE(v6[i], v8[i % 10]);
467 for (int i = 0; i < 60; ++i)
468 QCOMPARE(v1[i], v8[i % 10]);
469
470 v1.insert(position: int(v1.size()), first: v6.constBegin(), last: v6.constEnd());
471 // v1 is now [ 0..9 x 6, <new data>0..9 x 3</new data> ]
472 QCOMPARE(v1.size(), size_t(90));
473
474 for (int i = 0; i < 90; ++i)
475 QCOMPARE(v1[i], v8[i % 10]);
476
477 v1.insert(position: -1, first: v8.constBegin(), last: v8.constEnd());
478 // v1 is now [ 0..9 x 9, <new data>0..9</new data> ]
479 QCOMPARE(v1.size(), size_t(100));
480
481 for (int i = 0; i < 100; ++i)
482 QCOMPARE(v1[i], v8[i % 10]);
483
484 v1.insert(position: -11, first: v8.constBegin(), last: v8.constEnd());
485 // v1 is now [ 0..9 x 9, <new data>0..9</new data>, 0..9 ]
486 QCOMPARE(v1.size(), size_t(110));
487
488 for (int i = 0; i < 110; ++i)
489 QCOMPARE(v1[i], v8[i % 10]);
490
491 v1.insert(position: -200, first: v8.constBegin(), last: v8.constEnd());
492 // v1 is now [ <new data>0..9</new data>, 0..9 x 11 ]
493 QCOMPARE(v1.size(), size_t(120));
494
495 for (int i = 0; i < 120; ++i)
496 QCOMPARE(v1[i], v8[i % 10]);
497
498#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
499 {
500 v7.setSharable(true);
501 QVERIFY(v7.isSharable());
502
503 SimpleVector<int> copy1(v7);
504 QVERIFY(copy1.isSharedWith(v7));
505
506 v7.setSharable(false);
507 QVERIFY(!v7.isSharable());
508
509 QVERIFY(!copy1.isSharedWith(v7));
510 QCOMPARE(v7.size(), copy1.size());
511 for (size_t i = 0; i < copy1.size(); ++i)
512 QCOMPARE(v7[i], copy1[i]);
513
514 SimpleVector<int> clone(v7);
515 QVERIFY(!clone.isSharedWith(v7));
516 QCOMPARE(clone.size(), copy1.size());
517 for (size_t i = 0; i < copy1.size(); ++i)
518 QCOMPARE(clone[i], copy1[i]);
519
520 v7.setSharable(true);
521 QVERIFY(v7.isSharable());
522
523 SimpleVector<int> copy2(v7);
524 QVERIFY(copy2.isSharedWith(v7));
525 }
526
527 {
528 SimpleVector<int> null;
529 SimpleVector<int> empty(0, 5);
530
531 QVERIFY(null.isSharable());
532 QVERIFY(empty.isSharable());
533
534 null.setSharable(true);
535 empty.setSharable(true);
536
537 QVERIFY(null.isSharable());
538 QVERIFY(empty.isSharable());
539
540 QVERIFY(null.isEmpty());
541 QVERIFY(empty.isEmpty());
542
543 null.setSharable(false);
544 empty.setSharable(false);
545
546 QVERIFY(!null.isSharable());
547 QVERIFY(!empty.isSharable());
548
549 QVERIFY(null.isEmpty());
550 QVERIFY(empty.isEmpty());
551
552 null.setSharable(true);
553 empty.setSharable(true);
554
555 QVERIFY(null.isSharable());
556 QVERIFY(empty.isSharable());
557
558 QVERIFY(null.isEmpty());
559 QVERIFY(empty.isEmpty());
560 }
561#endif
562}
563
564Q_DECLARE_METATYPE(SimpleVector<int>)
565
566void tst_QArrayData::simpleVectorReserve_data()
567{
568 QTest::addColumn<SimpleVector<int> >(name: "vector");
569 QTest::addColumn<size_t>(name: "capacity");
570 QTest::addColumn<size_t>(name: "size");
571
572 QTest::newRow(dataTag: "null") << SimpleVector<int>() << size_t(0) << size_t(0);
573 QTest::newRow(dataTag: "empty") << SimpleVector<int>(0, 42) << size_t(0) << size_t(0);
574 QTest::newRow(dataTag: "non-empty") << SimpleVector<int>(5, 42) << size_t(5) << size_t(5);
575
576 static const QStaticArrayData<int, 15> array = {
577 Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(int, 15),
578 .data: { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } };
579 QArrayDataPointerRef<int> p = {
580 .ptr: static_cast<QTypedArrayData<int> *>(
581 const_cast<QArrayData *>(&array.header)) };
582
583 QTest::newRow(dataTag: "static") << SimpleVector<int>(p) << size_t(0) << size_t(15);
584 QTest::newRow(dataTag: "raw-data") << SimpleVector<int>::fromRawData(data: array.data, size: 15) << size_t(0) << size_t(15);
585}
586
587void tst_QArrayData::simpleVectorReserve()
588{
589 QFETCH(SimpleVector<int>, vector);
590 QFETCH(size_t, capacity);
591 QFETCH(size_t, size);
592
593 QVERIFY(!capacity || capacity >= size);
594
595 QCOMPARE(vector.capacity(), capacity);
596 QCOMPARE(vector.size(), size);
597
598 const SimpleVector<int> copy(vector);
599
600 vector.reserve(n: 0);
601 QCOMPARE(vector.capacity(), capacity);
602 QCOMPARE(vector.size(), size);
603
604 vector.reserve(n: 10);
605
606 // zero-capacity (immutable) resets with detach
607 if (!capacity)
608 capacity = size;
609
610 QCOMPARE(vector.capacity(), qMax(size_t(10), capacity));
611 QCOMPARE(vector.size(), size);
612
613 vector.reserve(n: 20);
614 QCOMPARE(vector.capacity(), size_t(20));
615 QCOMPARE(vector.size(), size);
616
617 vector.reserve(n: 30);
618 QCOMPARE(vector.capacity(), size_t(30));
619 QCOMPARE(vector.size(), size);
620
621 QVERIFY(vector == copy);
622}
623
624struct Deallocator
625{
626 Deallocator(size_t objectSize, size_t alignment)
627 : objectSize(objectSize)
628 , alignment(alignment)
629 {
630 }
631
632 ~Deallocator()
633 {
634 Q_FOREACH (QArrayData *data, headers)
635 QArrayData::deallocate(data, objectSize, alignment);
636 }
637
638 size_t objectSize;
639 size_t alignment;
640 QVector<QArrayData *> headers;
641};
642
643Q_DECLARE_METATYPE(const QArrayData *)
644Q_DECLARE_METATYPE(QArrayData::AllocationOptions)
645
646void tst_QArrayData::allocate_data()
647{
648 QTest::addColumn<size_t>(name: "objectSize");
649 QTest::addColumn<size_t>(name: "alignment");
650 QTest::addColumn<QArrayData::AllocationOptions>(name: "allocateOptions");
651 QTest::addColumn<bool>(name: "isCapacityReserved");
652 QTest::addColumn<bool>(name: "isSharable"); // ### Qt6: remove
653 QTest::addColumn<const QArrayData *>(name: "commonEmpty");
654
655 struct {
656 char const *typeName;
657 size_t objectSize;
658 size_t alignment;
659 } types[] = {
660 { .typeName: "char", .objectSize: sizeof(char), Q_ALIGNOF(char) },
661 { .typeName: "short", .objectSize: sizeof(short), Q_ALIGNOF(short) },
662 { .typeName: "void *", .objectSize: sizeof(void *), Q_ALIGNOF(void *) }
663 };
664
665 QArrayData *shared_empty = QArrayData::allocate(objectSize: 0, Q_ALIGNOF(QArrayData), capacity: 0);
666 QVERIFY(shared_empty);
667
668#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
669 QArrayData *unsharable_empty = QArrayData::allocate(objectSize: 0, Q_ALIGNOF(QArrayData), capacity: 0, options: QArrayData::Unsharable);
670 QVERIFY(unsharable_empty);
671#endif
672
673 struct {
674 char const *description;
675 QArrayData::AllocationOptions allocateOptions;
676 bool isCapacityReserved;
677 bool isSharable;
678 const QArrayData *commonEmpty;
679 } options[] = {
680 { .description: "Default", .allocateOptions: QArrayData::Default, .isCapacityReserved: false, .isSharable: true, .commonEmpty: shared_empty },
681 { .description: "Reserved", .allocateOptions: QArrayData::CapacityReserved, .isCapacityReserved: true, .isSharable: true, .commonEmpty: shared_empty },
682#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
683 { .description: "Reserved | Unsharable",
684 .allocateOptions: QArrayData::CapacityReserved | QArrayData::Unsharable, .isCapacityReserved: true, .isSharable: false,
685 .commonEmpty: unsharable_empty },
686 { .description: "Unsharable", .allocateOptions: QArrayData::Unsharable, .isCapacityReserved: false, .isSharable: false, .commonEmpty: unsharable_empty },
687#endif
688 { .description: "Grow", .allocateOptions: QArrayData::Grow, .isCapacityReserved: false, .isSharable: true, .commonEmpty: shared_empty }
689 };
690
691 for (size_t i = 0; i < sizeof(types)/sizeof(types[0]); ++i)
692 for (size_t j = 0; j < sizeof(options)/sizeof(options[0]); ++j)
693 QTest::newRow(qPrintable(
694 QLatin1String(types[i].typeName)
695 + QLatin1String(": ")
696 + QLatin1String(options[j].description)))
697 << types[i].objectSize << types[i].alignment
698 << options[j].allocateOptions << options[j].isCapacityReserved
699 << options[j].isSharable << options[j].commonEmpty;
700}
701
702void tst_QArrayData::allocate()
703{
704 QFETCH(size_t, objectSize);
705 QFETCH(size_t, alignment);
706 QFETCH(QArrayData::AllocationOptions, allocateOptions);
707 QFETCH(bool, isCapacityReserved);
708 QFETCH(const QArrayData *, commonEmpty);
709
710 // Minimum alignment that can be requested is that of QArrayData.
711 // Typically, this alignment is sizeof(void *) and ensured by malloc.
712 size_t minAlignment = qMax(a: alignment, Q_ALIGNOF(QArrayData));
713
714 // Shared Empty
715 QCOMPARE(QArrayData::allocate(objectSize, minAlignment, 0,
716 QArrayData::AllocationOptions(allocateOptions)), commonEmpty);
717
718 Deallocator keeper(objectSize, minAlignment);
719 keeper.headers.reserve(asize: 1024);
720
721 for (int capacity = 1; capacity <= 1024; capacity <<= 1) {
722 QArrayData *data = QArrayData::allocate(objectSize, alignment: minAlignment,
723 capacity, options: QArrayData::AllocationOptions(allocateOptions));
724 keeper.headers.append(t: data);
725
726 QCOMPARE(data->size, 0);
727 if (allocateOptions & QArrayData::Grow)
728 QVERIFY(data->alloc > uint(capacity));
729 else
730 QCOMPARE(data->alloc, uint(capacity));
731 QCOMPARE(data->capacityReserved, uint(isCapacityReserved));
732#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
733 QFETCH(bool, isSharable);
734 QCOMPARE(data->ref.isSharable(), isSharable);
735#endif
736
737 // Check that the allocated array can be used. Best tested with a
738 // memory checker, such as valgrind, running.
739 ::memset(s: data->data(), c: 'A', n: objectSize * capacity);
740 }
741}
742
743void tst_QArrayData::reallocate()
744{
745 QFETCH(size_t, objectSize);
746 QFETCH(size_t, alignment);
747 QFETCH(QArrayData::AllocationOptions, allocateOptions);
748 QFETCH(bool, isCapacityReserved);
749
750 // Maximum alignment that can be requested is that of QArrayData,
751 // otherwise, we can't use reallocate().
752 Q_ASSERT(alignment <= Q_ALIGNOF(QArrayData));
753
754 // Minimum alignment that can be requested is that of QArrayData.
755 // Typically, this alignment is sizeof(void *) and ensured by malloc.
756 size_t minAlignment = qMax(a: alignment, Q_ALIGNOF(QArrayData));
757
758 int capacity = 10;
759 Deallocator keeper(objectSize, minAlignment);
760 QArrayData *data = QArrayData::allocate(objectSize, alignment: minAlignment, capacity,
761 options: QArrayData::AllocationOptions(allocateOptions) & ~QArrayData::Grow);
762 keeper.headers.append(t: data);
763
764 memset(s: data->data(), c: 'A', n: objectSize * capacity);
765 data->size = capacity;
766
767 // now try to reallocate
768 int newCapacity = 40;
769 data = QArrayData::reallocateUnaligned(data, objectSize, newCapacity,
770 newOptions: QArrayData::AllocationOptions(allocateOptions));
771 QVERIFY(data);
772 keeper.headers.clear();
773 keeper.headers.append(t: data);
774
775 QCOMPARE(data->size, capacity);
776 if (allocateOptions & QArrayData::Grow)
777 QVERIFY(data->alloc > uint(newCapacity));
778 else
779 QCOMPARE(data->alloc, uint(newCapacity));
780 QCOMPARE(data->capacityReserved, uint(isCapacityReserved));
781#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
782 QFETCH(bool, isSharable);
783 QCOMPARE(data->ref.isSharable(), isSharable);
784#endif
785
786 for (int i = 0; i < capacity; ++i)
787 QCOMPARE(static_cast<char *>(data->data())[i], 'A');
788}
789
790class Unaligned
791{
792 char dummy[8];
793};
794
795void tst_QArrayData::alignment_data()
796{
797 QTest::addColumn<size_t>(name: "alignment");
798
799 for (size_t i = 1; i < 10; ++i) {
800 size_t alignment = size_t(1u) << i;
801 QTest::newRow(qPrintable(QString::number(alignment))) << alignment;
802 }
803}
804
805void tst_QArrayData::alignment()
806{
807 QFETCH(size_t, alignment);
808
809 // Minimum alignment that can be requested is that of QArrayData.
810 // Typically, this alignment is sizeof(void *) and ensured by malloc.
811 size_t minAlignment = qMax(a: alignment, Q_ALIGNOF(QArrayData));
812
813 Deallocator keeper(sizeof(Unaligned), minAlignment);
814 keeper.headers.reserve(asize: 100);
815
816 for (int i = 0; i < 100; ++i) {
817 QArrayData *data = QArrayData::allocate(objectSize: sizeof(Unaligned),
818 alignment: minAlignment, capacity: 8, options: QArrayData::Default);
819 keeper.headers.append(t: data);
820
821 QVERIFY(data);
822 QCOMPARE(data->size, 0);
823 QVERIFY(data->alloc >= uint(8));
824
825 // These conditions should hold as long as header and array are
826 // allocated together
827 QVERIFY(data->offset >= qptrdiff(sizeof(QArrayData)));
828 QVERIFY(data->offset <= qptrdiff(sizeof(QArrayData)
829 + minAlignment - Q_ALIGNOF(QArrayData)));
830
831 // Data is aligned
832 QCOMPARE(quintptr(quintptr(data->data()) % alignment), quintptr(0u));
833
834 // Check that the allocated array can be used. Best tested with a
835 // memory checker, such as valgrind, running.
836 ::memset(s: data->data(), c: 'A', n: sizeof(Unaligned) * 8);
837 }
838}
839
840void tst_QArrayData::typedData()
841{
842 QStaticArrayData<int, 10> data = {
843 Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(int, 10),
844 .data: { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }
845 };
846 QCOMPARE(data.header.size, 10);
847
848 {
849 QTypedArrayData<int> *array =
850 static_cast<QTypedArrayData<int> *>(&data.header);
851 QCOMPARE(array->data(), data.data);
852
853 int j = 0;
854 for (QTypedArrayData<int>::iterator iter = array->begin();
855 iter != array->end(); ++iter, ++j)
856 QCOMPARE((const int *)iter, data.data + j);
857 QCOMPARE(j, 10);
858 }
859
860 {
861 const QTypedArrayData<int> *array =
862 static_cast<const QTypedArrayData<int> *>(&data.header);
863
864 QCOMPARE(array->data(), data.data);
865
866 int j = 0;
867 for (QTypedArrayData<int>::const_iterator iter = array->begin();
868 iter != array->end(); ++iter, ++j)
869 QCOMPARE((const int *)iter, data.data + j);
870 QCOMPARE(j, 10);
871 }
872
873 {
874 QTypedArrayData<int> *null = QTypedArrayData<int>::sharedNull();
875 QTypedArrayData<int> *empty = QTypedArrayData<int>::allocate(capacity: 0);
876
877 QVERIFY(null != empty);
878
879 QCOMPARE(null->size, 0);
880 QCOMPARE(empty->size, 0);
881
882 QCOMPARE(null->begin(), null->end());
883 QCOMPARE(empty->begin(), empty->end());
884 }
885
886
887 {
888 Deallocator keeper(sizeof(char),
889 Q_ALIGNOF(QTypedArrayData<char>::AlignmentDummy));
890 QArrayData *array = QTypedArrayData<char>::allocate(capacity: 10);
891 keeper.headers.append(t: array);
892
893 QVERIFY(array);
894 QCOMPARE(array->size, 0);
895 QCOMPARE(array->alloc, 10u);
896
897 // Check that the allocated array can be used. Best tested with a
898 // memory checker, such as valgrind, running.
899 ::memset(s: array->data(), c: 0, n: 10 * sizeof(char));
900
901 keeper.headers.clear();
902 QTypedArrayData<short>::deallocate(data: array);
903
904 QVERIFY(true);
905 }
906
907 {
908 Deallocator keeper(sizeof(short),
909 Q_ALIGNOF(QTypedArrayData<short>::AlignmentDummy));
910 QArrayData *array = QTypedArrayData<short>::allocate(capacity: 10);
911 keeper.headers.append(t: array);
912
913 QVERIFY(array);
914 QCOMPARE(array->size, 0);
915 QCOMPARE(array->alloc, 10u);
916
917 // Check that the allocated array can be used. Best tested with a
918 // memory checker, such as valgrind, running.
919 ::memset(s: array->data(), c: 0, n: 10 * sizeof(short));
920
921 keeper.headers.clear();
922 QTypedArrayData<short>::deallocate(data: array);
923
924 QVERIFY(true);
925 }
926
927 {
928 Deallocator keeper(sizeof(double),
929 Q_ALIGNOF(QTypedArrayData<double>::AlignmentDummy));
930 QArrayData *array = QTypedArrayData<double>::allocate(capacity: 10);
931 keeper.headers.append(t: array);
932
933 QVERIFY(array);
934 QCOMPARE(array->size, 0);
935 QCOMPARE(array->alloc, 10u);
936
937 // Check that the allocated array can be used. Best tested with a
938 // memory checker, such as valgrind, running.
939 ::memset(s: array->data(), c: 0, n: 10 * sizeof(double));
940
941 keeper.headers.clear();
942 QTypedArrayData<double>::deallocate(data: array);
943
944 QVERIFY(true);
945 }
946}
947
948void tst_QArrayData::gccBug43247()
949{
950 // This test tries to verify QArrayData is not affected by GCC optimizer
951 // bug #43247.
952 // Reported on GCC 4.4.3, Linux, affects QVector
953
954 QTest::ignoreMessage(type: QtDebugMsg, message: "GCC Optimization bug #43247 not triggered (3)");
955 QTest::ignoreMessage(type: QtDebugMsg, message: "GCC Optimization bug #43247 not triggered (4)");
956 QTest::ignoreMessage(type: QtDebugMsg, message: "GCC Optimization bug #43247 not triggered (5)");
957 QTest::ignoreMessage(type: QtDebugMsg, message: "GCC Optimization bug #43247 not triggered (6)");
958 QTest::ignoreMessage(type: QtDebugMsg, message: "GCC Optimization bug #43247 not triggered (7)");
959
960 SimpleVector<int> array(10, 0);
961 // QVector<int> vector(10, 0);
962
963 for (int i = 0; i < 10; ++i) {
964 if (i >= 3 && i < 8)
965 qDebug(msg: "GCC Optimization bug #43247 not triggered (%i)", i);
966
967 // When access to data is implemented through an array of size 1, this
968 // line lets the compiler assume i == 0, and the conditional above is
969 // skipped.
970 QVERIFY(array.at(i) == 0);
971 // QVERIFY(vector.at(i) == 0);
972 }
973}
974
975struct CountedObject
976{
977 CountedObject()
978 : id(liveCount++)
979 , flags(DefaultConstructed)
980 {
981 }
982
983 CountedObject(const CountedObject &other)
984 : id(other.id)
985 , flags(other.flags == DefaultConstructed
986 ? ObjectFlags(CopyConstructed | DefaultConstructed)
987 : CopyConstructed)
988 {
989 ++liveCount;
990 }
991
992 ~CountedObject()
993 {
994 --liveCount;
995 }
996
997 CountedObject &operator=(const CountedObject &other)
998 {
999 flags = ObjectFlags(other.flags | CopyAssigned);
1000 id = other.id;
1001 return *this;
1002 }
1003
1004 struct LeakChecker
1005 {
1006 LeakChecker()
1007 : previousLiveCount(liveCount)
1008 {
1009 }
1010
1011 ~LeakChecker()
1012 {
1013 QCOMPARE(liveCount, previousLiveCount);
1014 }
1015
1016 private:
1017 const size_t previousLiveCount;
1018 };
1019
1020 enum ObjectFlags {
1021 DefaultConstructed = 1,
1022 CopyConstructed = 2,
1023 CopyAssigned = 4
1024 };
1025
1026 size_t id; // not unique
1027 ObjectFlags flags;
1028
1029 static size_t liveCount;
1030};
1031
1032size_t CountedObject::liveCount = 0;
1033
1034void tst_QArrayData::arrayOps()
1035{
1036 CountedObject::LeakChecker leakChecker; Q_UNUSED(leakChecker)
1037
1038 const int intArray[5] = { 80, 101, 100, 114, 111 };
1039 const QString stringArray[5] = {
1040 QLatin1String("just"),
1041 QLatin1String("for"),
1042 QLatin1String("testing"),
1043 QLatin1String("a"),
1044 QLatin1String("vector")
1045 };
1046 const CountedObject objArray[5];
1047
1048 QVERIFY(!QTypeInfo<int>::isComplex && !QTypeInfo<int>::isStatic);
1049 QVERIFY(QTypeInfo<QString>::isComplex && !QTypeInfo<QString>::isStatic);
1050 QVERIFY(QTypeInfo<CountedObject>::isComplex && QTypeInfo<CountedObject>::isStatic);
1051
1052 QCOMPARE(CountedObject::liveCount, size_t(5));
1053 for (size_t i = 0; i < 5; ++i)
1054 QCOMPARE(objArray[i].id, i);
1055
1056 ////////////////////////////////////////////////////////////////////////////
1057 // copyAppend (I)
1058 SimpleVector<int> vi(intArray, intArray + 5);
1059 SimpleVector<QString> vs(stringArray, stringArray + 5);
1060 SimpleVector<CountedObject> vo(objArray, objArray + 5);
1061
1062 QCOMPARE(CountedObject::liveCount, size_t(10));
1063 for (int i = 0; i < 5; ++i) {
1064 QCOMPARE(vi[i], intArray[i]);
1065 QVERIFY(vs[i].isSharedWith(stringArray[i]));
1066
1067 QCOMPARE(vo[i].id, objArray[i].id);
1068 QCOMPARE(int(vo[i].flags), CountedObject::CopyConstructed
1069 | CountedObject::DefaultConstructed);
1070 }
1071
1072 ////////////////////////////////////////////////////////////////////////////
1073 // destroyAll
1074 vi.clear();
1075 vs.clear();
1076 vo.clear();
1077
1078 QCOMPARE(CountedObject::liveCount, size_t(5));
1079
1080 ////////////////////////////////////////////////////////////////////////////
1081 // copyAppend (II)
1082 int referenceInt = 7;
1083 QString referenceString = QLatin1String("reference");
1084 CountedObject referenceObject;
1085
1086 vi = SimpleVector<int>(5, referenceInt);
1087 vs = SimpleVector<QString>(5, referenceString);
1088 vo = SimpleVector<CountedObject>(5, referenceObject);
1089
1090 QCOMPARE(vi.size(), size_t(5));
1091 QCOMPARE(vs.size(), size_t(5));
1092 QCOMPARE(vo.size(), size_t(5));
1093
1094 QCOMPARE(CountedObject::liveCount, size_t(11));
1095 for (int i = 0; i < 5; ++i) {
1096 QCOMPARE(vi[i], referenceInt);
1097 QVERIFY(vs[i].isSharedWith(referenceString));
1098
1099 QCOMPARE(vo[i].id, referenceObject.id);
1100 QCOMPARE(int(vo[i].flags), CountedObject::CopyConstructed
1101 | CountedObject::DefaultConstructed);
1102 }
1103
1104 ////////////////////////////////////////////////////////////////////////////
1105 // insert
1106 vi.reserve(n: 30);
1107 vs.reserve(n: 30);
1108 vo.reserve(n: 30);
1109
1110 QCOMPARE(vi.size(), size_t(5));
1111 QCOMPARE(vs.size(), size_t(5));
1112 QCOMPARE(vo.size(), size_t(5));
1113
1114 QVERIFY(vi.capacity() >= 30);
1115 QVERIFY(vs.capacity() >= 30);
1116 QVERIFY(vo.capacity() >= 30);
1117
1118 // Displace as many elements as array is extended by
1119 vi.insert(position: 0, first: intArray, last: intArray + 5);
1120 vs.insert(position: 0, first: stringArray, last: stringArray + 5);
1121 vo.insert(position: 0, first: objArray, last: objArray + 5);
1122
1123 QCOMPARE(vi.size(), size_t(10));
1124 QCOMPARE(vs.size(), size_t(10));
1125 QCOMPARE(vo.size(), size_t(10));
1126
1127 // Displace more elements than array is extended by
1128 vi.insert(position: 0, first: intArray, last: intArray + 5);
1129 vs.insert(position: 0, first: stringArray, last: stringArray + 5);
1130 vo.insert(position: 0, first: objArray, last: objArray + 5);
1131
1132 QCOMPARE(vi.size(), size_t(15));
1133 QCOMPARE(vs.size(), size_t(15));
1134 QCOMPARE(vo.size(), size_t(15));
1135
1136 // Displace less elements than array is extended by
1137 vi.insert(position: 5, first: vi.constBegin(), last: vi.constEnd());
1138 vs.insert(position: 5, first: vs.constBegin(), last: vs.constEnd());
1139 vo.insert(position: 5, first: vo.constBegin(), last: vo.constEnd());
1140
1141 QCOMPARE(vi.size(), size_t(30));
1142 QCOMPARE(vs.size(), size_t(30));
1143 QCOMPARE(vo.size(), size_t(30));
1144
1145 QCOMPARE(CountedObject::liveCount, size_t(36));
1146 for (int i = 0; i < 5; ++i) {
1147 QCOMPARE(vi[i], intArray[i % 5]);
1148 QVERIFY(vs[i].isSharedWith(stringArray[i % 5]));
1149
1150 QCOMPARE(vo[i].id, objArray[i % 5].id);
1151 QCOMPARE(int(vo[i].flags), CountedObject::DefaultConstructed
1152 | CountedObject::CopyAssigned);
1153 }
1154
1155 for (int i = 5; i < 15; ++i) {
1156 QCOMPARE(vi[i], intArray[i % 5]);
1157 QVERIFY(vs[i].isSharedWith(stringArray[i % 5]));
1158
1159 QCOMPARE(vo[i].id, objArray[i % 5].id);
1160 QCOMPARE(int(vo[i].flags), CountedObject::CopyConstructed
1161 | CountedObject::CopyAssigned);
1162 }
1163
1164 for (int i = 15; i < 20; ++i) {
1165 QCOMPARE(vi[i], referenceInt);
1166 QVERIFY(vs[i].isSharedWith(referenceString));
1167
1168 QCOMPARE(vo[i].id, referenceObject.id);
1169 QCOMPARE(int(vo[i].flags), CountedObject::CopyConstructed
1170 | CountedObject::CopyAssigned);
1171 }
1172
1173 for (int i = 20; i < 25; ++i) {
1174 QCOMPARE(vi[i], intArray[i % 5]);
1175 QVERIFY(vs[i].isSharedWith(stringArray[i % 5]));
1176
1177 QCOMPARE(vo[i].id, objArray[i % 5].id);
1178
1179 // Originally inserted as (DefaultConstructed | CopyAssigned), later
1180 // get shuffled into place by std::rotate (SimpleVector::insert,
1181 // overlapping mode).
1182 // Depending on implementation of rotate, final assignment can be:
1183 // - straight from source: DefaultConstructed | CopyAssigned
1184 // - through a temporary: CopyConstructed | CopyAssigned
1185 QCOMPARE(vo[i].flags & CountedObject::CopyAssigned,
1186 int(CountedObject::CopyAssigned));
1187 }
1188
1189 for (int i = 25; i < 30; ++i) {
1190 QCOMPARE(vi[i], referenceInt);
1191 QVERIFY(vs[i].isSharedWith(referenceString));
1192
1193 QCOMPARE(vo[i].id, referenceObject.id);
1194 QCOMPARE(int(vo[i].flags), CountedObject::CopyConstructed
1195 | CountedObject::CopyAssigned);
1196 }
1197}
1198
1199void tst_QArrayData::arrayOps2()
1200{
1201 CountedObject::LeakChecker leakChecker; Q_UNUSED(leakChecker)
1202
1203 ////////////////////////////////////////////////////////////////////////////
1204 // appendInitialize
1205 SimpleVector<int> vi(5);
1206 SimpleVector<QString> vs(5);
1207 SimpleVector<CountedObject> vo(5);
1208
1209 QCOMPARE(vi.size(), size_t(5));
1210 QCOMPARE(vs.size(), size_t(5));
1211 QCOMPARE(vo.size(), size_t(5));
1212
1213 QCOMPARE(CountedObject::liveCount, size_t(5));
1214 for (size_t i = 0; i < 5; ++i) {
1215 QCOMPARE(vi[i], 0);
1216 QVERIFY(vs[i].isNull());
1217
1218 QCOMPARE(vo[i].id, i);
1219 QCOMPARE(int(vo[i].flags), int(CountedObject::DefaultConstructed));
1220 }
1221
1222 ////////////////////////////////////////////////////////////////////////////
1223 // appendInitialize, again
1224
1225 // These will detach
1226 vi.resize(newSize: 10);
1227 vs.resize(newSize: 10);
1228 vo.resize(newSize: 10);
1229
1230 QCOMPARE(vi.size(), size_t(10));
1231 QCOMPARE(vs.size(), size_t(10));
1232 QCOMPARE(vo.size(), size_t(10));
1233
1234 QCOMPARE(CountedObject::liveCount, size_t(10));
1235 for (size_t i = 0; i < 5; ++i) {
1236 QCOMPARE(vi[i], 0);
1237 QVERIFY(vs[i].isNull());
1238
1239 QCOMPARE(vo[i].id, i);
1240 QCOMPARE(int(vo[i].flags), CountedObject::DefaultConstructed
1241 | CountedObject::CopyConstructed);
1242 }
1243
1244 for (size_t i = 5; i < 10; ++i) {
1245 QCOMPARE(vi[i], 0);
1246 QVERIFY(vs[i].isNull());
1247
1248 QCOMPARE(vo[i].id, i + 5);
1249 QCOMPARE(int(vo[i].flags), int(CountedObject::DefaultConstructed));
1250 }
1251
1252 ////////////////////////////////////////////////////////////////////////////
1253 // truncate
1254 QVERIFY(!vi.isShared());
1255 QVERIFY(!vs.isShared());
1256 QVERIFY(!vo.isShared());
1257
1258 // These shouldn't detach
1259 vi.resize(newSize: 7);
1260 vs.resize(newSize: 7);
1261 vo.resize(newSize: 7);
1262
1263 QCOMPARE(vi.size(), size_t(7));
1264 QCOMPARE(vs.size(), size_t(7));
1265 QCOMPARE(vo.size(), size_t(7));
1266
1267 QCOMPARE(CountedObject::liveCount, size_t(7));
1268 for (size_t i = 0; i < 5; ++i) {
1269 QCOMPARE(vi[i], 0);
1270 QVERIFY(vs[i].isNull());
1271
1272 QCOMPARE(vo[i].id, i);
1273 QCOMPARE(int(vo[i].flags), CountedObject::DefaultConstructed
1274 | CountedObject::CopyConstructed);
1275 }
1276
1277 for (size_t i = 5; i < 7; ++i) {
1278 QCOMPARE(vi[i], 0);
1279 QVERIFY(vs[i].isNull());
1280
1281 QCOMPARE(vo[i].id, i + 5);
1282 QCOMPARE(int(vo[i].flags), int(CountedObject::DefaultConstructed));
1283 }
1284
1285 ////////////////////////////////////////////////////////////////////////////
1286 vi.resize(newSize: 10);
1287 vs.resize(newSize: 10);
1288 vo.resize(newSize: 10);
1289
1290 for (size_t i = 7; i < 10; ++i) {
1291 vi[i] = int(i);
1292 vs[i] = QString::number(i);
1293
1294 QCOMPARE(vo[i].id, i);
1295 QCOMPARE(int(vo[i].flags), int(CountedObject::DefaultConstructed));
1296 }
1297
1298 QCOMPARE(CountedObject::liveCount, size_t(10));
1299
1300 ////////////////////////////////////////////////////////////////////////////
1301 // erase
1302 vi.erase(first: vi.begin() + 2, last: vi.begin() + 5);
1303 vs.erase(first: vs.begin() + 2, last: vs.begin() + 5);
1304 vo.erase(first: vo.begin() + 2, last: vo.begin() + 5);
1305
1306 QCOMPARE(vi.size(), size_t(7));
1307 QCOMPARE(vs.size(), size_t(7));
1308 QCOMPARE(vo.size(), size_t(7));
1309
1310 QCOMPARE(CountedObject::liveCount, size_t(7));
1311 for (size_t i = 0; i < 2; ++i) {
1312 QCOMPARE(vi[i], 0);
1313 QVERIFY(vs[i].isNull());
1314
1315 QCOMPARE(vo[i].id, i);
1316 QCOMPARE(int(vo[i].flags), CountedObject::DefaultConstructed
1317 | CountedObject::CopyConstructed);
1318 }
1319
1320 for (size_t i = 2; i < 4; ++i) {
1321 QCOMPARE(vi[i], 0);
1322 QVERIFY(vs[i].isNull());
1323
1324 QCOMPARE(vo[i].id, i + 8);
1325 QCOMPARE(int(vo[i].flags), int(CountedObject::DefaultConstructed)
1326 | CountedObject::CopyAssigned);
1327 }
1328
1329 for (size_t i = 4; i < 7; ++i) {
1330 QCOMPARE(vi[i], int(i + 3));
1331 QCOMPARE(vs[i], QString::number(i + 3));
1332
1333 QCOMPARE(vo[i].id, i + 3);
1334 QCOMPARE(int(vo[i].flags), CountedObject::DefaultConstructed
1335 | CountedObject::CopyAssigned);
1336 }
1337}
1338
1339Q_DECLARE_METATYPE(QArrayDataPointer<int>)
1340
1341static inline bool arrayIsFilledWith(const QArrayDataPointer<int> &array,
1342 int fillValue, size_t size)
1343{
1344 const int *iter = array->begin();
1345 const int *const end = array->end();
1346
1347 for (size_t i = 0; i < size; ++i, ++iter)
1348 if (*iter != fillValue)
1349 return false;
1350
1351 if (iter != end)
1352 return false;
1353
1354 return true;
1355}
1356
1357void tst_QArrayData::setSharable_data()
1358{
1359#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
1360 QTest::addColumn<QArrayDataPointer<int> >(name: "array");
1361 QTest::addColumn<size_t>(name: "size");
1362 QTest::addColumn<size_t>(name: "capacity");
1363 QTest::addColumn<bool>(name: "isCapacityReserved");
1364 QTest::addColumn<int>(name: "fillValue");
1365
1366 QArrayDataPointer<int> null;
1367 QArrayDataPointer<int> empty; empty.clear();
1368
1369 static QStaticArrayData<int, 10> staticArrayData = {
1370 Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(int, 10),
1371 .data: { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }
1372 };
1373
1374 QArrayDataPointer<int> emptyReserved(QTypedArrayData<int>::allocate(capacity: 5,
1375 options: QArrayData::CapacityReserved));
1376 QArrayDataPointer<int> nonEmpty(QTypedArrayData<int>::allocate(capacity: 5,
1377 options: QArrayData::Default));
1378 QArrayDataPointer<int> nonEmptyExtraCapacity(
1379 QTypedArrayData<int>::allocate(capacity: 10, options: QArrayData::Default));
1380 QArrayDataPointer<int> nonEmptyReserved(QTypedArrayData<int>::allocate(capacity: 15,
1381 options: QArrayData::CapacityReserved));
1382 QArrayDataPointer<int> staticArray(
1383 static_cast<QTypedArrayData<int> *>(&staticArrayData.header));
1384 QArrayDataPointer<int> rawData(
1385 QTypedArrayData<int>::fromRawData(data: staticArrayData.data, n: 10));
1386
1387 nonEmpty->copyAppend(n: 5, t: 1);
1388 nonEmptyExtraCapacity->copyAppend(n: 5, t: 1);
1389 nonEmptyReserved->copyAppend(n: 7, t: 2);
1390
1391 QTest::newRow(dataTag: "shared-null") << null << size_t(0) << size_t(0) << false << 0;
1392 QTest::newRow(dataTag: "shared-empty") << empty << size_t(0) << size_t(0) << false << 0;
1393 // unsharable-empty implicitly tested in shared-empty
1394 QTest::newRow(dataTag: "empty-reserved") << emptyReserved << size_t(0) << size_t(5) << true << 0;
1395 QTest::newRow(dataTag: "non-empty") << nonEmpty << size_t(5) << size_t(5) << false << 1;
1396 QTest::newRow(dataTag: "non-empty-extra-capacity") << nonEmptyExtraCapacity << size_t(5) << size_t(10) << false << 1;
1397 QTest::newRow(dataTag: "non-empty-reserved") << nonEmptyReserved << size_t(7) << size_t(15) << true << 2;
1398 QTest::newRow(dataTag: "static-array") << staticArray << size_t(10) << size_t(0) << false << 3;
1399 QTest::newRow(dataTag: "raw-data") << rawData << size_t(10) << size_t(0) << false << 3;
1400#endif
1401}
1402
1403void tst_QArrayData::setSharable()
1404{
1405#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
1406 QFETCH(QArrayDataPointer<int>, array);
1407 QFETCH(size_t, size);
1408 QFETCH(size_t, capacity);
1409 QFETCH(bool, isCapacityReserved);
1410 QFETCH(int, fillValue);
1411
1412 QVERIFY(array->ref.isShared()); // QTest has a copy
1413 QVERIFY(array->ref.isSharable());
1414
1415 QCOMPARE(size_t(array->size), size);
1416 QCOMPARE(size_t(array->alloc), capacity);
1417 QCOMPARE(bool(array->capacityReserved), isCapacityReserved);
1418 QVERIFY(arrayIsFilledWith(array, fillValue, size));
1419
1420 // shared-null becomes shared-empty, may otherwise detach
1421 array.setSharable(true);
1422
1423 QVERIFY(array->ref.isSharable());
1424 QVERIFY(arrayIsFilledWith(array, fillValue, size));
1425
1426 {
1427 QArrayDataPointer<int> copy(array);
1428 QVERIFY(array->ref.isShared());
1429 QVERIFY(array->ref.isSharable());
1430 QCOMPARE(copy.data(), array.data());
1431 }
1432
1433 // Unshare, must detach
1434 array.setSharable(false);
1435
1436 // Immutability (alloc == 0) is lost on detach, as is additional capacity
1437 // if capacityReserved flag is not set.
1438 if ((capacity == 0 && size != 0)
1439 || (!isCapacityReserved && capacity > size))
1440 capacity = size;
1441
1442 QVERIFY(!array->ref.isShared());
1443 QVERIFY(!array->ref.isSharable());
1444
1445 QCOMPARE(size_t(array->size), size);
1446 QCOMPARE(size_t(array->alloc), capacity);
1447 QCOMPARE(bool(array->capacityReserved), isCapacityReserved);
1448 QVERIFY(arrayIsFilledWith(array, fillValue, size));
1449
1450 {
1451 QArrayDataPointer<int> copy(array);
1452 QVERIFY(!array->ref.isShared());
1453 QVERIFY(!array->ref.isSharable());
1454
1455 // Null/empty is always shared
1456 QCOMPARE(copy->ref.isShared(), !(size || isCapacityReserved));
1457 QVERIFY(copy->ref.isSharable());
1458
1459 QCOMPARE(size_t(copy->size), size);
1460 QCOMPARE(size_t(copy->alloc), capacity);
1461 QCOMPARE(bool(copy->capacityReserved), isCapacityReserved);
1462 QVERIFY(arrayIsFilledWith(copy, fillValue, size));
1463 }
1464
1465 // Make sharable, again
1466 array.setSharable(true);
1467
1468 QCOMPARE(array->ref.isShared(), !(size || isCapacityReserved));
1469 QVERIFY(array->ref.isSharable());
1470
1471 QCOMPARE(size_t(array->size), size);
1472 QCOMPARE(size_t(array->alloc), capacity);
1473 QCOMPARE(bool(array->capacityReserved), isCapacityReserved);
1474 QVERIFY(arrayIsFilledWith(array, fillValue, size));
1475
1476 {
1477 QArrayDataPointer<int> copy(array);
1478 QVERIFY(array->ref.isShared());
1479 QCOMPARE(copy.data(), array.data());
1480 }
1481
1482 QCOMPARE(array->ref.isShared(), !(size || isCapacityReserved));
1483 QVERIFY(array->ref.isSharable());
1484#endif
1485}
1486
1487struct ResetOnDtor
1488{
1489 ResetOnDtor()
1490 : value_()
1491 {
1492 }
1493
1494 ResetOnDtor(int value)
1495 : value_(value)
1496 {
1497 }
1498
1499 ~ResetOnDtor()
1500 {
1501 value_ = 0;
1502 }
1503
1504 int value_;
1505};
1506
1507bool operator==(const ResetOnDtor &lhs, const ResetOnDtor &rhs)
1508{
1509 return lhs.value_ == rhs.value_;
1510}
1511
1512template <class T>
1513void fromRawData_impl()
1514{
1515 static const T array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
1516
1517 {
1518 // Default: Immutable, sharable
1519 SimpleVector<T> raw = SimpleVector<T>::fromRawData(array,
1520 sizeof(array)/sizeof(array[0]), QArrayData::Default);
1521
1522 QCOMPARE(raw.size(), size_t(11));
1523 QCOMPARE((const T *)raw.constBegin(), array);
1524 QCOMPARE((const T *)raw.constEnd(), (const T *)(array + sizeof(array)/sizeof(array[0])));
1525
1526 QVERIFY(!raw.isShared());
1527 QVERIFY(SimpleVector<T>(raw).isSharedWith(raw));
1528 QVERIFY(!raw.isShared());
1529
1530 // Detach
1531 QCOMPARE(raw.back(), T(11));
1532 QVERIFY((const T *)raw.constBegin() != array);
1533 }
1534
1535#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
1536 {
1537 // Immutable, unsharable
1538 SimpleVector<T> raw = SimpleVector<T>::fromRawData(array,
1539 sizeof(array)/sizeof(array[0]), QArrayData::Unsharable);
1540
1541 QCOMPARE(raw.size(), size_t(11));
1542 QCOMPARE((const T *)raw.constBegin(), array);
1543 QCOMPARE((const T *)raw.constEnd(), (const T *)(array + sizeof(array)/sizeof(array[0])));
1544
1545 SimpleVector<T> copy(raw);
1546 QVERIFY(!copy.isSharedWith(raw));
1547 QVERIFY(!raw.isShared());
1548
1549 QCOMPARE(copy.size(), size_t(11));
1550
1551 for (size_t i = 0; i < 11; ++i) {
1552 QCOMPARE(const_(copy)[i], const_(raw)[i]);
1553 QCOMPARE(const_(copy)[i], T(i + 1));
1554 }
1555
1556 QCOMPARE(raw.size(), size_t(11));
1557 QCOMPARE((const T *)raw.constBegin(), array);
1558 QCOMPARE((const T *)raw.constEnd(), (const T *)(array + sizeof(array)/sizeof(array[0])));
1559
1560 // Detach
1561 QCOMPARE(raw.back(), T(11));
1562 QVERIFY((const T *)raw.constBegin() != array);
1563 }
1564#endif
1565}
1566
1567void tst_QArrayData::fromRawData_data()
1568{
1569 QTest::addColumn<int>(name: "type");
1570
1571 QTest::newRow(dataTag: "int") << 0;
1572 QTest::newRow(dataTag: "ResetOnDtor") << 1;
1573}
1574void tst_QArrayData::fromRawData()
1575{
1576 QFETCH(int, type);
1577
1578 switch (type)
1579 {
1580 case 0:
1581 fromRawData_impl<int>();
1582 break;
1583 case 1:
1584 fromRawData_impl<ResetOnDtor>();
1585 break;
1586
1587 default:
1588 QFAIL("Unexpected type data");
1589 }
1590}
1591
1592void tst_QArrayData::literals()
1593{
1594 {
1595 QArrayDataPointer<char> d = Q_ARRAY_LITERAL(char, "ABCDEFGHIJ");
1596 QCOMPARE(d->size, 10 + 1);
1597 for (int i = 0; i < 10; ++i)
1598 QCOMPARE(d->data()[i], char('A' + i));
1599 }
1600
1601 {
1602 // wchar_t is not necessarily 2-bytes
1603 QArrayDataPointer<wchar_t> d = Q_ARRAY_LITERAL(wchar_t, L"ABCDEFGHIJ");
1604 QCOMPARE(d->size, 10 + 1);
1605 for (int i = 0; i < 10; ++i)
1606 QCOMPARE(d->data()[i], wchar_t('A' + i));
1607 }
1608
1609 {
1610 SimpleVector<char> v = Q_ARRAY_LITERAL(char, "ABCDEFGHIJ");
1611
1612 QVERIFY(!v.isNull());
1613 QVERIFY(!v.isEmpty());
1614 QCOMPARE(v.size(), size_t(11));
1615 // v.capacity() is unspecified, for now
1616
1617 QVERIFY(v.isStatic());
1618
1619#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
1620 QVERIFY(v.isSharable());
1621#endif
1622 QCOMPARE((void*)(const char*)(v.constBegin() + v.size()), (void*)(const char*)v.constEnd());
1623
1624 for (int i = 0; i < 10; ++i)
1625 QCOMPARE(const_(v)[i], char('A' + i));
1626 QCOMPARE(const_(v)[10], char('\0'));
1627 }
1628
1629 {
1630 struct LiteralType {
1631 int value;
1632 Q_DECL_CONSTEXPR LiteralType(int v = 0) : value(v) {}
1633 };
1634
1635 QArrayDataPointer<LiteralType> d = Q_ARRAY_LITERAL(LiteralType, LiteralType(0), LiteralType(1), LiteralType(2));
1636 QCOMPARE(d->size, 3);
1637 for (int i = 0; i < 3; ++i)
1638 QCOMPARE(d->data()[i].value, i);
1639 }
1640}
1641
1642// Variadic Q_ARRAY_LITERAL need to be available in the current configuration.
1643void tst_QArrayData::variadicLiterals()
1644{
1645 {
1646 QArrayDataPointer<int> d =
1647 Q_ARRAY_LITERAL(int, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
1648 QCOMPARE(d->size, 10);
1649 for (int i = 0; i < 10; ++i)
1650 QCOMPARE(d->data()[i], i);
1651 }
1652
1653 {
1654 QArrayDataPointer<char> d = Q_ARRAY_LITERAL(char,
1655 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J');
1656 QCOMPARE(d->size, 10);
1657 for (int i = 0; i < 10; ++i)
1658 QCOMPARE(d->data()[i], char('A' + i));
1659 }
1660
1661 {
1662 QArrayDataPointer<const char *> d = Q_ARRAY_LITERAL(const char *,
1663 "A", "B", "C", "D", "E", "F", "G", "H", "I", "J");
1664 QCOMPARE(d->size, 10);
1665 for (int i = 0; i < 10; ++i) {
1666 QCOMPARE(d->data()[i][0], char('A' + i));
1667 QCOMPARE(d->data()[i][1], '\0');
1668 }
1669 }
1670
1671 {
1672 SimpleVector<int> v = Q_ARRAY_LITERAL(int, 0, 1, 2, 3, 4, 5, 6);
1673
1674 QVERIFY(!v.isNull());
1675 QVERIFY(!v.isEmpty());
1676 QCOMPARE(v.size(), size_t(7));
1677 // v.capacity() is unspecified, for now
1678
1679 QVERIFY(v.isStatic());
1680
1681#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
1682 QVERIFY(v.isSharable());
1683#endif
1684 QCOMPARE((const int *)(v.constBegin() + v.size()), (const int *)v.constEnd());
1685
1686 for (int i = 0; i < 7; ++i)
1687 QCOMPARE(const_(v)[i], i);
1688 }
1689}
1690
1691// std::remove_reference is in C++11, but requires library support
1692template <class T> struct RemoveReference { typedef T Type; };
1693template <class T> struct RemoveReference<T &> { typedef T Type; };
1694
1695// single-argument std::move is in C++11, but requires library support
1696template <class T>
1697typename RemoveReference<T>::Type &&cxx11Move(T &&t)
1698{
1699 return static_cast<typename RemoveReference<T>::Type &&>(t);
1700}
1701
1702struct CompilerHasCxx11ImplicitMoves
1703{
1704 static bool value()
1705 {
1706 DetectImplicitMove d(cxx11Move(t: DetectImplicitMove()));
1707 return d.constructor == DetectConstructor::MoveConstructor;
1708 }
1709
1710 struct DetectConstructor
1711 {
1712 Q_DECL_CONSTEXPR DetectConstructor()
1713 : constructor(DefaultConstructor)
1714 {
1715 }
1716
1717 Q_DECL_CONSTEXPR DetectConstructor(const DetectConstructor &)
1718 : constructor(CopyConstructor)
1719 {
1720 }
1721
1722 Q_DECL_CONSTEXPR DetectConstructor(DetectConstructor &&)
1723 : constructor(MoveConstructor)
1724 {
1725 }
1726
1727 enum Constructor {
1728 DefaultConstructor,
1729 CopyConstructor,
1730 MoveConstructor
1731 };
1732
1733 Constructor constructor;
1734 };
1735
1736 struct DetectImplicitMove
1737 : DetectConstructor
1738 {
1739 };
1740};
1741
1742// RValue references need to be supported in the current configuration
1743void tst_QArrayData::rValueReferences()
1744{
1745 if (!CompilerHasCxx11ImplicitMoves::value())
1746 QSKIP("Implicit move ctor not supported in current configuration");
1747
1748 SimpleVector<int> v1(1, 42);
1749 SimpleVector<int> v2;
1750
1751 const SimpleVector<int>::const_iterator begin = v1.constBegin();
1752
1753 QVERIFY(!v1.isNull());
1754 QVERIFY(v2.isNull());
1755
1756 // move-assign
1757 v2 = cxx11Move(t&: v1);
1758
1759 QVERIFY(v1.isNull());
1760 QVERIFY(!v2.isNull());
1761 QCOMPARE(v2.constBegin(), begin);
1762
1763 SimpleVector<int> v3(cxx11Move(t&: v2));
1764
1765 QVERIFY(v1.isNull());
1766 QVERIFY(v2.isNull());
1767 QVERIFY(!v3.isNull());
1768 QCOMPARE(v3.constBegin(), begin);
1769
1770 QCOMPARE(v3.size(), size_t(1));
1771 QCOMPARE(v3.front(), 42);
1772}
1773
1774void tst_QArrayData::grow()
1775{
1776 SimpleVector<int> vector;
1777
1778 QCOMPARE(vector.size(), size_t(0));
1779 QCOMPARE(vector.capacity(), size_t(0));
1780
1781 size_t previousCapacity = 0;
1782 size_t allocations = 0;
1783 for (size_t i = 1; i < (1 << 20); ++i) {
1784 int source[1] = { int(i) };
1785 vector.append(first: source, last: source + 1);
1786 QCOMPARE(vector.size(), i);
1787 if (vector.capacity() != previousCapacity) {
1788 // Don't re-allocate until necessary
1789 QVERIFY(previousCapacity < i);
1790
1791 previousCapacity = vector.capacity();
1792 ++allocations;
1793
1794 // Going element-wise is slow under valgrind
1795 if (previousCapacity - i > 10) {
1796 i = previousCapacity - 5;
1797 vector.back() = -int(i);
1798 vector.resize(newSize: i);
1799
1800 // It's still not the time to re-allocate
1801 QCOMPARE(vector.capacity(), previousCapacity);
1802 }
1803 }
1804 }
1805 QVERIFY(vector.size() >= size_t(1 << 20));
1806
1807 // QArrayData::Grow prevents excessive allocations on a growing container
1808 QVERIFY(allocations > 20 / 2);
1809 QVERIFY(allocations < 20 * 2);
1810
1811 for (size_t i = 0; i < vector.size(); ++i) {
1812 int value = const_(t: vector).at(i);
1813 if (value < 0) {
1814 i = -value;
1815 continue;
1816 }
1817
1818 QCOMPARE(value, int(i + 1));
1819 }
1820}
1821
1822QTEST_APPLESS_MAIN(tst_QArrayData)
1823#include "tst_qarraydata.moc"
1824

source code of qtbase/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp