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/. */
8 # error "Don't include this file directly"
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
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()) {
38 template <class Alloc
, class RelocationStrategy
>
39 nsTArray_base
<Alloc
, RelocationStrategy
>::nsTArray_base(const nsTArray_base
&)
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
53 template <class Alloc
, class RelocationStrategy
>
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!
61 &reinterpret_cast<const AutoTArray
<nsTArray
<uint32_t>, 1>*>(this)
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.
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
) {
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),
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");
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.");
128 return mHdr
== GetAutoArrayBuffer(4) || mHdr
== GetAutoArrayBuffer(8);
131 // defined in nsTArray.cpp
132 bool IsTwiceTheRequiredBytesRepresentableAsUint32(size_t aCapacity
,
135 template <class Alloc
, class RelocationStrategy
>
136 template <typename ActualAlloc
>
137 typename
ActualAlloc::ResultTypeProxy
138 nsTArray_base
<Alloc
, RelocationStrategy
>::ExtendCapacity(size_type aLength
,
140 size_type aElemSize
) {
141 mozilla::CheckedInt
<size_type
> newLength
= aLength
;
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()) {
173 Header
* header
= static_cast<Header
*>(ActualAlloc::Malloc(reqSize
));
175 return ActualAlloc::FailureResult();
178 header
->mCapacity
= aCapacity
;
179 header
->mIsAutoArray
= 0;
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;
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
);
201 // Round up to the next power of two.
202 bytesToAlloc
= mozilla::RoundUpPow2(reqSize
);
206 if (UsesAutoArrayBuffer() || !RelocationStrategy::allowRealloc
) {
208 header
= static_cast<Header
*>(ActualAlloc::Malloc(bytesToAlloc
));
210 return ActualAlloc::FailureResult();
213 RelocationStrategy::RelocateNonOverlappingRegionWithHeader(
214 header
, mHdr
, Length(), aElemSize
);
216 if (!UsesAutoArrayBuffer()) {
217 ActualAlloc::Free(mHdr
);
220 // Realloc() existing data
221 header
= static_cast<Header
*>(ActualAlloc::Realloc(mHdr
, bytesToAlloc
));
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
;
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()) {
246 if (mHdr
->mLength
>= mHdr
->mCapacity
) { // should never be greater than...
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,
260 nsTArrayFallibleAllocator::Free(mHdr
);
266 MOZ_ASSERT(!IsAutoArray(), "autoarray should have fit 0 elements");
267 nsTArrayFallibleAllocator::Free(mHdr
);
272 size_type newSize
= sizeof(Header
) + length
* aElemSize
;
275 if (!RelocationStrategy::allowRealloc
) {
276 // Malloc() and copy.
278 static_cast<Header
*>(nsTArrayFallibleAllocator::Malloc(newSize
));
283 RelocationStrategy::RelocateNonOverlappingRegionWithHeader(
284 newHeader
, mHdr
, Length(), aElemSize
);
286 nsTArrayFallibleAllocator::Free(mHdr
);
288 // Realloc() existing data.
290 static_cast<Header
*>(nsTArrayFallibleAllocator::Realloc(mHdr
, newSize
));
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()) {
309 const bool isAutoArray
= IsAutoArray();
311 nsTArrayFallibleAllocator::Free(mHdr
);
314 mHdr
= GetAutoArrayBufferUnsafe(aElemAlign
);
321 template <class Alloc
, class RelocationStrategy
>
322 template <typename ActualAlloc
>
323 void nsTArray_base
<Alloc
, RelocationStrategy
>::ShiftData(index_type aStart
,
328 if (aOldLen
== aNewLen
) {
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
);
340 // Maybe nothing needs to be shifted
344 // Perform shift (change units to bytes first)
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
,
360 // This method is part of the implementation of
361 // nsTArray::SwapRemoveElement{s,}At. For more information, read the
362 // documentation on that method.
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
);
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) {
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
,
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
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
,
440 : mArray(aArray
), mElemAlign(aElemAlign
), mIsAuto(aArray
.IsAutoArray()) {}
442 template <class Alloc
, class RelocationStrategy
>
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
,
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
>(
479 return ActualAlloc::FailureResult();
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;
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
,
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
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()) {
589 aOther
.mHdr
= EmptyHdr();
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
,
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()) {
635 // aOther might be an (Copyable)AutoTArray though, and it might use its inline
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
642 aOther
.template EnsureNotUsingAutoArrayBuffer
<nsTArrayInfallibleAllocator
>(
646 const bool otherIsAuto
= otherUsesAutoArrayBuffer
|| aOther
.IsAutoArray();
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());
653 mHdr
->mIsAutoArray
= false;
654 aOther
.mHdr
= aOther
.GetAutoArrayBufferUnsafe(aElemAlign
);
655 aOther
.mHdr
->mLength
= 0;
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.)
675 size_type size
= sizeof(Header
) + Length() * aElemSize
;
677 Header
* header
= static_cast<Header
*>(ActualAlloc::Malloc(size
));
682 RelocationStrategy::RelocateNonOverlappingRegionWithHeader(
683 header
, mHdr
, Length(), aElemSize
);
684 header
->mCapacity
= Length();