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 }