1 /*
2  * DQt - D bindings for the Qt Toolkit
3  *
4  * GNU Lesser General Public License Usage
5  * This file may be used under the terms of the GNU Lesser
6  * General Public License version 3 as published by the Free Software
7  * Foundation and appearing in the file LICENSE.LGPL3 included in the
8  * packaging of this file. Please review the following information to
9  * ensure the GNU Lesser General Public License version 3 requirements
10  * will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
11  */
12 module qt.core.arraydataops;
13 extern(C++):
14 
15 import qt.config;
16 import qt.core.arraydata;
17 import qt.core.arraydatapointer;
18 import qt.core.global;
19 import qt.core.typeinfo;
20 import qt.helpers;
21 
22 
23 extern(C++, "QtPrivate") {
24 
25 /+ QT_WARNING_PUSH
26 #if defined(Q_CC_GNU) && Q_CC_GNU >= 700
27 QT_WARNING_DISABLE_GCC("-Wstringop-overflow")
28 #endif +/
29 
30 template QPodArrayOps(T)
31 {
32 //    public QArrayDataPointer!(T) base0;
33 //    alias base0 this;
34 //    static assert (/+ std:: +/is_nothrow_destructible_v!(T), "Types with throwing destructors are not supported in Qt containers.");
35 
36 protected:
37     alias Data = QTypedArrayData!(T);
38     alias DataPointer = QArrayDataPointer!(T);
39 
40 public:
41     alias parameter_type = QArrayDataPointer!(T).parameter_type;
42 
43     void appendInitialize()(ref QArrayDataPointer!T this_, qsizetype newSize)/+ noexcept+/
44     {
45         (mixin(Q_ASSERT(q{this_.isMutable()})));
46         (mixin(Q_ASSERT(q{!this_.isShared()})));
47         (mixin(Q_ASSERT(q{newSize > this_.size})));
48         (mixin(Q_ASSERT(q{newSize - this_.size <= this_.freeSpaceAtEnd()})));
49 
50         T* where = this_.end();
51         this_.size = newSize;
52         const(T)* e = this_.end();
53         while (where != e)
54             *where++ = T.init;
55     }
56 
57     void copyAppend()(ref QArrayDataPointer!T this_, const(T)* b, const(T)* e)/+ noexcept+/
58     {
59         import core.stdc.string;
60 
61         (mixin(Q_ASSERT(q{this_.isMutable() || b == e})));
62         (mixin(Q_ASSERT(q{!this_.isShared() || b == e})));
63         (mixin(Q_ASSERT(q{b <= e})));
64         (mixin(Q_ASSERT(q{(e - b) <= this_.freeSpaceAtEnd()})));
65 
66         if (b == e)
67             return;
68 
69         memcpy(static_cast!(void*)(this_.end()), static_cast!(const(void)*)(b), (e - b) * T.sizeof);
70         this_.size += (e - b);
71     }
72 
73     void copyAppend()(ref QArrayDataPointer!T this_, qsizetype n, parameter_type t)/+ noexcept+/
74     {
75         (mixin(Q_ASSERT(q{!this_.isShared() || n == 0})));
76         (mixin(Q_ASSERT(q{this_.freeSpaceAtEnd() >= n})));
77         if (!n)
78             return;
79 
80         T* where = this_.end();
81         this_.size += qsizetype(n);
82         while (n--)
83             *where++ = t;
84     }
85 
86     void moveAppend()(ref QArrayDataPointer!T this_, T* b, T* e)/+ noexcept+/
87     {
88         copyAppend(this_, b, e);
89     }
90 
91     void truncate()(ref QArrayDataPointer!T this_, size_t newSize)/+ noexcept+/
92     {
93         (mixin(Q_ASSERT(q{this_.isMutable()})));
94         (mixin(Q_ASSERT(q{!this_.isShared()})));
95         (mixin(Q_ASSERT(q{newSize < size_t(this_.size)})));
96 
97         this_.size = qsizetype(newSize);
98     }
99 
100     void destroyAll()(ref QArrayDataPointer!T this_)/+ noexcept+/ // Call from destructors, ONLY!
101     {
102         (mixin(Q_ASSERT(q{this_.d})));
103         (mixin(Q_ASSERT(q{this_.d.ref__.loadRelaxed() == 0})));
104 
105         // As this is to be called only from destructor, it doesn't need to be
106         // exception safe; size not updated.
107     }
108 
109     T* createHole()(ref QArrayDataPointer!T this_, QArrayData.GrowthPosition pos, qsizetype where, qsizetype n)
110     {
111         import core.stdc.string;
112 
113         (mixin(Q_ASSERT(q{(pos == QArrayData.GrowthPosition.GrowsAtBeginning && n <= this_.freeSpaceAtBegin()) ||
114                      (pos == QArrayData.GrowthPosition.GrowsAtEnd && n <= this_.freeSpaceAtEnd())})));
115 
116         T* insertionPoint = this_.ptr + where;
117         if (pos == QArrayData.GrowthPosition.GrowsAtEnd) {
118             if (where < this_.size)
119                 memmove(static_cast!(void*)(insertionPoint + n), static_cast!(void*)(insertionPoint), (this_.size - where) * T.sizeof);
120         } else {
121             (mixin(Q_ASSERT(q{where == 0})));
122             this_.ptr -= n;
123             insertionPoint -= n;
124         }
125         this_.size += n;
126         return insertionPoint;
127     }
128 
129     void insert()(ref QArrayDataPointer!T this_, qsizetype i, const(T)* data, qsizetype n)
130     {
131         import core.stdc.string;
132 
133         Data.GrowthPosition pos = Data.GrowthPosition.GrowsAtEnd;
134         if (this_.size != 0 && i == 0)
135             pos = Data.GrowthPosition.GrowsAtBeginning;
136 
137         DataPointer oldData;
138         this_.detachAndGrow(pos, n, &data, &oldData);
139         (mixin(Q_ASSERT(q{(pos == Data.GrowthPosition.GrowsAtBeginning && this_.freeSpaceAtBegin() >= n) ||
140                      (pos == Data.GrowthPosition.GrowsAtEnd && this_.freeSpaceAtEnd() >= n)})));
141 
142         T* where = createHole(this_, pos, i, n);
143         memcpy(static_cast!(void*)(where), static_cast!(const(void)*)(data), n * T.sizeof);
144     }
145 
146     void insert()(ref QArrayDataPointer!T this_, qsizetype i, qsizetype n, parameter_type t)
147     {
148         auto copy = /*T*/(t);
149 
150         Data.GrowthPosition pos = Data.GrowthPosition.GrowsAtEnd;
151         if (this_.size != 0 && i == 0)
152             pos = Data.GrowthPosition.GrowsAtBeginning;
153 
154         this_.detachAndGrow(pos, n, null, null);
155         (mixin(Q_ASSERT(q{(pos == Data.GrowthPosition.GrowsAtBeginning && this_.freeSpaceAtBegin() >= n) ||
156                      (pos == Data.GrowthPosition.GrowsAtEnd && this_.freeSpaceAtEnd() >= n)})));
157 
158         T* where = createHole(this_, pos, i, n);
159         while (n--)
160             *where++ = copy;
161     }
162 
163     /+ template<typename... Args> +/
164     void emplace(Args...)(ref QArrayDataPointer!T this_, qsizetype i, auto ref Args /+ && +/ args)
165     {
166         import core.lifetime;
167 
168         bool detach = this_.needsDetach();
169         if (!detach) {
170             if (i == this_.size && this_.freeSpaceAtEnd()) {
171                 core.lifetime.emplace!T(this_.end(), args /+ /+ std:: +/forward!(Args)(args)...+/);
172                 ++this_.size;
173                 return;
174             }
175             if (i == 0 && this_.freeSpaceAtBegin()) {
176                 core.lifetime.emplace!T(this_.begin() - 1, args /+ /+ std:: +/forward!(Args)(args)...+/);
177                 --this_.ptr;
178                 ++this_.size;
179                 return;
180             }
181         }
182         static if(Args.length == 1 && is(const(Args[0]) == const(T)))
183             auto tmp = args[0];
184         else
185             auto tmp = T(args /+ /+ std:: +/forward!(Args)(args)...+/);
186         QArrayData.GrowthPosition pos = QArrayData.GrowthPosition.GrowsAtEnd;
187         if (this_.size != 0 && i == 0)
188             pos = QArrayData.GrowthPosition.GrowsAtBeginning;
189 
190         this_.detachAndGrow(pos, 1, null, null);
191 
192         T* where = createHole(this_, pos, i, 1);
193         core.lifetime.emplace!T(where, /+ std:: +//+move+/(tmp));
194     }
195 
196     void erase()(ref QArrayDataPointer!T this_, T* b, qsizetype n)
197     {
198         import core.stdc.string;
199 
200         T* e = b + n;
201         (mixin(Q_ASSERT(q{this_.isMutable()})));
202         (mixin(Q_ASSERT(q{b < e})));
203         (mixin(Q_ASSERT(q{b >= this_.begin() && b < this_.end()})));
204         (mixin(Q_ASSERT(q{e > this_.begin() && e <= this_.end()})));
205 
206         // Comply with std::vector::erase(): erased elements and all after them
207         // are invalidated. However, erasing from the beginning effectively
208         // means that all iterators are invalidated. We can use this_ freedom to
209         // erase by moving towards the end.
210         if (b == this_.begin() && e != this_.end()) {
211             this_.ptr = e;
212         } else if (e != this_.end()) {
213             memmove(static_cast!(void*)(b), static_cast!(void*)(e),
214                       (static_cast!(T*)(this_.end()) - e) * T.sizeof);
215         }
216         this_.size -= n;
217     }
218 
219     void eraseFirst()(ref QArrayDataPointer!T this_)/+ noexcept+/
220     {
221         (mixin(Q_ASSERT(q{this_.isMutable()})));
222         (mixin(Q_ASSERT(q{this_.size})));
223         ++this_.ptr;
224         --this_.size;
225     }
226 
227     void eraseLast()(ref QArrayDataPointer!T this_)/+ noexcept+/
228     {
229         (mixin(Q_ASSERT(q{this_.isMutable()})));
230         (mixin(Q_ASSERT(q{this_.size})));
231         --this_.size;
232     }
233 
234     void assign()(ref QArrayDataPointer!T this_, T* b, T* e, parameter_type t)/+ noexcept+/
235     {
236         import core.stdc.string;
237 
238         (mixin(Q_ASSERT(q{b <= e})));
239         (mixin(Q_ASSERT(q{b >= this_.begin() && e <= this_.end()})));
240 
241         while (b != e)
242             memcpy(static_cast!(void*)(b++), static_cast!(const(void)*)(&t), T.sizeof);
243     }
244 
245     bool compare()(const ref QArrayDataPointer!T this_, const(T)* begin1, const(T)* begin2, size_t n)
246     {
247         import core.stdc.string;
248 
249         // only use memcmp for fundamental types or pointers.
250         // Other types could have padding in the data structure or custom comparison
251         // operators that would break the comparison using memcmp
252         static if (false /*TODO: QArrayDataPointer!(T).pass_parameter_by_value*/) {
253             return memcmp(begin1, begin2, n * T.sizeof) == 0;
254         } else {
255             const(T)* end1 = begin1 + n;
256             while (begin1 != end1) {
257                 if (*begin1 == *begin2) {
258                     ++begin1;
259                     ++begin2;
260                 } else {
261                     return false;
262                 }
263             }
264             return true;
265         }
266     }
267 
268     void reallocate()(ref QArrayDataPointer!T this_, qsizetype alloc, QArrayData.AllocationOption option)
269     {
270         auto pair = Data.reallocateUnaligned(this_.d, this_.ptr, alloc, option);
271         mixin(Q_CHECK_PTR(q{pair.second}));
272         (mixin(Q_ASSERT(q{pair.first !is null})));
273         this_.d = pair.first;
274         this_.ptr = pair.second;
275     }
276 }
277 /+ QT_WARNING_POP +/
278 
279 template QGenericArrayOps(T)
280 {
281 //    public QArrayDataPointer!(T) base0;
282 //    alias base0 this_;
283     //static assert (/+ std:: +/is_nothrow_destructible_v!(T), "Types with throwing destructors are not supported in Qt containers.");
284 
285 protected:
286     alias Data = QTypedArrayData!(T);
287     alias DataPointer = QArrayDataPointer!(T);
288 
289 public:
290     alias parameter_type = QArrayDataPointer!(T).parameter_type;
291 
292     void appendInitialize()(ref QArrayDataPointer!T this_, qsizetype newSize)
293     {
294         import core.lifetime;
295 
296         (mixin(Q_ASSERT(q{this_.isMutable()})));
297         (mixin(Q_ASSERT(q{!this_.isShared()})));
298         (mixin(Q_ASSERT(q{newSize > this_.size})));
299         (mixin(Q_ASSERT(q{newSize - this_.size <= this_.freeSpaceAtEnd()})));
300 
301         T*/+ const +/  b = this_.begin();
302         do {
303             core.lifetime.emplace!T(b + this_.size);
304         } while (++this_.size != newSize);
305     }
306 
307     void copyAppend()(ref QArrayDataPointer!T this_, const(T)* b, const(T)* e)
308     {
309         import core.lifetime;
310 
311         (mixin(Q_ASSERT(q{this_.isMutable() || b == e})));
312         (mixin(Q_ASSERT(q{!this_.isShared() || b == e})));
313         (mixin(Q_ASSERT(q{b <= e})));
314         (mixin(Q_ASSERT(q{(e - b) <= this_.freeSpaceAtEnd()})));
315 
316         if (b == e) // short-cut and handling the case b and e == nullptr
317             return;
318 
319         T* data = this_.begin();
320         while (b < e) {
321             static if(is(T == struct) && __traits(hasCopyConstructor, T)) {
322                 // Workaround for https://issues.dlang.org/show_bug.cgi?id=22766
323                 import core.stdc.string;
324                 memset((data + this_.size), 0, T.sizeof);
325                 (*cast(T*)(data + this_.size)).__ctor(*cast(T*)b);
326             } else {
327                 core.lifetime.copyEmplace!T(*cast(T*)b, *cast(T*)(data + this_.size));
328             }
329             ++b;
330             ++this_.size;
331         }
332     }
333 
334     void copyAppend()(ref QArrayDataPointer!T this_, qsizetype n, parameter_type t)
335     {
336         import core.lifetime;
337 
338         (mixin(Q_ASSERT(q{!this_.isShared() || n == 0})));
339         (mixin(Q_ASSERT(q{this_.freeSpaceAtEnd() >= n})));
340         if (!n)
341             return;
342 
343         T* data = this_.begin();
344         while (n--) {
345             core.lifetime.copyEmplace!T(*cast(T*)&t, *(data + this_.size));
346             ++this_.size;
347         }
348     }
349 
350     void moveAppend()(ref QArrayDataPointer!T this_, T* b, T* e)
351     {
352         import core.lifetime;
353 
354         (mixin(Q_ASSERT(q{this_.isMutable() || b == e})));
355         (mixin(Q_ASSERT(q{!this_.isShared() || b == e})));
356         (mixin(Q_ASSERT(q{b <= e})));
357         (mixin(Q_ASSERT(q{(e - b) <= this_.freeSpaceAtEnd()})));
358 
359         if (b == e)
360             return;
361 
362         T* data = this_.begin();
363         while (b < e) {
364             core.lifetime.emplace!T(data + this_.size, /+ std:: +/move(*b));
365             ++b;
366             ++this_.size;
367         }
368     }
369 
370     void truncate()(ref QArrayDataPointer!T this_, size_t newSize)
371     {
372         (mixin(Q_ASSERT(q{this_.isMutable()})));
373         (mixin(Q_ASSERT(q{!this_.isShared()})));
374         (mixin(Q_ASSERT(q{newSize < size_t(this_.size)})));
375 
376         for (auto it = this_.begin() + newSize; it != this_.end(); it++)
377             destroy(*it);
378         this_.size = newSize;
379     }
380 
381     void destroyAll()(ref QArrayDataPointer!T this_) // Call from destructors, ONLY
382     {
383         (mixin(Q_ASSERT(q{this_.d})));
384         // As this_ is to be called only from destructor, it doesn't need to be
385         // exception safe; size not updated.
386 
387         (mixin(Q_ASSERT(q{this_.d.ref__.loadRelaxed() == 0})));
388 
389         for (auto it = this_.begin(); it != this_.end(); it++)
390             destroy(*it);
391     }
392 
393     struct Inserter
394     {
395         QArrayDataPointer!(T)* data;
396         T* begin;
397         qsizetype size;
398 
399         qsizetype sourceCopyConstruct = 0; qsizetype nSource = 0; qsizetype move = 0; qsizetype sourceCopyAssign = 0;
400         T* end = null; T* last = null; T* where = null;
401 
402         this(QArrayDataPointer!(T)* d)
403         {
404             this.data = d;
405 
406             begin = d.ptr;
407             size = d.size;
408         }
409         ~this() {
410             data.ptr = begin;
411             data.size = size;
412         }
413         /+ Q_DISABLE_COPY(Inserter) +/
414 @disable this(this);
415 /+this(ref const(Inserter));+//+ref Inserter operator =(ref const(Inserter));+/
416         void setup()(qsizetype pos, qsizetype n)
417         {
418             end = begin + size;
419             last = end - 1;
420             where = begin + pos;
421             qsizetype dist = size - pos;
422             sourceCopyConstruct = 0;
423             nSource = n;
424             move = n - dist; // smaller 0
425             sourceCopyAssign = n;
426             if (n > dist) {
427                 sourceCopyConstruct = n - dist;
428                 move = 0;
429                 sourceCopyAssign -= sourceCopyConstruct;
430             }
431         }
432 
433         void insert()(qsizetype pos, const(T)* source, qsizetype n)
434         {
435             import core.lifetime;
436 
437             qsizetype oldSize = size;
438             /+ Q_UNUSED(oldSize) +/
439 
440             setup(pos, n);
441 
442             // first create new elements at the end, by copying from elements
443             // to be inserted (if they extend past the current end of the array)
444             for (qsizetype i = 0; i != sourceCopyConstruct; ++i) {
445                 core.lifetime.copyEmplace!T(*cast(T*)&source[nSource - sourceCopyConstruct + i], *(end + i));
446                 ++size;
447             }
448             (mixin(Q_ASSERT(q{size <= oldSize + n})));
449 
450             // now move construct new elements at the end from existing elements inside
451             // the array.
452             for (qsizetype i = sourceCopyConstruct; i != nSource; ++i) {
453                 core.lifetime.emplace!T(end + i, /+ std:: +//*move*/(*(end + i - nSource)));
454                 ++size;
455             }
456             // array has the new size now!
457             (mixin(Q_ASSERT(q{size == oldSize + n})));
458 
459             // now move assign existing elements towards the end
460             for (qsizetype i = 0; i != move; --i)
461                 last[i] = /+ std:: +//*move*/(last[i - nSource]);
462 
463             // finally copy the remaining elements from source over
464             for (qsizetype i = 0; i != sourceCopyAssign; ++i)
465                 where[i] = *cast(T*)&source[i];
466         }
467 
468         void insert()(qsizetype pos, ref const(T) t, qsizetype n)
469         {
470             import core.lifetime;
471 
472             const(qsizetype) oldSize = size;
473             /+ Q_UNUSED(oldSize) +/
474 
475             setup(pos, n);
476 
477             // first create new elements at the end, by copying from elements
478             // to be inserted (if they extend past the current end of the array)
479             for (qsizetype i = 0; i != sourceCopyConstruct; ++i) {
480                 core.lifetime.copyEmplace!T(*cast(T*)&t, *(end + i));
481                 ++size;
482             }
483             (mixin(Q_ASSERT(q{size <= oldSize + n})));
484 
485             // now move construct new elements at the end from existing elements inside
486             // the array.
487             for (qsizetype i = sourceCopyConstruct; i != nSource; ++i) {
488                 core.lifetime.emplace!T(end + i, /+ std:: +//*move*/(*(end + i - nSource)));
489                 ++size;
490             }
491             // array has the new size now!
492             (mixin(Q_ASSERT(q{size == oldSize + n})));
493 
494             // now move assign existing elements towards the end
495             for (qsizetype i = 0; i != move; --i)
496                 last[i] = /+ std:: +//*move*/(last[i - nSource]);
497 
498             // finally copy the remaining elements from source over
499             for (qsizetype i = 0; i != sourceCopyAssign; ++i)
500                 where[i] = *cast(T*)&t;
501         }
502 
503         void insertOne()(qsizetype pos, auto ref T /+ && +/ t)
504         {
505             import core.lifetime;
506 
507             setup(pos, 1);
508 
509             if (sourceCopyConstruct) {
510                 (mixin(Q_ASSERT(q{QGenericArrayOps.Inserter.sourceCopyConstruct == 1})));
511                 core.lifetime.emplace!T(end, /+ std:: +/move(t));
512                 ++size;
513             } else {
514                 // create a new element at the end by move constructing one existing element
515                 // inside the array.
516                 core.lifetime.emplace!T(end, /+ std:: +/move(*(end - 1)));
517                 ++size;
518 
519                 // now move assign existing elements towards the end
520                 for (qsizetype i = 0; i != move; --i)
521                     last[i] = /+ std:: +/move(last[i - 1]);
522 
523                 // and move the new item into place
524                 *where = /+ std:: +/move(t);
525             }
526         }
527     }
528 
529     void insert()(ref QArrayDataPointer!T this_, qsizetype i, const(T)* data, qsizetype n)
530     {
531         import core.lifetime;
532 
533         const(bool) growsAtBegin = this_.size != 0 && i == 0;
534         const pos = growsAtBegin ? Data.GrowthPosition.GrowsAtBeginning : Data.GrowthPosition.GrowsAtEnd;
535 
536         DataPointer oldData;
537         this_.detachAndGrow(pos, n, &data, &oldData);
538         (mixin(Q_ASSERT(q{(pos == Data.GrowthPosition.GrowsAtBeginning && this_.freeSpaceAtBegin() >= n) ||
539                      (pos == Data.GrowthPosition.GrowsAtEnd && this_.freeSpaceAtEnd() >= n)})));
540 
541         if (growsAtBegin) {
542             // copy construct items in reverse order at the begin
543             (mixin(Q_ASSERT(q{this_.freeSpaceAtBegin() >= n})));
544             while (n) {
545                 --n;
546                 core.lifetime.copyEmplace!T(*cast(T*)&data[n], *(this_.begin() - 1));
547                 --this_.ptr;
548                 ++this_.size;
549             }
550         } else {
551             Inserter(&this_).insert(i, data, n);
552         }
553     }
554 
555     void insert()(ref QArrayDataPointer!T this_, qsizetype i, qsizetype n, parameter_type t)
556     {
557         import core.lifetime;
558 
559         auto copy = /*T*/(t);
560 
561         const(bool) growsAtBegin = this_.size != 0 && i == 0;
562         const pos = growsAtBegin ? Data.GrowthPosition.GrowsAtBeginning : Data.GrowthPosition.GrowsAtEnd;
563 
564         this_.detachAndGrow(pos, n, null, null);
565         (mixin(Q_ASSERT(q{(pos == Data.GrowthPosition.GrowsAtBeginning && this_.freeSpaceAtBegin() >= n) ||
566                      (pos == Data.GrowthPosition.GrowsAtEnd && this_.freeSpaceAtEnd() >= n)})));
567 
568         if (growsAtBegin) {
569             // copy construct items in reverse order at the begin
570             (mixin(Q_ASSERT(q{this_.freeSpaceAtBegin() >= n})));
571             while (n--) {
572                 core.lifetime.emplace!T(this_.begin() - 1, copy);
573                 --this_.ptr;
574                 ++this_.size;
575             }
576         } else {
577             Inserter(&this_).insert(i, copy, n);
578         }
579     }
580 
581     /+ template<typename... Args> +/
582     void emplace(Args)(ref QArrayDataPointer!T this_, qsizetype i, auto ref Args /+ && +/ args)
583     {
584         import core.lifetime;
585 
586         bool detach = this_.needsDetach();
587         if (!detach) {
588             if (i == this_.size && this_.freeSpaceAtEnd()) {
589                 core.lifetime.emplace!T(this_.end(), args /+ /+ std:: +/forward!(Args)(args)...+/);
590                 ++this_.size;
591                 return;
592             }
593             if (i == 0 && this_.freeSpaceAtBegin()) {
594                 core.lifetime.emplace!T(this_.begin() - 1, args /+ /+ std:: +/forward!(Args)(args)...+/);
595                 --this_.ptr;
596                 ++this_.size;
597                 return;
598             }
599         }
600         auto tmp = T(args /+ /+ std:: +/forward!(Args)(args)...+/);
601         const(bool) growsAtBegin = this_.size != 0 && i == 0;
602         const pos = growsAtBegin ? Data.GrowthPosition.GrowsAtBeginning : Data.GrowthPosition.GrowsAtEnd;
603 
604         this_.detachAndGrow(pos, 1, null, null);
605 
606         if (growsAtBegin) {
607             (mixin(Q_ASSERT(q{this_.freeSpaceAtBegin()})));
608             core.lifetime.emplace!T(this_.begin() - 1, /+ std:: +//+move+/(tmp));
609             --this_.ptr;
610             ++this_.size;
611         } else {
612             Inserter(&this_).insertOne(i, /+ std:: +//+move+/(tmp));
613         }
614     }
615 
616     void erase()(ref QArrayDataPointer!T this_, T* b, qsizetype n)
617     {
618         T* e = b + n;
619         (mixin(Q_ASSERT(q{this_.isMutable()})));
620         (mixin(Q_ASSERT(q{b < e})));
621         (mixin(Q_ASSERT(q{b >= this_.begin() && b < this_.end()})));
622         (mixin(Q_ASSERT(q{e > this_.begin() && e <= this_.end()})));
623 
624         // Comply with std::vector::erase(): erased elements and all after them
625         // are invalidated. However, erasing from the beginning effectively
626         // means that all iterators are invalidated. We can use this_ freedom to
627         // erase by moving towards the end.
628         if (b == this_.begin() && e != this_.end()) {
629             this_.ptr = e;
630         } else {
631             const(T)*/+ const +/  end = this_.end();
632 
633             // move (by assignment) the elements from e to end
634             // onto b to the new end
635             while (e != end) {
636                 *b = /+ std:: +//*move*/(*e);
637                 ++b;
638                 ++e;
639             }
640         }
641         this_.size -= n;
642         for (auto it = b; it != e; it++)
643             destroy(*it);
644     }
645 
646     void eraseFirst()(ref QArrayDataPointer!T this_)/+ noexcept+/
647     {
648         (mixin(Q_ASSERT(q{this_.isMutable()})));
649         (mixin(Q_ASSERT(q{this_.size})));
650         destroy!false(*this_.begin());
651         ++this_.ptr;
652         --this_.size;
653     }
654 
655     void eraseLast()(ref QArrayDataPointer!T this_)/+ noexcept+/
656     {
657         (mixin(Q_ASSERT(q{this_.isMutable()})));
658         (mixin(Q_ASSERT(q{this_.size})));
659         destroy!false(*this_.end() - 1);
660         --this_.size;
661     }
662 
663 
664     void assign()(ref QArrayDataPointer!T this_, T* b, T* e, parameter_type t)
665     {
666         (mixin(Q_ASSERT(q{b <= e})));
667         (mixin(Q_ASSERT(q{b >= this_.begin() && e <= this_.end()})));
668 
669         while (b != e)
670             *b++ = t;
671     }
672 
673     bool compare()(const ref QArrayDataPointer!T this_, const(T)* begin1, const(T)* begin2, size_t n)
674     {
675         const(T)* end1 = begin1 + n;
676         while (begin1 != end1) {
677             if (*begin1 == *begin2) {
678                 ++begin1;
679                 ++begin2;
680             } else {
681                 return false;
682             }
683         }
684         return true;
685     }
686 }
687 
688 template QMovableArrayOps(T)
689 {
690     //QGenericArrayOps!(T) base0;
691     //alias base0 this_;
692     //static assert (/+ std:: +/is_nothrow_destructible_v!(T), "Types with throwing destructors are not supported in Qt containers.");
693 
694 protected:
695     alias Data = QTypedArrayData!(T);
696     alias DataPointer = QArrayDataPointer!(T);
697 
698 public:
699     alias copyAppend = QGenericArrayOps!T.copyAppend;
700     alias moveAppend = QGenericArrayOps!T.moveAppend;
701     alias truncate = QGenericArrayOps!T.truncate;
702     alias destroyAll = QGenericArrayOps!T.destroyAll;
703     alias assign = QGenericArrayOps!T.assign;
704     alias compare = QGenericArrayOps!T.compare;
705     alias parameter_type = QGenericArrayOps!T.parameter_type;
706     alias appendInitialize = QGenericArrayOps!T.appendInitialize;
707 
708     struct Inserter
709     {
710         QArrayDataPointer!(T)* data;
711         T* displaceFrom;
712         T* displaceTo;
713         qsizetype nInserts = 0;
714         qsizetype bytes;
715 
716         this(QArrayDataPointer!(T)* d)
717         {
718             this.data = d;
719         }
720         ~this() {
721             import core.stdc.string;
722 
723             static if (true /* !/+ std:: +/is_nothrow_copy_constructible_v!(T)*/) {
724                 if (displaceFrom != displaceTo) {
725                     memmove(static_cast!(void*)(displaceFrom), static_cast!(void*)(displaceTo), bytes);
726                     nInserts -= qAbs(displaceFrom - displaceTo);
727                 }
728             }
729             data.size += nInserts;
730         }
731         /+ Q_DISABLE_COPY(Inserter) +/
732 @disable this(this);
733 /+this(ref const(Inserter));+//+ref Inserter operator =(ref const(Inserter));+/
734         T* displace()(qsizetype pos, qsizetype n)
735         {
736             import core.stdc.string;
737             version(D_LP64)
738                 import core.stdc.config;
739 
740             nInserts = n;
741             T* insertionPoint = data.ptr + pos;
742             displaceFrom = data.ptr + pos;
743             displaceTo = displaceFrom + n;
744             bytes = data.size - pos;
745             bytes *= T.sizeof;
746             memmove(static_cast!(void*)(displaceTo), static_cast!(void*)(displaceFrom), bytes);
747             return insertionPoint;
748         }
749 
750         void insert()(qsizetype pos, const(T)* source, qsizetype n)
751         {
752             import core.lifetime;
753 
754             T* where = displace(pos, n);
755 
756             while (n--) {
757                 core.lifetime.copyEmplace!T(*cast(T*)source, *where);
758                 ++where;
759                 ++source;
760                 ++displaceFrom;
761             }
762         }
763 
764         void insert()(qsizetype pos, ref const(T) t, qsizetype n)
765         {
766             import core.lifetime;
767 
768             T* where = displace(pos, n);
769 
770             while (n--) {
771                 core.lifetime.copyEmplace!T(*cast(T*)&t, *where);
772                 ++where;
773                 ++displaceFrom;
774             }
775         }
776 
777         void insertOne()(qsizetype pos, auto ref T /+ && +/ t)
778         {
779             import core.lifetime;
780 
781             T* where = displace(pos, 1);
782             core.lifetime.emplace!T(where, /+ std:: +/move(t));
783             ++displaceFrom;
784             (mixin(Q_ASSERT(q{displaceFrom == displaceTo})));
785         }
786 
787     }
788 
789 
790     void insert()(ref QArrayDataPointer!T this_, qsizetype i, const(T)* data, qsizetype n)
791     {
792         import core.lifetime;
793 
794         const(bool) growsAtBegin = this_.size != 0 && i == 0;
795         const pos = growsAtBegin ? Data.GrowthPosition.GrowsAtBeginning : Data.GrowthPosition.GrowsAtEnd;
796 
797         DataPointer oldData;
798         this_.detachAndGrow(pos, n, &data, &oldData);
799         (mixin(Q_ASSERT(q{(pos == Data.GrowthPosition.GrowsAtBeginning && this_.freeSpaceAtBegin() >= n) ||
800                      (pos == Data.GrowthPosition.GrowsAtEnd && this_.freeSpaceAtEnd() >= n)})));
801 
802         if (growsAtBegin) {
803             // copy construct items in reverse order at the begin
804             (mixin(Q_ASSERT(q{this_.freeSpaceAtBegin() >= n})));
805             while (n) {
806                 --n;
807                 core.lifetime.copyEmplace!T(*cast(T*)&data[n], *(this_.begin() - 1));
808                 --this_.ptr;
809                 ++this_.size;
810             }
811         } else {
812             Inserter(&this_).insert(i, data, n);
813         }
814     }
815 
816     void insert()(ref QArrayDataPointer!T this_, qsizetype i, qsizetype n, parameter_type t)
817     {
818         import core.lifetime;
819 
820         auto copy = /*T*/(t);
821 
822         const(bool) growsAtBegin = this_.size != 0 && i == 0;
823         const pos = growsAtBegin ? Data.GrowthPosition.GrowsAtBeginning : Data.GrowthPosition.GrowsAtEnd;
824 
825         this_.detachAndGrow(pos, n, null, null);
826         (mixin(Q_ASSERT(q{(pos == Data.GrowthPosition.GrowsAtBeginning && this_.freeSpaceAtBegin() >= n) ||
827                      (pos == Data.GrowthPosition.GrowsAtEnd && this_.freeSpaceAtEnd() >= n)})));
828 
829         if (growsAtBegin) {
830             // copy construct items in reverse order at the begin
831             (mixin(Q_ASSERT(q{this_.freeSpaceAtBegin() >= n})));
832             while (n--) {
833                 core.lifetime.emplace!T(this_.begin() - 1, copy);
834                 --this_.ptr;
835                 ++this_.size;
836             }
837         } else {
838             Inserter(&this_).insert(i, copy, n);
839         }
840     }
841 
842     /+ template<typename... Args> +/
843     void emplace(Args...)(ref QArrayDataPointer!T this_, qsizetype i, auto ref Args /+ && +/ args)
844     {
845         import core.lifetime;
846 
847         bool detach = this_.needsDetach();
848         if (!detach) {
849             if (i == this_.size && this_.freeSpaceAtEnd()) {
850                 core.lifetime.emplace!T(this_.end(), args /+ /+ std:: +/forward!(Args)(args)...+/);
851                 ++this_.size;
852                 return;
853             }
854             if (i == 0 && this_.freeSpaceAtBegin()) {
855                 core.lifetime.emplace!T(this_.begin() - 1, args /+ /+ std:: +/forward!(Args)(args)...+/);
856                 --this_.ptr;
857                 ++this_.size;
858                 return;
859             }
860         }
861         static if(Args.length == 1 && is(const(Args[0]) == const(T)))
862             auto tmp = args[0];
863         else
864             auto tmp = T(args /+ /+ std:: +/forward!(Args)(args)...+/);
865         const(bool) growsAtBegin = this_.size != 0 && i == 0;
866         const pos = growsAtBegin ? Data.GrowthPosition.GrowsAtBeginning : Data.GrowthPosition.GrowsAtEnd;
867 
868         this_.detachAndGrow(pos, 1, null, null);
869         if (growsAtBegin) {
870             (mixin(Q_ASSERT(q{this_.freeSpaceAtBegin()})));
871             core.lifetime.emplace!T(this_.begin() - 1, /+ std:: +//+move+/(tmp));
872             --this_.ptr;
873             ++this_.size;
874         } else {
875             Inserter(&this_).insertOne(i, /+ std:: +//+move+/(tmp));
876         }
877     }
878 
879     void erase()(ref QArrayDataPointer!T this_, T* b, qsizetype n)
880     {
881         import core.stdc.string;
882 
883         T* e = b + n;
884 
885         (mixin(Q_ASSERT(q{this_.isMutable()})));
886         (mixin(Q_ASSERT(q{b < e})));
887         (mixin(Q_ASSERT(q{b >= this_.begin() && b < this_.end()})));
888         (mixin(Q_ASSERT(q{e > this_.begin() && e <= this_.end()})));
889 
890         // Comply with std::vector::erase(): erased elements and all after them
891         // are invalidated. However, erasing from the beginning effectively
892         // means that all iterators are invalidated. We can use this_ freedom to
893         // erase by moving towards the end.
894 
895         for (auto it = b; it != e; it++)
896             destroy(*it);
897         if (b == this_.begin() && e != this_.end()) {
898             this_.ptr = e;
899         } else if (e != this_.end()) {
900             memmove(static_cast!(void*)(b), static_cast!(const(void)*)(e), (static_cast!(const(T)*)(this_.end()) - e)*T.sizeof);
901         }
902         this_.size -= n;
903     }
904 
905     void reallocate()(ref QArrayDataPointer!T this_, qsizetype alloc, QArrayData.AllocationOption option)
906     {
907         auto pair = Data.reallocateUnaligned(this_.d, this_.ptr, alloc, option);
908         mixin(Q_CHECK_PTR(q{pair.second}));
909         (mixin(Q_ASSERT(q{pair.first !is null})));
910         this_.d = pair.first;
911         this_.ptr = pair.second;
912     }
913 }
914 
915 template QArrayOpsSelector(T)
916 {
917     static if(!QTypeInfo!T.isComplex && QTypeInfo!T.isRelocatable)
918         alias Type = QPodArrayOps!(T);
919     else static if(QTypeInfo!T.isComplex && QTypeInfo!T.isRelocatable)
920         alias Type = QMovableArrayOps!(T);
921     else
922         alias Type = QGenericArrayOps!(T);
923 }
924 
925 /+ template <class T>
926 struct QArrayOpsSelector<T,
927     typename std::enable_if<
928         !QTypeInfo<T>::isComplex && QTypeInfo<T>::isRelocatable
929     >::type>
930 {
931     typedef QPodArrayOps<T> Type;
932 };
933 
934 template <class T>
935 struct QArrayOpsSelector<T,
936     typename std::enable_if<
937         QTypeInfo<T>::isComplex && QTypeInfo<T>::isRelocatable
938     >::type>
939 {
940     typedef QMovableArrayOps<T> Type;
941 }; +/
942 
943 template QCommonArrayOps(T)
944 {
945     //QArrayOpsSelector!(T).Type base0;
946     //alias base0 this_;
947     alias Base = QArrayOpsSelector!(T).Type;
948     alias Data = QTypedArrayData!(T);
949     alias DataPointer = QArrayDataPointer!(T);
950     alias parameter_type = Base.parameter_type;
951 
952 protected:
953     alias Self = QCommonArrayOps!(T);
954 
955 public:
956     alias truncate = Base.truncate;
957     alias destroyAll = Base.destroyAll;
958     alias assign = Base.assign;
959     alias compare = Base.compare;
960     static if(__traits(hasMember, Base, "reallocate"))
961         alias reallocate = Base.reallocate;
962     alias copyAppend = Base.copyAppend;
963     alias moveAppend = Base.moveAppend;
964     alias appendInitialize = Base.appendInitialize;
965     alias emplace = Base.emplace;
966 
967     /+ template<typename It> +/
968     void appendIteratorRange(It)(It b, It e, /+ QtPrivate:: +/IfIsForwardIterator!(It) /+ = true +/)
969     {
970         import core.lifetime;
971 
972         (mixin(Q_ASSERT(q{this.isMutable() || b == e})));
973         (mixin(Q_ASSERT(q{!this.isShared() || b == e})));
974         const(qsizetype) distance = /+ std:: +/distance(b, e);
975         (mixin(Q_ASSERT(q{distance >= 0 && distance <= this.allocatedCapacity() - this.size})));
976         /+ Q_UNUSED(distance) +/
977 
978         T* iter = this.end();
979         for (; b != e; ++iter, ++b) {
980             core.lifetime.emplace!T(iter, *b);
981             ++this.size;
982         }
983     }
984 
985     // slightly higher level API than copyAppend() that also preallocates space
986     void growAppend()(ref QArrayDataPointer!T this_, const(T)* b, const(T)* e)
987     {
988         if (b == e)
989             return;
990         (mixin(Q_ASSERT(q{b < e})));
991         const(qsizetype) n = e - b;
992         DataPointer old;
993 
994         // points into range:
995         if (b >= this_.begin() && b < this_.end()) {
996             this_.detachAndGrow(QArrayData.GrowthPosition.GrowsAtEnd, n, &b, &old);
997         } else {
998             this_.detachAndGrow(QArrayData.GrowthPosition.GrowsAtEnd, n, null, null);
999         }
1000         (mixin(Q_ASSERT(q{this_.freeSpaceAtEnd() >= n})));
1001         // b might be updated so use [b, n)
1002         this_.copyAppend(b, b + n);
1003     }
1004 }
1005 
1006 } // namespace QtPrivate
1007 
1008 template QArrayDataOps(T)
1009 {
1010     // /+ QtPrivate:: +/QCommonArrayOps!(T) base0;
1011     //alias base0 this_;
1012     alias QArrayDataOps = QCommonArrayOps!T;
1013 }