Bug 1825336 - Make toolkit/components/url-classifier/ buildable outside of a unified...
[gecko.git] / xpcom / ds / nsTArray-inl.h
blob801d3fab01c4d4d432cf7888a7238be25c2ae8b3
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>::EnsureCapacity(size_type aCapacity,
155 size_type aElemSize) {
156 // This should be the most common case so test this first
157 if (aCapacity <= mHdr->mCapacity) {
158 return ActualAlloc::SuccessResult();
161 // If the requested memory allocation exceeds size_type(-1)/2, then
162 // our doubling algorithm may not be able to allocate it.
163 // Additionally, if it exceeds uint32_t(-1) then we couldn't fit in the
164 // Header::mCapacity member. Just bail out in cases like that. We don't want
165 // to be allocating 2 GB+ arrays anyway.
166 if (!IsTwiceTheRequiredBytesRepresentableAsUint32(aCapacity, aElemSize)) {
167 ActualAlloc::SizeTooBig((size_t)aCapacity * aElemSize);
168 return ActualAlloc::FailureResult();
171 size_t reqSize = sizeof(Header) + aCapacity * aElemSize;
173 if (HasEmptyHeader()) {
174 // Malloc() new data
175 Header* header = static_cast<Header*>(ActualAlloc::Malloc(reqSize));
176 if (!header) {
177 return ActualAlloc::FailureResult();
179 header->mLength = 0;
180 header->mCapacity = aCapacity;
181 header->mIsAutoArray = 0;
182 mHdr = header;
184 return ActualAlloc::SuccessResult();
187 // We increase our capacity so that the allocated buffer grows exponentially,
188 // which gives us amortized O(1) appending. Below the threshold, we use
189 // powers-of-two. Above the threshold, we grow by at least 1.125, rounding up
190 // to the nearest MiB.
191 const size_t slowGrowthThreshold = 8 * 1024 * 1024;
193 size_t bytesToAlloc;
194 if (reqSize >= slowGrowthThreshold) {
195 size_t currSize = sizeof(Header) + Capacity() * aElemSize;
196 size_t minNewSize = currSize + (currSize >> 3); // multiply by 1.125
197 bytesToAlloc = reqSize > minNewSize ? reqSize : minNewSize;
199 // Round up to the next multiple of MiB.
200 const size_t MiB = 1 << 20;
201 bytesToAlloc = MiB * ((bytesToAlloc + MiB - 1) / MiB);
202 } else {
203 // Round up to the next power of two.
204 bytesToAlloc = mozilla::RoundUpPow2(reqSize);
207 Header* header;
208 if (UsesAutoArrayBuffer() || !RelocationStrategy::allowRealloc) {
209 // Malloc() and copy
210 header = static_cast<Header*>(ActualAlloc::Malloc(bytesToAlloc));
211 if (!header) {
212 return ActualAlloc::FailureResult();
215 RelocationStrategy::RelocateNonOverlappingRegionWithHeader(
216 header, mHdr, Length(), aElemSize);
218 if (!UsesAutoArrayBuffer()) {
219 ActualAlloc::Free(mHdr);
221 } else {
222 // Realloc() existing data
223 header = static_cast<Header*>(ActualAlloc::Realloc(mHdr, bytesToAlloc));
224 if (!header) {
225 return ActualAlloc::FailureResult();
229 // How many elements can we fit in bytesToAlloc?
230 size_t newCapacity = (bytesToAlloc - sizeof(Header)) / aElemSize;
231 MOZ_ASSERT(newCapacity >= aCapacity, "Didn't enlarge the array enough!");
232 header->mCapacity = newCapacity;
234 mHdr = header;
236 return ActualAlloc::SuccessResult();
239 // We don't need use Alloc template parameter specified here because failure to
240 // shrink the capacity will leave the array unchanged.
241 template <class Alloc, class RelocationStrategy>
242 void nsTArray_base<Alloc, RelocationStrategy>::ShrinkCapacity(
243 size_type aElemSize, size_t aElemAlign) {
244 if (HasEmptyHeader() || UsesAutoArrayBuffer()) {
245 return;
248 if (mHdr->mLength >= mHdr->mCapacity) { // should never be greater than...
249 return;
252 size_type length = Length();
254 if (IsAutoArray() && GetAutoArrayBuffer(aElemAlign)->mCapacity >= length) {
255 Header* header = GetAutoArrayBuffer(aElemAlign);
257 // Move the data, but don't copy the header to avoid overwriting mCapacity.
258 header->mLength = length;
259 RelocationStrategy::RelocateNonOverlappingRegion(header + 1, mHdr + 1,
260 length, aElemSize);
262 nsTArrayFallibleAllocator::Free(mHdr);
263 mHdr = header;
264 return;
267 if (length == 0) {
268 MOZ_ASSERT(!IsAutoArray(), "autoarray should have fit 0 elements");
269 nsTArrayFallibleAllocator::Free(mHdr);
270 mHdr = EmptyHdr();
271 return;
274 size_type newSize = sizeof(Header) + length * aElemSize;
276 Header* newHeader;
277 if (!RelocationStrategy::allowRealloc) {
278 // Malloc() and copy.
279 newHeader =
280 static_cast<Header*>(nsTArrayFallibleAllocator::Malloc(newSize));
281 if (!newHeader) {
282 return;
285 RelocationStrategy::RelocateNonOverlappingRegionWithHeader(
286 newHeader, mHdr, Length(), aElemSize);
288 nsTArrayFallibleAllocator::Free(mHdr);
289 } else {
290 // Realloc() existing data.
291 newHeader =
292 static_cast<Header*>(nsTArrayFallibleAllocator::Realloc(mHdr, newSize));
293 if (!newHeader) {
294 return;
298 mHdr = newHeader;
299 mHdr->mCapacity = length;
302 template <class Alloc, class RelocationStrategy>
303 void nsTArray_base<Alloc, RelocationStrategy>::ShrinkCapacityToZero(
304 size_type aElemSize, size_t aElemAlign) {
305 MOZ_ASSERT(mHdr->mLength == 0);
307 if (HasEmptyHeader() || UsesAutoArrayBuffer()) {
308 return;
311 const bool isAutoArray = IsAutoArray();
313 nsTArrayFallibleAllocator::Free(mHdr);
315 if (isAutoArray) {
316 mHdr = GetAutoArrayBufferUnsafe(aElemAlign);
317 mHdr->mLength = 0;
318 } else {
319 mHdr = EmptyHdr();
323 template <class Alloc, class RelocationStrategy>
324 template <typename ActualAlloc>
325 void nsTArray_base<Alloc, RelocationStrategy>::ShiftData(index_type aStart,
326 size_type aOldLen,
327 size_type aNewLen,
328 size_type aElemSize,
329 size_t aElemAlign) {
330 if (aOldLen == aNewLen) {
331 return;
334 // Determine how many elements need to be shifted
335 size_type num = mHdr->mLength - (aStart + aOldLen);
337 // Compute the resulting length of the array
338 mHdr->mLength += aNewLen - aOldLen;
339 if (mHdr->mLength == 0) {
340 ShrinkCapacityToZero(aElemSize, aElemAlign);
341 } else {
342 // Maybe nothing needs to be shifted
343 if (num == 0) {
344 return;
346 // Perform shift (change units to bytes first)
347 aStart *= aElemSize;
348 aNewLen *= aElemSize;
349 aOldLen *= aElemSize;
350 char* baseAddr = reinterpret_cast<char*>(mHdr + 1) + aStart;
351 RelocationStrategy::RelocateOverlappingRegion(
352 baseAddr + aNewLen, baseAddr + aOldLen, num, aElemSize);
356 template <class Alloc, class RelocationStrategy>
357 template <typename ActualAlloc>
358 void nsTArray_base<Alloc, RelocationStrategy>::SwapFromEnd(index_type aStart,
359 size_type aCount,
360 size_type aElemSize,
361 size_t aElemAlign) {
362 // This method is part of the implementation of
363 // nsTArray::SwapRemoveElement{s,}At. For more information, read the
364 // documentation on that method.
365 if (aCount == 0) {
366 return;
369 // We are going to be removing aCount elements. Update our length to point to
370 // the new end of the array.
371 size_type oldLength = mHdr->mLength;
372 mHdr->mLength -= aCount;
374 if (mHdr->mLength == 0) {
375 // If we have no elements remaining in the array, we can free our buffer.
376 ShrinkCapacityToZero(aElemSize, aElemAlign);
377 return;
380 // Determine how many elements we need to move from the end of the array into
381 // the now-removed section. This will either be the number of elements which
382 // were removed (if there are more elements in the tail of the array), or the
383 // entire tail of the array, whichever is smaller.
384 size_type relocCount = std::min(aCount, mHdr->mLength - aStart);
385 if (relocCount == 0) {
386 return;
389 // Move the elements which are now stranded after the end of the array back
390 // into the now-vacated memory.
391 index_type sourceBytes = (oldLength - relocCount) * aElemSize;
392 index_type destBytes = aStart * aElemSize;
394 // Perform the final copy. This is guaranteed to be a non-overlapping copy
395 // as our source contains only still-valid entries, and the destination
396 // contains only invalid entries which need to be overwritten.
397 MOZ_ASSERT(sourceBytes >= destBytes,
398 "The source should be after the destination.");
399 MOZ_ASSERT(sourceBytes - destBytes >= relocCount * aElemSize,
400 "The range should be nonoverlapping");
402 char* baseAddr = reinterpret_cast<char*>(mHdr + 1);
403 RelocationStrategy::RelocateNonOverlappingRegion(
404 baseAddr + destBytes, baseAddr + sourceBytes, relocCount, aElemSize);
407 template <class Alloc, class RelocationStrategy>
408 template <typename ActualAlloc>
409 typename ActualAlloc::ResultTypeProxy
410 nsTArray_base<Alloc, RelocationStrategy>::InsertSlotsAt(index_type aIndex,
411 size_type aCount,
412 size_type aElemSize,
413 size_t aElemAlign) {
414 if (MOZ_UNLIKELY(aIndex > Length())) {
415 mozilla::detail::InvalidArrayIndex_CRASH(aIndex, Length());
418 if (!ActualAlloc::Successful(
419 this->ExtendCapacity<ActualAlloc>(Length(), aCount, aElemSize))) {
420 return ActualAlloc::FailureResult();
423 // Move the existing elements as needed. Note that this will
424 // change our mLength, so no need to call IncrementLength.
425 ShiftData<ActualAlloc>(aIndex, 0, aCount, aElemSize, aElemAlign);
427 return ActualAlloc::SuccessResult();
430 // nsTArray_base::IsAutoArrayRestorer is an RAII class which takes
431 // |nsTArray_base &array| in its constructor. When it's destructed, it ensures
432 // that
434 // * array.mIsAutoArray has the same value as it did when we started, and
435 // * if array has an auto buffer and mHdr would otherwise point to
436 // sEmptyTArrayHeader, array.mHdr points to array's auto buffer.
438 template <class Alloc, class RelocationStrategy>
439 nsTArray_base<Alloc, RelocationStrategy>::IsAutoArrayRestorer::
440 IsAutoArrayRestorer(nsTArray_base<Alloc, RelocationStrategy>& aArray,
441 size_t aElemAlign)
442 : mArray(aArray), mElemAlign(aElemAlign), mIsAuto(aArray.IsAutoArray()) {}
444 template <class Alloc, class RelocationStrategy>
445 nsTArray_base<Alloc,
446 RelocationStrategy>::IsAutoArrayRestorer::~IsAutoArrayRestorer() {
447 // Careful: We don't want to set mIsAutoArray = 1 on sEmptyTArrayHeader.
448 if (mIsAuto && mArray.HasEmptyHeader()) {
449 // Call GetAutoArrayBufferUnsafe() because GetAutoArrayBuffer() asserts
450 // that mHdr->mIsAutoArray is true, which surely isn't the case here.
451 mArray.mHdr = mArray.GetAutoArrayBufferUnsafe(mElemAlign);
452 mArray.mHdr->mLength = 0;
453 } else if (!mArray.HasEmptyHeader()) {
454 mArray.mHdr->mIsAutoArray = mIsAuto;
458 template <class Alloc, class RelocationStrategy>
459 template <typename ActualAlloc, class Allocator>
460 typename ActualAlloc::ResultTypeProxy
461 nsTArray_base<Alloc, RelocationStrategy>::SwapArrayElements(
462 nsTArray_base<Allocator, RelocationStrategy>& aOther, size_type aElemSize,
463 size_t aElemAlign) {
464 // EnsureNotUsingAutoArrayBuffer will set mHdr = sEmptyTArrayHeader even if we
465 // have an auto buffer. We need to point mHdr back to our auto buffer before
466 // we return, otherwise we'll forget that we have an auto buffer at all!
467 // IsAutoArrayRestorer takes care of this for us.
469 IsAutoArrayRestorer ourAutoRestorer(*this, aElemAlign);
470 typename nsTArray_base<Allocator, RelocationStrategy>::IsAutoArrayRestorer
471 otherAutoRestorer(aOther, aElemAlign);
473 // If neither array uses an auto buffer which is big enough to store the
474 // other array's elements, then ensure that both arrays use malloc'ed storage
475 // and swap their mHdr pointers.
476 if ((!UsesAutoArrayBuffer() || Capacity() < aOther.Length()) &&
477 (!aOther.UsesAutoArrayBuffer() || aOther.Capacity() < Length())) {
478 if (!EnsureNotUsingAutoArrayBuffer<ActualAlloc>(aElemSize) ||
479 !aOther.template EnsureNotUsingAutoArrayBuffer<ActualAlloc>(
480 aElemSize)) {
481 return ActualAlloc::FailureResult();
484 Header* temp = mHdr;
485 mHdr = aOther.mHdr;
486 aOther.mHdr = temp;
488 return ActualAlloc::SuccessResult();
491 // Swap the two arrays by copying, since at least one is using an auto
492 // buffer which is large enough to hold all of the aOther's elements. We'll
493 // copy the shorter array into temporary storage.
495 // (We could do better than this in some circumstances. Suppose we're
496 // swapping arrays X and Y. X has space for 2 elements in its auto buffer,
497 // but currently has length 4, so it's using malloc'ed storage. Y has length
498 // 2. When we swap X and Y, we don't need to use a temporary buffer; we can
499 // write Y straight into X's auto buffer, write X's malloc'ed buffer on top
500 // of Y, and then switch X to using its auto buffer.)
502 if (!ActualAlloc::Successful(
503 EnsureCapacity<ActualAlloc>(aOther.Length(), aElemSize)) ||
504 !Allocator::Successful(
505 aOther.template EnsureCapacity<Allocator>(Length(), aElemSize))) {
506 return ActualAlloc::FailureResult();
509 // The EnsureCapacity calls above shouldn't have caused *both* arrays to
510 // switch from their auto buffers to malloc'ed space.
511 MOZ_ASSERT(UsesAutoArrayBuffer() || aOther.UsesAutoArrayBuffer(),
512 "One of the arrays should be using its auto buffer.");
514 size_type smallerLength = XPCOM_MIN(Length(), aOther.Length());
515 size_type largerLength = XPCOM_MAX(Length(), aOther.Length());
516 void* smallerElements;
517 void* largerElements;
518 if (Length() <= aOther.Length()) {
519 smallerElements = Hdr() + 1;
520 largerElements = aOther.Hdr() + 1;
521 } else {
522 smallerElements = aOther.Hdr() + 1;
523 largerElements = Hdr() + 1;
526 // Allocate temporary storage for the smaller of the two arrays. We want to
527 // allocate this space on the stack, if it's not too large. Sounds like a
528 // job for AutoTArray! (One of the two arrays we're swapping is using an
529 // auto buffer, so we're likely not allocating a lot of space here. But one
530 // could, in theory, allocate a huge AutoTArray on the heap.)
531 AutoTArray<uint8_t, 64 * sizeof(void*)> temp;
532 if (!ActualAlloc::Successful(temp.template EnsureCapacity<ActualAlloc>(
533 smallerLength * aElemSize, sizeof(uint8_t)))) {
534 return ActualAlloc::FailureResult();
537 RelocationStrategy::RelocateNonOverlappingRegion(
538 temp.Elements(), smallerElements, smallerLength, aElemSize);
539 RelocationStrategy::RelocateNonOverlappingRegion(
540 smallerElements, largerElements, largerLength, aElemSize);
541 RelocationStrategy::RelocateNonOverlappingRegion(
542 largerElements, temp.Elements(), smallerLength, aElemSize);
544 // Swap the arrays' lengths.
545 MOZ_ASSERT((aOther.Length() == 0 || !HasEmptyHeader()) &&
546 (Length() == 0 || !aOther.HasEmptyHeader()),
547 "Don't set sEmptyTArrayHeader's length.");
548 size_type tempLength = Length();
550 // Avoid writing to EmptyHdr, since it can trigger false
551 // positives with TSan.
552 if (!HasEmptyHeader()) {
553 mHdr->mLength = aOther.Length();
555 if (!aOther.HasEmptyHeader()) {
556 aOther.mHdr->mLength = tempLength;
559 return ActualAlloc::SuccessResult();
562 template <class Alloc, class RelocationStrategy>
563 template <class Allocator>
564 void nsTArray_base<Alloc, RelocationStrategy>::MoveInit(
565 nsTArray_base<Allocator, RelocationStrategy>& aOther, size_type aElemSize,
566 size_t aElemAlign) {
567 // This method is similar to SwapArrayElements, but specialized for the case
568 // where the target array is empty with no allocated heap storage. It is
569 // provided and used to simplify template instantiation and enable better code
570 // generation.
572 MOZ_ASSERT(Length() == 0);
573 MOZ_ASSERT(Capacity() == 0 || (IsAutoArray() && UsesAutoArrayBuffer()));
575 // EnsureNotUsingAutoArrayBuffer will set mHdr = sEmptyTArrayHeader even if we
576 // have an auto buffer. We need to point mHdr back to our auto buffer before
577 // we return, otherwise we'll forget that we have an auto buffer at all!
578 // IsAutoArrayRestorer takes care of this for us.
580 IsAutoArrayRestorer ourAutoRestorer(*this, aElemAlign);
581 typename nsTArray_base<Allocator, RelocationStrategy>::IsAutoArrayRestorer
582 otherAutoRestorer(aOther, aElemAlign);
584 // If neither array uses an auto buffer which is big enough to store the
585 // other array's elements, then ensure that both arrays use malloc'ed storage
586 // and swap their mHdr pointers.
587 if ((!IsAutoArray() || Capacity() < aOther.Length()) &&
588 !aOther.UsesAutoArrayBuffer()) {
589 mHdr = aOther.mHdr;
591 aOther.mHdr = EmptyHdr();
593 return;
596 // Move the data by copying, since at least one has an auto
597 // buffer which is large enough to hold all of the aOther's elements.
599 EnsureCapacity<nsTArrayInfallibleAllocator>(aOther.Length(), aElemSize);
601 // The EnsureCapacity calls above shouldn't have caused *both* arrays to
602 // switch from their auto buffers to malloc'ed space.
603 MOZ_ASSERT(UsesAutoArrayBuffer() || aOther.UsesAutoArrayBuffer(),
604 "One of the arrays should be using its auto buffer.");
606 RelocationStrategy::RelocateNonOverlappingRegion(Hdr() + 1, aOther.Hdr() + 1,
607 aOther.Length(), aElemSize);
609 // Swap the arrays' lengths.
610 MOZ_ASSERT((aOther.Length() == 0 || !HasEmptyHeader()) &&
611 (Length() == 0 || !aOther.HasEmptyHeader()),
612 "Don't set sEmptyTArrayHeader's length.");
614 // Avoid writing to EmptyHdr, since it can trigger false
615 // positives with TSan.
616 if (!HasEmptyHeader()) {
617 mHdr->mLength = aOther.Length();
619 if (!aOther.HasEmptyHeader()) {
620 aOther.mHdr->mLength = 0;
624 template <class Alloc, class RelocationStrategy>
625 template <class Allocator>
626 void nsTArray_base<Alloc, RelocationStrategy>::MoveConstructNonAutoArray(
627 nsTArray_base<Allocator, RelocationStrategy>& aOther, size_type aElemSize,
628 size_t aElemAlign) {
629 // We know that we are not an (Copyable)AutoTArray and we know that we are
630 // empty, so don't use SwapArrayElements which doesn't know either of these
631 // facts and is very complex.
633 if (aOther.IsEmpty()) {
634 return;
637 // aOther might be an (Copyable)AutoTArray though, and it might use its inline
638 // buffer.
639 const bool otherUsesAutoArrayBuffer = aOther.UsesAutoArrayBuffer();
640 if (otherUsesAutoArrayBuffer) {
641 // Use nsTArrayInfallibleAllocator regardless of Alloc because this is
642 // called from a move constructor, which cannot report an error to the
643 // caller.
644 aOther.template EnsureNotUsingAutoArrayBuffer<nsTArrayInfallibleAllocator>(
645 aElemSize);
648 const bool otherIsAuto = otherUsesAutoArrayBuffer || aOther.IsAutoArray();
649 mHdr = aOther.mHdr;
650 // We might write to mHdr, so ensure it's not the static empty header. aOther
651 // shouldn't have been empty if we get here anyway.
652 MOZ_ASSERT(!HasEmptyHeader());
654 if (otherIsAuto) {
655 mHdr->mIsAutoArray = false;
656 aOther.mHdr = aOther.GetAutoArrayBufferUnsafe(aElemAlign);
657 aOther.mHdr->mLength = 0;
658 } else {
659 aOther.mHdr = aOther.EmptyHdr();
663 template <class Alloc, class RelocationStrategy>
664 template <typename ActualAlloc>
665 bool nsTArray_base<Alloc, RelocationStrategy>::EnsureNotUsingAutoArrayBuffer(
666 size_type aElemSize) {
667 if (UsesAutoArrayBuffer()) {
668 // If you call this on a 0-length array, we'll set that array's mHdr to
669 // sEmptyTArrayHeader, in flagrant violation of the AutoTArray invariants.
670 // It's up to you to set it back! (If you don't, the AutoTArray will
671 // forget that it has an auto buffer.)
672 if (Length() == 0) {
673 mHdr = EmptyHdr();
674 return true;
677 size_type size = sizeof(Header) + Length() * aElemSize;
679 Header* header = static_cast<Header*>(ActualAlloc::Malloc(size));
680 if (!header) {
681 return false;
684 RelocationStrategy::RelocateNonOverlappingRegionWithHeader(
685 header, mHdr, Length(), aElemSize);
686 header->mCapacity = Length();
687 mHdr = header;
690 return true;