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.arraydatapointer; 13 extern(C++): 14 15 import qt.config; 16 import qt.core.arraydata; 17 import qt.core.arraydataops; 18 import qt.core.global; 19 import qt.core.pair; 20 import qt.core.typeinfo; 21 import qt.helpers; 22 23 struct QArrayDataPointer(T) 24 { 25 private: 26 alias Data = QTypedArrayData!(T); 27 alias DataOps = QArrayDataOps!(T); 28 29 public: 30 /+ enum { 31 pass_parameter_by_value = 32 /+ std:: +/is_arithmetic!(T).value || /+ std:: +/is_pointer!(T).value || /+ std:: +/is_enum!(T).value 33 } 34 35 }+/ 36 37 // alias parameter_type = /+ std:: +/conditional!(pass_parameter_by_value, T, ref const(T)).type; 38 alias parameter_type = T; // TODO: ref 39 40 /+this()/+ noexcept+/ 41 { 42 this.d = null; 43 this.ptr = null; 44 this.size = 0; 45 }+/ 46 47 /*this(ref const(QArrayDataPointer) other)/+ noexcept+/ 48 { 49 this.d = other.d; 50 this.ptr = other.ptr; 51 this.size = other.size; 52 53 ref_(); 54 }*/ 55 this(this) 56 { 57 ref_(); 58 } 59 60 this(Data* header, T* adata, qsizetype n = 0)/+ noexcept+/ 61 { 62 this.d = header; 63 this.ptr = adata; 64 this.size = n; 65 } 66 67 /+ explicit +/this(qt.core.pair.QPair!(QTypedArrayData!(T)*, T*) adata, qsizetype n = 0)/+ noexcept+/ 68 { 69 this.d = adata.first; 70 this.ptr = adata.second; 71 this.size = n; 72 } 73 74 static QArrayDataPointer fromRawData(const(T)* rawData, qsizetype length)/+ noexcept+/ 75 { 76 (mixin(Q_ASSERT(q{rawData || !length}))); 77 return QArrayDataPointer( null, const_cast!(T*)(rawData), length) ; 78 } 79 80 /+ref QArrayDataPointer operator =(ref const(QArrayDataPointer) other)/+ noexcept+/ 81 { 82 auto tmp = QArrayDataPointer(other); 83 this.swap(tmp); 84 return this; 85 }+/ 86 87 /+ QArrayDataPointer(QArrayDataPointer &&other) noexcept 88 : d(other.d), ptr(other.ptr), size(other.size) 89 { 90 other.d = nullptr; 91 other.ptr = nullptr; 92 other.size = 0; 93 } +/ 94 95 /+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QArrayDataPointer) +/ 96 97 /*ref DataOps opUnary(string op)()/+ noexcept+/ if(op == "*") 98 { 99 return *static_cast!(DataOps*)(&this); 100 }*/ 101 102 /+DataOps* operator ->()/+ noexcept+/ 103 { 104 return static_cast!(DataOps*)(&this); 105 }+/ 106 107 /*ref const(DataOps) opUnary(string op)() const/+ noexcept+/ if(op == "*") 108 { 109 return *static_cast!(const(DataOps)*)(&this); 110 }*/ 111 112 /+const(DataOps)* operator ->() const/+ noexcept+/ 113 { 114 return static_cast!(const(DataOps)*)(&this); 115 }+/ 116 117 ~this() 118 { 119 if (!deref()) { 120 DataOps.destroyAll(this); 121 Data.deallocate(cast(QArrayData*)d); 122 } 123 } 124 125 bool isNull() const/+ noexcept+/ 126 { 127 return !ptr; 128 } 129 130 T* data()/+ noexcept+/ { return ptr; } 131 const(T)* data() const/+ noexcept+/ { return ptr; } 132 133 T* begin()/+ noexcept+/ { return data(); } 134 T* end()/+ noexcept+/ { return data() + size; } 135 const(T)* begin() const/+ noexcept+/ { return data(); } 136 const(T)* end() const/+ noexcept+/ { return data() + size; } 137 const(T)* constBegin() const/+ noexcept+/ { return data(); } 138 const(T)* constEnd() const/+ noexcept+/ { return data() + size; } 139 140 void swap(ref QArrayDataPointer other) /+ noexcept +/ 141 { 142 import std.algorithm; 143 std.algorithm.swap(d, other.d); 144 std.algorithm.swap(ptr, other.ptr); 145 std.algorithm.swap(size, other.size); 146 } 147 148 void clear()/+ noexcept(is_nothrow_destructible<T>.value)+/ 149 { 150 QArrayDataPointer tmp; 151 swap(tmp); 152 } 153 154 void detach()(QArrayDataPointer* old = null) 155 { 156 if (needsDetach()) 157 reallocateAndGrow(QArrayData.GrowthPosition.GrowsAtEnd, 0, old); 158 } 159 160 /*! \internal 161 162 Detaches this (optionally) and grows to accommodate the free space for 163 \a n elements at the required side. The side is determined from \a pos. 164 165 \a data pointer can be provided when the caller knows that \a data 166 points into range [this->begin(), this->end()). In case it is, *data 167 would be updated so that it continues to point to the element it was 168 pointing to before the data move. if \a data does not point into range, 169 one can/should pass \c nullptr. 170 171 Similarly to \a data, \a old, pointer to a default-constructed QADP, can 172 be provided when the caller expects to e.g. copy the data from this to 173 itself: 174 \code 175 QList<T> list(5); 176 qsizetype pos = getArbitraryPos(); 177 list.insert(pos, list.begin(), list.end()); 178 \endcode 179 180 The default rule would be: \a data and \a old must either both be valid 181 pointers, or both equal to \c nullptr. 182 */ 183 void detachAndGrow()(QArrayData.GrowthPosition where, qsizetype n, const(T)** data, 184 QArrayDataPointer* old) 185 { 186 const(bool) detach = needsDetach(); 187 bool readjusted = false; 188 if (!detach) { 189 if (!n || (where == QArrayData.GrowthPosition.GrowsAtBeginning && freeSpaceAtBegin() >= n) 190 || (where == QArrayData.GrowthPosition.GrowsAtEnd && freeSpaceAtEnd() >= n)) 191 return; 192 readjusted = tryReadjustFreeSpace(where, n, data); 193 (mixin(Q_ASSERT(q{!readjusted 194 || (where == QArrayData.GrowthPosition.GrowsAtBeginning && freeSpaceAtBegin() >= n) 195 || (where == QArrayData.GrowthPosition.GrowsAtEnd && freeSpaceAtEnd() >= n)}))); 196 } 197 198 if (!readjusted) 199 reallocateAndGrow(where, n, old); 200 } 201 202 /*! \internal 203 204 Reallocates to accommodate the free space for \a n elements at the 205 required side. The side is determined from \a pos. Might also shrink 206 when n < 0. 207 */ 208 /+ Q_NEVER_INLINE +/ void reallocateAndGrow()(QArrayData.GrowthPosition where, qsizetype n, 209 QArrayDataPointer* old = null) 210 { 211 static if (QTypeInfo!(T).isRelocatable && T.alignof <= 8/* /+ std:: +/max_align_t.alignof*/) { 212 if (where == QArrayData.GrowthPosition.GrowsAtEnd && !old && !needsDetach() && n > 0) { 213 DataOps.reallocate(this, constAllocatedCapacity() - freeSpaceAtEnd() + n, QArrayData.AllocationOption.Grow); // fast path 214 return; 215 } 216 } 217 218 auto dp = /*QArrayDataPointer*/(allocateGrow(this, n, where)); 219 if (n > 0) 220 mixin(Q_CHECK_PTR(q{dp.data()})); 221 if (where == QArrayData.GrowthPosition.GrowsAtBeginning) { 222 (mixin(Q_ASSERT(q{dp.freeSpaceAtBegin() >= n}))); 223 } else { 224 (mixin(Q_ASSERT(q{dp.freeSpaceAtEnd() >= n}))); 225 } 226 if (size) { 227 qsizetype toCopy = size; 228 if (n < 0) 229 toCopy += n; 230 if (needsDetach() || old) 231 DataOps.copyAppend(dp, begin(), begin() + toCopy); 232 else 233 DataOps.moveAppend(dp, begin(), begin() + toCopy); 234 (mixin(Q_ASSERT(q{dp.size == toCopy}))); 235 } 236 237 swap(dp); 238 if (old) 239 old.swap(dp); 240 } 241 242 /*! \internal 243 244 Attempts to relocate [begin(), end()) to accommodate the free space for 245 \a n elements at the required side. The side is determined from \a pos. 246 247 Returns \c true if the internal data is moved. Returns \c false when 248 there is no point in moving the data or the move is impossible. If \c 249 false is returned, it is the responsibility of the caller to figure out 250 how to accommodate the free space for \a n elements at \a pos. 251 252 This function expects that certain preconditions are met, e.g. the 253 detach is not needed, n > 0 and so on. This is intentional to reduce the 254 number of if-statements when the caller knows that preconditions would 255 be satisfied. 256 257 \sa reallocateAndGrow 258 */ 259 bool tryReadjustFreeSpace(QArrayData.GrowthPosition pos, qsizetype n, const(T)** data = null) 260 { 261 (mixin(Q_ASSERT(q{!this.needsDetach()}))); 262 (mixin(Q_ASSERT(q{n > 0}))); 263 (mixin(Q_ASSERT(q{(pos == QArrayData.GrowthPosition.GrowsAtEnd && this.freeSpaceAtEnd() < n) 264 || (pos == QArrayData.GrowthPosition.GrowsAtBeginning && this.freeSpaceAtBegin() < n)}))); 265 266 const(qsizetype) capacity = this.constAllocatedCapacity(); 267 const(qsizetype) freeAtBegin = this.freeSpaceAtBegin(); 268 const(qsizetype) freeAtEnd = this.freeSpaceAtEnd(); 269 270 qsizetype dataStartOffset = 0; 271 // algorithm: 272 // a. GrowsAtEnd: relocate if space at begin AND size < (capacity * 2) / 3 273 // [all goes to free space at end]: 274 // new free space at begin = 0 275 // 276 // b. GrowsAtBeginning: relocate if space at end AND size < capacity / 3 277 // [balance the free space]: 278 // new free space at begin = n + (total free space - n) / 2 279 if (pos == QArrayData.GrowthPosition.GrowsAtEnd && freeAtBegin >= n 280 && ((3 * this.size) < (2 * capacity))) { 281 // dataStartOffset = 0; - done in declaration 282 } else if (pos == QArrayData.GrowthPosition.GrowsAtBeginning && freeAtEnd >= n 283 && ((3 * this.size) < capacity)) { 284 // total free space == capacity - size 285 dataStartOffset = n + qMax(0, (capacity - this.size - n) / 2); 286 } else { 287 // nothing to do otherwise 288 return false; 289 } 290 291 relocate(dataStartOffset - freeAtBegin, data); 292 293 (mixin(Q_ASSERT(q{(pos == QArrayData.GrowthPosition.GrowsAtEnd && this.freeSpaceAtEnd() >= n) 294 || (pos == QArrayData.GrowthPosition.GrowsAtBeginning && this.freeSpaceAtBegin() >= n)}))); 295 return true; 296 } 297 298 /*! \internal 299 300 Relocates [begin(), end()) by \a offset and updates \a data if it is not 301 \c nullptr and points into [begin(), end()). 302 */ 303 void relocate(qsizetype offset, const(T)** data = null) 304 { 305 //import qt.core.containertools_impl; 306 307 T* res = this.ptr + offset; 308 // /+ QtPrivate:: +/qt.core.containertools_impl.q_relocate_overlap_n(this.ptr, this.size, res); 309 if (offset > 0) { 310 foreach_reverse (i; 0..this.size) { 311 this.ptr[i + offset] = this.ptr[i]; 312 } 313 } 314 else { 315 foreach (i; 0..this.size) { 316 this.ptr[i + offset] = this.ptr[i]; 317 } 318 } 319 // first update data pointer, then this->ptr 320 if (data && *data >= this.begin() && *data < this.end()) 321 *data += offset; 322 this.ptr = res; 323 } 324 325 // forwards from QArrayData 326 qsizetype allocatedCapacity()/+ noexcept+/ { return d ? d.allocatedCapacity() : 0; } 327 qsizetype constAllocatedCapacity() const/+ noexcept+/ { return d ? d.constAllocatedCapacity() : 0; } 328 void ref_()/+ noexcept+/ { if (d) d.ref_(); } 329 bool deref()/+ noexcept+/ { return !d || d.deref(); } 330 bool isMutable() const/+ noexcept+/ { return cast(bool)(d); } 331 bool isShared() const/+ noexcept+/ { return !d || d.isShared(); } 332 bool isSharedWith(ref const(QArrayDataPointer) other) const/+ noexcept+/ { return d && d == other.d; } 333 bool needsDetach() const/+ noexcept+/ { return !d || d.needsDetach(); } 334 qsizetype detachCapacity(qsizetype newSize) const/+ noexcept+/ { return d ? d.detachCapacity(newSize) : newSize; } 335 const(Data.ArrayOptions) flags() const/+ noexcept+/ { return d ? d.flags : Data.ArrayOptions.ArrayOptionDefault; } 336 void setFlag(Data.ArrayOptions f)/+ noexcept+/ { assert(d); d.flags |= f; } 337 void clearFlag(Data.ArrayOptions f)/+ noexcept+/ { if (d) d.flags &= ~f; } 338 339 Data* d_ptr()/+ noexcept+/ { return d; } 340 void setBegin(T* begin)/+ noexcept+/ { ptr = begin; } 341 342 qsizetype freeSpaceAtBegin() const/+ noexcept+/ 343 { 344 if (d is null) 345 return 0; 346 return this.ptr - Data.dataStart(cast(QArrayData*)d, Data.AlignmentDummy.alignof); 347 } 348 349 qsizetype freeSpaceAtEnd() const/+ noexcept+/ 350 { 351 if (d is null) 352 return 0; 353 return d.constAllocatedCapacity() - freeSpaceAtBegin() - this.size; 354 } 355 356 // allocate and grow. Ensure that at the minimum requiredSpace is available at the requested end 357 static QArrayDataPointer allocateGrow(ref const(QArrayDataPointer) from, qsizetype n, QArrayData.GrowthPosition position) 358 { 359 // calculate new capacity. We keep the free capacity at the side that does not have to grow 360 // to avoid quadratic behavior with mixed append/prepend cases 361 362 // use qMax below, because constAllocatedCapacity() can be 0 when using fromRawData() 363 qsizetype minimalCapacity = qMax(from.size, from.constAllocatedCapacity()) + n; 364 // subtract the free space at the side we want to allocate. This ensures that the total size requested is 365 // the existing allocation at the other side + size + n. 366 minimalCapacity -= (position == QArrayData.GrowthPosition.GrowsAtEnd) ? from.freeSpaceAtEnd() : from.freeSpaceAtBegin(); 367 qsizetype capacity = from.detachCapacity(minimalCapacity); 368 const(bool) grows = capacity > from.constAllocatedCapacity(); 369 auto headerAndDataPtr = Data.allocate(capacity, grows ? QArrayData.AllocationOption.Grow : QArrayData.AllocationOption.KeepSize); 370 auto header = headerAndDataPtr.first; 371 auto dataPtr = headerAndDataPtr.second; 372 const(bool) valid = header !is null && dataPtr !is null; 373 if (!valid) 374 return QArrayDataPointer(header, dataPtr); 375 376 // Idea: * when growing backwards, adjust pointer to prepare free space at the beginning 377 // * when growing forward, adjust by the previous data pointer offset 378 dataPtr += (position == QArrayData.GrowthPosition.GrowsAtBeginning) 379 ? n + qMax(0, (header.alloc - from.size - n) / 2) 380 : from.freeSpaceAtBegin(); 381 header.flags = from.flags(); 382 return QArrayDataPointer(header, dataPtr); 383 } 384 385 /+ friend bool operator==(const QArrayDataPointer &lhs, const QArrayDataPointer &rhs) noexcept 386 { 387 return lhs.data() == rhs.data() && lhs.size == rhs.size; 388 } +/ 389 390 /+ friend bool operator!=(const QArrayDataPointer &lhs, const QArrayDataPointer &rhs) noexcept 391 { 392 return lhs.data() != rhs.data() || lhs.size != rhs.size; 393 } +/ 394 395 Data* d = null; 396 T* ptr = null; 397 qsizetype size = 0; 398 } 399 400 /+pragma(inline, true) void qSwap(T)(/+ QArrayDataPointer<T> &p1, QArrayDataPointer<T> &p2 +/)/+ noexcept+/ 401 { 402 p1.swap(p2); 403 }+/ 404 405 //////////////////////////////////////////////////////////////////////////////// 406 // Q_ARRAY_LITERAL 407 408 // The idea here is to place a (read-only) copy of header and array data in an 409 // mmappable portion of the executable (typically, .rodata section). 410 411 // Hide array inside a lambda 412 /+ #define Q_ARRAY_LITERAL(Type, ...) \ 413 ([]() -> QArrayDataPointer<Type> { \ 414 static Type const data[] = { __VA_ARGS__ }; \ 415 return QArrayDataPointer<Type>::fromRawData(const_cast<Type *>(data), std::size(data)); \ 416 }()) +/ 417 /**/ 418