Bug 1860073 [wpt PR 42637] - Update wpt metadata, a=testonly
[gecko.git] / xpcom / ds / nsTArray-inl.h
blob3d9ff1d41056eeb0637939c842a8c9e898879284
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef nsTArray_h__
8 # error "Don't include this file directly"
9 #endif
11 // NOTE: We don't use MOZ_COUNT_CTOR/MOZ_COUNT_DTOR to perform leak checking of
12 // nsTArray_base objects intentionally for the following reasons:
13 // * The leak logging isn't as useful as other types of logging, as
14 // nsTArray_base is frequently relocated without invoking a constructor, such
15 // as when stored within another nsTArray. This means that
16 // XPCOM_MEM_LOG_CLASSES cannot be used to identify specific leaks of nsTArray
17 // objects.
18 // * The nsTArray type is layout compatible with the ThinVec crate with the
19 // correct flags, and ThinVec does not currently perform leak logging.
20 // This means that if a large number of arrays are transferred between Rust
21 // and C++ code using ThinVec, for example within another ThinVec, they
22 // will not be logged correctly and might appear as e.g. negative leaks.
23 // * Leaks which have been found thanks to the leak logging added by this
24 // type have often not been significant, and/or have needed to be
25 // circumvented using some other mechanism. Most leaks found with this type
26 // in them also include other types which will continue to be tracked.
28 template <class Alloc, class RelocationStrategy>
29 nsTArray_base<Alloc, RelocationStrategy>::nsTArray_base() : mHdr(EmptyHdr()) {}
31 template <class Alloc, class RelocationStrategy>
32 nsTArray_base<Alloc, RelocationStrategy>::~nsTArray_base() {
33 if (!HasEmptyHeader() && !UsesAutoArrayBuffer()) {
34 Alloc::Free(mHdr);
38 template <class Alloc, class RelocationStrategy>
39 nsTArray_base<Alloc, RelocationStrategy>::nsTArray_base(const nsTArray_base&)
40 : mHdr(EmptyHdr()) {
41 // Actual copying happens through nsTArray_CopyEnabler, we just need to do the
42 // initialization of mHdr.
45 template <class Alloc, class RelocationStrategy>
46 nsTArray_base<Alloc, RelocationStrategy>&
47 nsTArray_base<Alloc, RelocationStrategy>::operator=(const nsTArray_base&) {
48 // Actual copying happens through nsTArray_CopyEnabler, so do nothing here (do
49 // not copy mHdr).
50 return *this;
53 template <class Alloc, class RelocationStrategy>
54 const nsTArrayHeader*
55 nsTArray_base<Alloc, RelocationStrategy>::GetAutoArrayBufferUnsafe(
56 size_t aElemAlign) const {
57 // Assuming |this| points to an nsAutoArray, we want to get a pointer to
58 // mAutoBuf. So just cast |this| to nsAutoArray* and read &mAutoBuf!
60 const void* autoBuf =
61 &reinterpret_cast<const AutoTArray<nsTArray<uint32_t>, 1>*>(this)
62 ->mAutoBuf;
64 // If we're on a 32-bit system and aElemAlign is 8, we need to adjust our
65 // pointer to take into account the extra alignment in the auto array.
67 static_assert(
68 sizeof(void*) != 4 || (MOZ_ALIGNOF(mozilla::AlignedElem<8>) == 8 &&
69 sizeof(AutoTArray<mozilla::AlignedElem<8>, 1>) ==
70 sizeof(void*) + sizeof(nsTArrayHeader) + 4 +
71 sizeof(mozilla::AlignedElem<8>)),
72 "auto array padding wasn't what we expected");
74 // We don't support alignments greater than 8 bytes.
75 MOZ_ASSERT(aElemAlign <= 4 || aElemAlign == 8, "unsupported alignment.");
76 if (sizeof(void*) == 4 && aElemAlign == 8) {
77 autoBuf = reinterpret_cast<const char*>(autoBuf) + 4;
80 return reinterpret_cast<const Header*>(autoBuf);
83 template <class Alloc, class RelocationStrategy>
84 bool nsTArray_base<Alloc, RelocationStrategy>::UsesAutoArrayBuffer() const {
85 if (!mHdr->mIsAutoArray) {
86 return false;
89 // This is nuts. If we were sane, we'd pass aElemAlign as a parameter to
90 // this function. Unfortunately this function is called in nsTArray_base's
91 // destructor, at which point we don't know value_type's alignment.
93 // We'll fall on our face and return true when we should say false if
95 // * we're not using our auto buffer,
96 // * aElemAlign == 4, and
97 // * mHdr == GetAutoArrayBuffer(8).
99 // This could happen if |*this| lives on the heap and malloc allocated our
100 // buffer on the heap adjacent to |*this|.
102 // However, we can show that this can't happen. If |this| is an auto array
103 // (as we ensured at the beginning of the method), GetAutoArrayBuffer(8)
104 // always points to memory owned by |*this|, because (as we assert below)
106 // * GetAutoArrayBuffer(8) is at most 4 bytes past GetAutoArrayBuffer(4),
107 // and
108 // * sizeof(nsTArrayHeader) > 4.
110 // Since AutoTArray always contains an nsTArrayHeader,
111 // GetAutoArrayBuffer(8) will always point inside the auto array object,
112 // even if it doesn't point at the beginning of the header.
114 // Note that this means that we can't store elements with alignment 16 in an
115 // nsTArray, because GetAutoArrayBuffer(16) could lie outside the memory
116 // owned by this AutoTArray. We statically assert that value_type's
117 // alignment is 8 bytes or less in AutoTArray.
119 static_assert(sizeof(nsTArrayHeader) > 4, "see comment above");
121 #ifdef DEBUG
122 ptrdiff_t diff = reinterpret_cast<const char*>(GetAutoArrayBuffer(8)) -
123 reinterpret_cast<const char*>(GetAutoArrayBuffer(4));
124 MOZ_ASSERT(diff >= 0 && diff <= 4,
125 "GetAutoArrayBuffer doesn't do what we expect.");
126 #endif
128 return mHdr == GetAutoArrayBuffer(4) || mHdr == GetAutoArrayBuffer(8);
131 // defined in nsTArray.cpp
132 bool IsTwiceTheRequiredBytesRepresentableAsUint32(size_t aCapacity,
133 size_t aElemSize);
135 template <class Alloc, class RelocationStrategy>
136 template <typename ActualAlloc>
137 typename ActualAlloc::ResultTypeProxy
138 nsTArray_base<Alloc, RelocationStrategy>::ExtendCapacity(size_type aLength,
139 size_type aCount,
140 size_type aElemSize) {
141 mozilla::CheckedInt<size_type> newLength = aLength;
142 newLength += aCount;
144 if (!newLength.isValid()) {
145 return ActualAlloc::FailureResult();
148 return this->EnsureCapacity<ActualAlloc>(newLength.value(), aElemSize);
151 template <class Alloc, class RelocationStrategy>
152 template <typename ActualAlloc>
153 typename ActualAlloc::ResultTypeProxy
154 nsTArray_base<Alloc, RelocationStrategy>::EnsureCapacityImpl(
155 size_type aCapacity, size_type aElemSize) {
156 MOZ_ASSERT(aCapacity > mHdr->mCapacity,
157 "Should have been checked by caller (EnsureCapacity)");
159 // If the requested memory allocation exceeds size_type(-1)/2, then
160 // our doubling algorithm may not be able to allocate it.
161 // Additionally, if it exceeds uint32_t(-1) then we couldn't fit in the
162 // Header::mCapacity member. Just bail out in cases like that. We don't want
163 // to be allocating 2 GB+ arrays anyway.
164 if (!IsTwiceTheRequiredBytesRepresentableAsUint32(aCapacity, aElemSize)) {
165 ActualAlloc::SizeTooBig((size_t)aCapacity * aElemSize);
166 return ActualAlloc::FailureResult();
169 size_t reqSize = sizeof(Header) + aCapacity * aElemSize;
171 if (HasEmptyHeader()) {
172 // Malloc() new data
173 Header* header = static_cast<Header*>(ActualAlloc::Malloc(reqSize));
174 if (!header) {
175 return ActualAlloc::FailureResult();
177 header->mLength = 0;
178 header->mCapacity = aCapacity;
179 header->mIsAutoArray = 0;
180 mHdr = header;
182 return ActualAlloc::SuccessResult();
185 // We increase our capacity so that the allocated buffer grows exponentially,
186 // which gives us amortized O(1) appending. Below the threshold, we use
187 // powers-of-two. Above the threshold, we grow by at least 1.125, rounding up
188 // to the nearest MiB.
189 const size_t slowGrowthThreshold = 8 * 1024 * 1024;
191 size_t bytesToAlloc;
192 if (reqSize >= slowGrowthThreshold) {
193 size_t currSize = sizeof(Header) + Capacity() * aElemSize;
194 size_t minNewSize = currSize + (currSize >> 3); // multiply by 1.125
195 bytesToAlloc = reqSize > minNewSize ? reqSize : minNewSize;
197 // Round up to the next multiple of MiB.
198 const size_t MiB = 1 << 20;
199 bytesToAlloc = MiB * ((bytesToAlloc + MiB - 1) / MiB);
200 } else {
201 // Round up to the next power of two.
202 bytesToAlloc = mozilla::RoundUpPow2(reqSize);
205 Header* header;
206 if (UsesAutoArrayBuffer() || !RelocationStrategy::allowRealloc) {
207 // Malloc() and copy
208 header = static_cast<Header*>(ActualAlloc::Malloc(bytesToAlloc));
209 if (!header) {
210 return ActualAlloc::FailureResult();
213 RelocationStrategy::RelocateNonOverlappingRegionWithHeader(
214 header, mHdr, Length(), aElemSize);
216 if (!UsesAutoArrayBuffer()) {
217 ActualAlloc::Free(mHdr);
219 } else {
220 // Realloc() existing data
221 header = static_cast<Header*>(ActualAlloc::Realloc(mHdr, bytesToAlloc));
222 if (!header) {
223 return ActualAlloc::FailureResult();
227 // How many elements can we fit in bytesToAlloc?
228 size_t newCapacity = (bytesToAlloc - sizeof(Header)) / aElemSize;
229 MOZ_ASSERT(newCapacity >= aCapacity, "Didn't enlarge the array enough!");
230 header->mCapacity = newCapacity;
232 mHdr = header;
234 return ActualAlloc::SuccessResult();
237 // We don't need use Alloc template parameter specified here because failure to
238 // shrink the capacity will leave the array unchanged.
239 template <class Alloc, class RelocationStrategy>
240 void nsTArray_base<Alloc, RelocationStrategy>::ShrinkCapacity(
241 size_type aElemSize, size_t aElemAlign) {
242 if (HasEmptyHeader() || UsesAutoArrayBuffer()) {
243 return;
246 if (mHdr->mLength >= mHdr->mCapacity) { // should never be greater than...
247 return;
250 size_type length = Length();
252 if (IsAutoArray() && GetAutoArrayBuffer(aElemAlign)->mCapacity >= length) {
253 Header* header = GetAutoArrayBuffer(aElemAlign);
255 // Move the data, but don't copy the header to avoid overwriting mCapacity.
256 header->mLength = length;
257 RelocationStrategy::RelocateNonOverlappingRegion(header + 1, mHdr + 1,
258 length, aElemSize);
260 nsTArrayFallibleAllocator::Free(mHdr);
261 mHdr = header;
262 return;
265 if (length == 0) {
266 MOZ_ASSERT(!IsAutoArray(), "autoarray should have fit 0 elements");
267 nsTArrayFallibleAllocator::Free(mHdr);
268 mHdr = EmptyHdr();
269 return;
272 size_type newSize = sizeof(Header) + length * aElemSize;
274 Header* newHeader;
275 if (!RelocationStrategy::allowRealloc) {
276 // Malloc() and copy.
277 newHeader =
278 static_cast<Header*>(nsTArrayFallibleAllocator::Malloc(newSize));
279 if (!newHeader) {
280 return;
283 RelocationStrategy::RelocateNonOverlappingRegionWithHeader(
284 newHeader, mHdr, Length(), aElemSize);
286 nsTArrayFallibleAllocator::Free(mHdr);
287 } else {
288 // Realloc() existing data.
289 newHeader =
290 static_cast<Header*>(nsTArrayFallibleAllocator::Realloc(mHdr, newSize));
291 if (!newHeader) {
292 return;
296 mHdr = newHeader;
297 mHdr->mCapacity = length;
300 template <class Alloc, class RelocationStrategy>
301 void nsTArray_base<Alloc, RelocationStrategy>::ShrinkCapacityToZero(
302 size_type aElemSize, size_t aElemAlign) {
303 MOZ_ASSERT(mHdr->mLength == 0);
305 if (HasEmptyHeader() || UsesAutoArrayBuffer()) {
306 return;
309 const bool isAutoArray = IsAutoArray();
311 nsTArrayFallibleAllocator::Free(mHdr);
313 if (isAutoArray) {
314 mHdr = GetAutoArrayBufferUnsafe(aElemAlign);
315 mHdr->mLength = 0;
316 } else {
317 mHdr = EmptyHdr();
321 template <class Alloc, class RelocationStrategy>
322 template <typename ActualAlloc>
323 void nsTArray_base<Alloc, RelocationStrategy>::ShiftData(index_type aStart,
324 size_type aOldLen,
325 size_type aNewLen,
326 size_type aElemSize,
327 size_t aElemAlign) {
328 if (aOldLen == aNewLen) {
329 return;
332 // Determine how many elements need to be shifted
333 size_type num = mHdr->mLength - (aStart + aOldLen);
335 // Compute the resulting length of the array
336 mHdr->mLength += aNewLen - aOldLen;
337 if (mHdr->mLength == 0) {
338 ShrinkCapacityToZero(aElemSize, aElemAlign);
339 } else {
340 // Maybe nothing needs to be shifted
341 if (num == 0) {
342 return;
344 // Perform shift (change units to bytes first)
345 aStart *= aElemSize;
346 aNewLen *= aElemSize;
347 aOldLen *= aElemSize;
348 char* baseAddr = reinterpret_cast<char*>(mHdr + 1) + aStart;
349 RelocationStrategy::RelocateOverlappingRegion(
350 baseAddr + aNewLen, baseAddr + aOldLen, num, aElemSize);
354 template <class Alloc, class RelocationStrategy>
355 template <typename ActualAlloc>
356 void nsTArray_base<Alloc, RelocationStrategy>::SwapFromEnd(index_type aStart,
357 size_type aCount,
358 size_type aElemSize,
359 size_t aElemAlign) {
360 // This method is part of the implementation of
361 // nsTArray::SwapRemoveElement{s,}At. For more information, read the
362 // documentation on that method.
363 if (aCount == 0) {
364 return;
367 // We are going to be removing aCount elements. Update our length to point to
368 // the new end of the array.
369 size_type oldLength = mHdr->mLength;
370 mHdr->mLength -= aCount;
372 if (mHdr->mLength == 0) {
373 // If we have no elements remaining in the array, we can free our buffer.
374 ShrinkCapacityToZero(aElemSize, aElemAlign);
375 return;
378 // Determine how many elements we need to move from the end of the array into
379 // the now-removed section. This will either be the number of elements which
380 // were removed (if there are more elements in the tail of the array), or the
381 // entire tail of the array, whichever is smaller.
382 size_type relocCount = std::min(aCount, mHdr->mLength - aStart);
383 if (relocCount == 0) {
384 return;
387 // Move the elements which are now stranded after the end of the array back
388 // into the now-vacated memory.
389 index_type sourceBytes = (oldLength - relocCount) * aElemSize;
390 index_type destBytes = aStart * aElemSize;
392 // Perform the final copy. This is guaranteed to be a non-overlapping copy
393 // as our source contains only still-valid entries, and the destination
394 // contains only invalid entries which need to be overwritten.
395 MOZ_ASSERT(sourceBytes >= destBytes,
396 "The source should be after the destination.");
397 MOZ_ASSERT(sourceBytes - destBytes >= relocCount * aElemSize,
398 "The range should be nonoverlapping");
400 char* baseAddr = reinterpret_cast<char*>(mHdr + 1);
401 RelocationStrategy::RelocateNonOverlappingRegion(
402 baseAddr + destBytes, baseAddr + sourceBytes, relocCount, aElemSize);
405 template <class Alloc, class RelocationStrategy>
406 template <typename ActualAlloc>
407 typename ActualAlloc::ResultTypeProxy
408 nsTArray_base<Alloc, RelocationStrategy>::InsertSlotsAt(index_type aIndex,
409 size_type aCount,
410 size_type aElemSize,
411 size_t aElemAlign) {
412 if (MOZ_UNLIKELY(aIndex > Length())) {
413 mozilla::detail::InvalidArrayIndex_CRASH(aIndex, Length());
416 if (!ActualAlloc::Successful(
417 this->ExtendCapacity<ActualAlloc>(Length(), aCount, aElemSize))) {
418 return ActualAlloc::FailureResult();
421 // Move the existing elements as needed. Note that this will
422 // change our mLength, so no need to call IncrementLength.
423 ShiftData<ActualAlloc>(aIndex, 0, aCount, aElemSize, aElemAlign);
425 return ActualAlloc::SuccessResult();
428 // nsTArray_base::IsAutoArrayRestorer is an RAII class which takes
429 // |nsTArray_base &array| in its constructor. When it's destructed, it ensures
430 // that
432 // * array.mIsAutoArray has the same value as it did when we started, and
433 // * if array has an auto buffer and mHdr would otherwise point to
434 // sEmptyTArrayHeader, array.mHdr points to array's auto buffer.
436 template <class Alloc, class RelocationStrategy>
437 nsTArray_base<Alloc, RelocationStrategy>::IsAutoArrayRestorer::
438 IsAutoArrayRestorer(nsTArray_base<Alloc, RelocationStrategy>& aArray,
439 size_t aElemAlign)
440 : mArray(aArray), mElemAlign(aElemAlign), mIsAuto(aArray.IsAutoArray()) {}
442 template <class Alloc, class RelocationStrategy>
443 nsTArray_base<Alloc,
444 RelocationStrategy>::IsAutoArrayRestorer::~IsAutoArrayRestorer() {
445 // Careful: We don't want to set mIsAutoArray = 1 on sEmptyTArrayHeader.
446 if (mIsAuto && mArray.HasEmptyHeader()) {
447 // Call GetAutoArrayBufferUnsafe() because GetAutoArrayBuffer() asserts
448 // that mHdr->mIsAutoArray is true, which surely isn't the case here.
449 mArray.mHdr = mArray.GetAutoArrayBufferUnsafe(mElemAlign);
450 mArray.mHdr->mLength = 0;
451 } else if (!mArray.HasEmptyHeader()) {
452 mArray.mHdr->mIsAutoArray = mIsAuto;
456 template <class Alloc, class RelocationStrategy>
457 template <typename ActualAlloc, class Allocator>
458 typename ActualAlloc::ResultTypeProxy
459 nsTArray_base<Alloc, RelocationStrategy>::SwapArrayElements(
460 nsTArray_base<Allocator, RelocationStrategy>& aOther, size_type aElemSize,
461 size_t aElemAlign) {
462 // EnsureNotUsingAutoArrayBuffer will set mHdr = sEmptyTArrayHeader even if we
463 // have an auto buffer. We need to point mHdr back to our auto buffer before
464 // we return, otherwise we'll forget that we have an auto buffer at all!
465 // IsAutoArrayRestorer takes care of this for us.
467 IsAutoArrayRestorer ourAutoRestorer(*this, aElemAlign);
468 typename nsTArray_base<Allocator, RelocationStrategy>::IsAutoArrayRestorer
469 otherAutoRestorer(aOther, aElemAlign);
471 // If neither array uses an auto buffer which is big enough to store the
472 // other array's elements, then ensure that both arrays use malloc'ed storage
473 // and swap their mHdr pointers.
474 if ((!UsesAutoArrayBuffer() || Capacity() < aOther.Length()) &&
475 (!aOther.UsesAutoArrayBuffer() || aOther.Capacity() < Length())) {
476 if (!EnsureNotUsingAutoArrayBuffer<ActualAlloc>(aElemSize) ||
477 !aOther.template EnsureNotUsingAutoArrayBuffer<ActualAlloc>(
478 aElemSize)) {
479 return ActualAlloc::FailureResult();
482 Header* temp = mHdr;
483 mHdr = aOther.mHdr;
484 aOther.mHdr = temp;
486 return ActualAlloc::SuccessResult();
489 // Swap the two arrays by copying, since at least one is using an auto
490 // buffer which is large enough to hold all of the aOther's elements. We'll
491 // copy the shorter array into temporary storage.
493 // (We could do better than this in some circumstances. Suppose we're
494 // swapping arrays X and Y. X has space for 2 elements in its auto buffer,
495 // but currently has length 4, so it's using malloc'ed storage. Y has length
496 // 2. When we swap X and Y, we don't need to use a temporary buffer; we can
497 // write Y straight into X's auto buffer, write X's malloc'ed buffer on top
498 // of Y, and then switch X to using its auto buffer.)
500 if (!ActualAlloc::Successful(
501 EnsureCapacity<ActualAlloc>(aOther.Length(), aElemSize)) ||
502 !Allocator::Successful(
503 aOther.template EnsureCapacity<Allocator>(Length(), aElemSize))) {
504 return ActualAlloc::FailureResult();
507 // The EnsureCapacity calls above shouldn't have caused *both* arrays to
508 // switch from their auto buffers to malloc'ed space.
509 MOZ_ASSERT(UsesAutoArrayBuffer() || aOther.UsesAutoArrayBuffer(),
510 "One of the arrays should be using its auto buffer.");
512 size_type smallerLength = XPCOM_MIN(Length(), aOther.Length());
513 size_type largerLength = XPCOM_MAX(Length(), aOther.Length());
514 void* smallerElements;
515 void* largerElements;
516 if (Length() <= aOther.Length()) {
517 smallerElements = Hdr() + 1;
518 largerElements = aOther.Hdr() + 1;
519 } else {
520 smallerElements = aOther.Hdr() + 1;
521 largerElements = Hdr() + 1;
524 // Allocate temporary storage for the smaller of the two arrays. We want to
525 // allocate this space on the stack, if it's not too large. Sounds like a
526 // job for AutoTArray! (One of the two arrays we're swapping is using an
527 // auto buffer, so we're likely not allocating a lot of space here. But one
528 // could, in theory, allocate a huge AutoTArray on the heap.)
529 AutoTArray<uint8_t, 64 * sizeof(void*)> temp;
530 if (!ActualAlloc::Successful(temp.template EnsureCapacity<ActualAlloc>(
531 smallerLength * aElemSize, sizeof(uint8_t)))) {
532 return ActualAlloc::FailureResult();
535 RelocationStrategy::RelocateNonOverlappingRegion(
536 temp.Elements(), smallerElements, smallerLength, aElemSize);
537 RelocationStrategy::RelocateNonOverlappingRegion(
538 smallerElements, largerElements, largerLength, aElemSize);
539 RelocationStrategy::RelocateNonOverlappingRegion(
540 largerElements, temp.Elements(), smallerLength, aElemSize);
542 // Swap the arrays' lengths.
543 MOZ_ASSERT((aOther.Length() == 0 || !HasEmptyHeader()) &&
544 (Length() == 0 || !aOther.HasEmptyHeader()),
545 "Don't set sEmptyTArrayHeader's length.");
546 size_type tempLength = Length();
548 // Avoid writing to EmptyHdr, since it can trigger false
549 // positives with TSan.
550 if (!HasEmptyHeader()) {
551 mHdr->mLength = aOther.Length();
553 if (!aOther.HasEmptyHeader()) {
554 aOther.mHdr->mLength = tempLength;
557 return ActualAlloc::SuccessResult();
560 template <class Alloc, class RelocationStrategy>
561 template <class Allocator>
562 void nsTArray_base<Alloc, RelocationStrategy>::MoveInit(
563 nsTArray_base<Allocator, RelocationStrategy>& aOther, size_type aElemSize,
564 size_t aElemAlign) {
565 // This method is similar to SwapArrayElements, but specialized for the case
566 // where the target array is empty with no allocated heap storage. It is
567 // provided and used to simplify template instantiation and enable better code
568 // generation.
570 MOZ_ASSERT(Length() == 0);
571 MOZ_ASSERT(Capacity() == 0 || (IsAutoArray() && UsesAutoArrayBuffer()));
573 // EnsureNotUsingAutoArrayBuffer will set mHdr = sEmptyTArrayHeader even if we
574 // have an auto buffer. We need to point mHdr back to our auto buffer before
575 // we return, otherwise we'll forget that we have an auto buffer at all!
576 // IsAutoArrayRestorer takes care of this for us.
578 IsAutoArrayRestorer ourAutoRestorer(*this, aElemAlign);
579 typename nsTArray_base<Allocator, RelocationStrategy>::IsAutoArrayRestorer
580 otherAutoRestorer(aOther, aElemAlign);
582 // If neither array uses an auto buffer which is big enough to store the
583 // other array's elements, then ensure that both arrays use malloc'ed storage
584 // and swap their mHdr pointers.
585 if ((!IsAutoArray() || Capacity() < aOther.Length()) &&
586 !aOther.UsesAutoArrayBuffer()) {
587 mHdr = aOther.mHdr;
589 aOther.mHdr = EmptyHdr();
591 return;
594 // Move the data by copying, since at least one has an auto
595 // buffer which is large enough to hold all of the aOther's elements.
597 EnsureCapacity<nsTArrayInfallibleAllocator>(aOther.Length(), aElemSize);
599 // The EnsureCapacity calls above shouldn't have caused *both* arrays to
600 // switch from their auto buffers to malloc'ed space.
601 MOZ_ASSERT(UsesAutoArrayBuffer() || aOther.UsesAutoArrayBuffer(),
602 "One of the arrays should be using its auto buffer.");
604 RelocationStrategy::RelocateNonOverlappingRegion(Hdr() + 1, aOther.Hdr() + 1,
605 aOther.Length(), aElemSize);
607 // Swap the arrays' lengths.
608 MOZ_ASSERT((aOther.Length() == 0 || !HasEmptyHeader()) &&
609 (Length() == 0 || !aOther.HasEmptyHeader()),
610 "Don't set sEmptyTArrayHeader's length.");
612 // Avoid writing to EmptyHdr, since it can trigger false
613 // positives with TSan.
614 if (!HasEmptyHeader()) {
615 mHdr->mLength = aOther.Length();
617 if (!aOther.HasEmptyHeader()) {
618 aOther.mHdr->mLength = 0;
622 template <class Alloc, class RelocationStrategy>
623 template <class Allocator>
624 void nsTArray_base<Alloc, RelocationStrategy>::MoveConstructNonAutoArray(
625 nsTArray_base<Allocator, RelocationStrategy>& aOther, size_type aElemSize,
626 size_t aElemAlign) {
627 // We know that we are not an (Copyable)AutoTArray and we know that we are
628 // empty, so don't use SwapArrayElements which doesn't know either of these
629 // facts and is very complex.
631 if (aOther.IsEmpty()) {
632 return;
635 // aOther might be an (Copyable)AutoTArray though, and it might use its inline
636 // buffer.
637 const bool otherUsesAutoArrayBuffer = aOther.UsesAutoArrayBuffer();
638 if (otherUsesAutoArrayBuffer) {
639 // Use nsTArrayInfallibleAllocator regardless of Alloc because this is
640 // called from a move constructor, which cannot report an error to the
641 // caller.
642 aOther.template EnsureNotUsingAutoArrayBuffer<nsTArrayInfallibleAllocator>(
643 aElemSize);
646 const bool otherIsAuto = otherUsesAutoArrayBuffer || aOther.IsAutoArray();
647 mHdr = aOther.mHdr;
648 // We might write to mHdr, so ensure it's not the static empty header. aOther
649 // shouldn't have been empty if we get here anyway.
650 MOZ_ASSERT(!HasEmptyHeader());
652 if (otherIsAuto) {
653 mHdr->mIsAutoArray = false;
654 aOther.mHdr = aOther.GetAutoArrayBufferUnsafe(aElemAlign);
655 aOther.mHdr->mLength = 0;
656 } else {
657 aOther.mHdr = aOther.EmptyHdr();
661 template <class Alloc, class RelocationStrategy>
662 template <typename ActualAlloc>
663 bool nsTArray_base<Alloc, RelocationStrategy>::EnsureNotUsingAutoArrayBuffer(
664 size_type aElemSize) {
665 if (UsesAutoArrayBuffer()) {
666 // If you call this on a 0-length array, we'll set that array's mHdr to
667 // sEmptyTArrayHeader, in flagrant violation of the AutoTArray invariants.
668 // It's up to you to set it back! (If you don't, the AutoTArray will
669 // forget that it has an auto buffer.)
670 if (Length() == 0) {
671 mHdr = EmptyHdr();
672 return true;
675 size_type size = sizeof(Header) + Length() * aElemSize;
677 Header* header = static_cast<Header*>(ActualAlloc::Malloc(size));
678 if (!header) {
679 return false;
682 RelocationStrategy::RelocateNonOverlappingRegionWithHeader(
683 header, mHdr, Length(), aElemSize);
684 header->mCapacity = Length();
685 mHdr = header;
688 return true;