Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* vim:set ts=2 sw=2 sts=2 et cindent: */ |
michael@0 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | #ifndef nsTArray_h__ |
michael@0 | 8 | # error "Don't include this file directly" |
michael@0 | 9 | #endif |
michael@0 | 10 | |
michael@0 | 11 | template<class Alloc, class Copy> |
michael@0 | 12 | nsTArray_base<Alloc, Copy>::nsTArray_base() |
michael@0 | 13 | : mHdr(EmptyHdr()) { |
michael@0 | 14 | MOZ_COUNT_CTOR(nsTArray_base); |
michael@0 | 15 | } |
michael@0 | 16 | |
michael@0 | 17 | template<class Alloc, class Copy> |
michael@0 | 18 | nsTArray_base<Alloc, Copy>::~nsTArray_base() { |
michael@0 | 19 | if (mHdr != EmptyHdr() && !UsesAutoArrayBuffer()) { |
michael@0 | 20 | Alloc::Free(mHdr); |
michael@0 | 21 | } |
michael@0 | 22 | MOZ_COUNT_DTOR(nsTArray_base); |
michael@0 | 23 | } |
michael@0 | 24 | |
michael@0 | 25 | template<class Alloc, class Copy> |
michael@0 | 26 | const nsTArrayHeader* nsTArray_base<Alloc, Copy>::GetAutoArrayBufferUnsafe(size_t elemAlign) const { |
michael@0 | 27 | // Assuming |this| points to an nsAutoArray, we want to get a pointer to |
michael@0 | 28 | // mAutoBuf. So just cast |this| to nsAutoArray* and read &mAutoBuf! |
michael@0 | 29 | |
michael@0 | 30 | const void* autoBuf = &reinterpret_cast<const nsAutoArrayBase<nsTArray<uint32_t>, 1>*>(this)->mAutoBuf; |
michael@0 | 31 | |
michael@0 | 32 | // If we're on a 32-bit system and elemAlign is 8, we need to adjust our |
michael@0 | 33 | // pointer to take into account the extra alignment in the auto array. |
michael@0 | 34 | |
michael@0 | 35 | static_assert(sizeof(void*) != 4 || |
michael@0 | 36 | (MOZ_ALIGNOF(mozilla::AlignedElem<8>) == 8 && |
michael@0 | 37 | sizeof(nsAutoTArray<mozilla::AlignedElem<8>, 1>) == |
michael@0 | 38 | sizeof(void*) + sizeof(nsTArrayHeader) + |
michael@0 | 39 | 4 + sizeof(mozilla::AlignedElem<8>)), |
michael@0 | 40 | "auto array padding wasn't what we expected"); |
michael@0 | 41 | |
michael@0 | 42 | // We don't support alignments greater than 8 bytes. |
michael@0 | 43 | NS_ABORT_IF_FALSE(elemAlign <= 4 || elemAlign == 8, "unsupported alignment."); |
michael@0 | 44 | if (sizeof(void*) == 4 && elemAlign == 8) { |
michael@0 | 45 | autoBuf = reinterpret_cast<const char*>(autoBuf) + 4; |
michael@0 | 46 | } |
michael@0 | 47 | |
michael@0 | 48 | return reinterpret_cast<const Header*>(autoBuf); |
michael@0 | 49 | } |
michael@0 | 50 | |
michael@0 | 51 | template<class Alloc, class Copy> |
michael@0 | 52 | bool nsTArray_base<Alloc, Copy>::UsesAutoArrayBuffer() const { |
michael@0 | 53 | if (!mHdr->mIsAutoArray) { |
michael@0 | 54 | return false; |
michael@0 | 55 | } |
michael@0 | 56 | |
michael@0 | 57 | // This is nuts. If we were sane, we'd pass elemAlign as a parameter to |
michael@0 | 58 | // this function. Unfortunately this function is called in nsTArray_base's |
michael@0 | 59 | // destructor, at which point we don't know elem_type's alignment. |
michael@0 | 60 | // |
michael@0 | 61 | // We'll fall on our face and return true when we should say false if |
michael@0 | 62 | // |
michael@0 | 63 | // * we're not using our auto buffer, |
michael@0 | 64 | // * elemAlign == 4, and |
michael@0 | 65 | // * mHdr == GetAutoArrayBuffer(8). |
michael@0 | 66 | // |
michael@0 | 67 | // This could happen if |*this| lives on the heap and malloc allocated our |
michael@0 | 68 | // buffer on the heap adjacent to |*this|. |
michael@0 | 69 | // |
michael@0 | 70 | // However, we can show that this can't happen. If |this| is an auto array |
michael@0 | 71 | // (as we ensured at the beginning of the method), GetAutoArrayBuffer(8) |
michael@0 | 72 | // always points to memory owned by |*this|, because (as we assert below) |
michael@0 | 73 | // |
michael@0 | 74 | // * GetAutoArrayBuffer(8) is at most 4 bytes past GetAutoArrayBuffer(4), and |
michael@0 | 75 | // * sizeof(nsTArrayHeader) > 4. |
michael@0 | 76 | // |
michael@0 | 77 | // Since nsAutoTArray always contains an nsTArrayHeader, |
michael@0 | 78 | // GetAutoArrayBuffer(8) will always point inside the auto array object, |
michael@0 | 79 | // even if it doesn't point at the beginning of the header. |
michael@0 | 80 | // |
michael@0 | 81 | // Note that this means that we can't store elements with alignment 16 in an |
michael@0 | 82 | // nsTArray, because GetAutoArrayBuffer(16) could lie outside the memory |
michael@0 | 83 | // owned by this nsAutoTArray. We statically assert that elem_type's |
michael@0 | 84 | // alignment is 8 bytes or less in nsAutoArrayBase. |
michael@0 | 85 | |
michael@0 | 86 | static_assert(sizeof(nsTArrayHeader) > 4, |
michael@0 | 87 | "see comment above"); |
michael@0 | 88 | |
michael@0 | 89 | #ifdef DEBUG |
michael@0 | 90 | ptrdiff_t diff = reinterpret_cast<const char*>(GetAutoArrayBuffer(8)) - |
michael@0 | 91 | reinterpret_cast<const char*>(GetAutoArrayBuffer(4)); |
michael@0 | 92 | NS_ABORT_IF_FALSE(diff >= 0 && diff <= 4, "GetAutoArrayBuffer doesn't do what we expect."); |
michael@0 | 93 | #endif |
michael@0 | 94 | |
michael@0 | 95 | return mHdr == GetAutoArrayBuffer(4) || mHdr == GetAutoArrayBuffer(8); |
michael@0 | 96 | } |
michael@0 | 97 | |
michael@0 | 98 | |
michael@0 | 99 | template<class Alloc, class Copy> |
michael@0 | 100 | typename Alloc::ResultTypeProxy |
michael@0 | 101 | nsTArray_base<Alloc, Copy>::EnsureCapacity(size_type capacity, size_type elemSize) { |
michael@0 | 102 | // This should be the most common case so test this first |
michael@0 | 103 | if (capacity <= mHdr->mCapacity) |
michael@0 | 104 | return Alloc::SuccessResult(); |
michael@0 | 105 | |
michael@0 | 106 | // If the requested memory allocation exceeds size_type(-1)/2, then |
michael@0 | 107 | // our doubling algorithm may not be able to allocate it. |
michael@0 | 108 | // Additionally we couldn't fit in the Header::mCapacity |
michael@0 | 109 | // member. Just bail out in cases like that. We don't want to be |
michael@0 | 110 | // allocating 2 GB+ arrays anyway. |
michael@0 | 111 | if ((uint64_t)capacity * elemSize > size_type(-1)/2) { |
michael@0 | 112 | Alloc::SizeTooBig((size_t)capacity * elemSize); |
michael@0 | 113 | return Alloc::FailureResult(); |
michael@0 | 114 | } |
michael@0 | 115 | |
michael@0 | 116 | if (mHdr == EmptyHdr()) { |
michael@0 | 117 | // Malloc() new data |
michael@0 | 118 | Header *header = static_cast<Header*> |
michael@0 | 119 | (Alloc::Malloc(sizeof(Header) + capacity * elemSize)); |
michael@0 | 120 | if (!header) |
michael@0 | 121 | return Alloc::FailureResult(); |
michael@0 | 122 | header->mLength = 0; |
michael@0 | 123 | header->mCapacity = capacity; |
michael@0 | 124 | header->mIsAutoArray = 0; |
michael@0 | 125 | mHdr = header; |
michael@0 | 126 | |
michael@0 | 127 | return Alloc::SuccessResult(); |
michael@0 | 128 | } |
michael@0 | 129 | |
michael@0 | 130 | // We increase our capacity so |capacity * elemSize + sizeof(Header)| is the |
michael@0 | 131 | // next power of two, if this value is less than pageSize bytes, or otherwise |
michael@0 | 132 | // so it's the next multiple of pageSize. |
michael@0 | 133 | const uint32_t pageSizeBytes = 12; |
michael@0 | 134 | const uint32_t pageSize = 1 << pageSizeBytes; |
michael@0 | 135 | |
michael@0 | 136 | uint32_t minBytes = capacity * elemSize + sizeof(Header); |
michael@0 | 137 | uint32_t bytesToAlloc; |
michael@0 | 138 | if (minBytes >= pageSize) { |
michael@0 | 139 | // Round up to the next multiple of pageSize. |
michael@0 | 140 | bytesToAlloc = pageSize * ((minBytes + pageSize - 1) / pageSize); |
michael@0 | 141 | } |
michael@0 | 142 | else { |
michael@0 | 143 | // Round up to the next power of two. See |
michael@0 | 144 | // http://graphics.stanford.edu/~seander/bithacks.html |
michael@0 | 145 | bytesToAlloc = minBytes - 1; |
michael@0 | 146 | bytesToAlloc |= bytesToAlloc >> 1; |
michael@0 | 147 | bytesToAlloc |= bytesToAlloc >> 2; |
michael@0 | 148 | bytesToAlloc |= bytesToAlloc >> 4; |
michael@0 | 149 | bytesToAlloc |= bytesToAlloc >> 8; |
michael@0 | 150 | bytesToAlloc |= bytesToAlloc >> 16; |
michael@0 | 151 | bytesToAlloc++; |
michael@0 | 152 | |
michael@0 | 153 | MOZ_ASSERT((bytesToAlloc & (bytesToAlloc - 1)) == 0, |
michael@0 | 154 | "nsTArray's allocation size should be a power of two!"); |
michael@0 | 155 | } |
michael@0 | 156 | |
michael@0 | 157 | Header *header; |
michael@0 | 158 | if (UsesAutoArrayBuffer() || !Copy::allowRealloc) { |
michael@0 | 159 | // Malloc() and copy |
michael@0 | 160 | header = static_cast<Header*>(Alloc::Malloc(bytesToAlloc)); |
michael@0 | 161 | if (!header) |
michael@0 | 162 | return Alloc::FailureResult(); |
michael@0 | 163 | |
michael@0 | 164 | Copy::CopyHeaderAndElements(header, mHdr, Length(), elemSize); |
michael@0 | 165 | |
michael@0 | 166 | if (!UsesAutoArrayBuffer()) |
michael@0 | 167 | Alloc::Free(mHdr); |
michael@0 | 168 | } else { |
michael@0 | 169 | // Realloc() existing data |
michael@0 | 170 | header = static_cast<Header*>(Alloc::Realloc(mHdr, bytesToAlloc)); |
michael@0 | 171 | if (!header) |
michael@0 | 172 | return Alloc::FailureResult(); |
michael@0 | 173 | } |
michael@0 | 174 | |
michael@0 | 175 | // How many elements can we fit in bytesToAlloc? |
michael@0 | 176 | uint32_t newCapacity = (bytesToAlloc - sizeof(Header)) / elemSize; |
michael@0 | 177 | MOZ_ASSERT(newCapacity >= capacity, "Didn't enlarge the array enough!"); |
michael@0 | 178 | header->mCapacity = newCapacity; |
michael@0 | 179 | |
michael@0 | 180 | mHdr = header; |
michael@0 | 181 | |
michael@0 | 182 | return Alloc::SuccessResult(); |
michael@0 | 183 | } |
michael@0 | 184 | |
michael@0 | 185 | template<class Alloc, class Copy> |
michael@0 | 186 | void |
michael@0 | 187 | nsTArray_base<Alloc, Copy>::ShrinkCapacity(size_type elemSize, size_t elemAlign) { |
michael@0 | 188 | if (mHdr == EmptyHdr() || UsesAutoArrayBuffer()) |
michael@0 | 189 | return; |
michael@0 | 190 | |
michael@0 | 191 | if (mHdr->mLength >= mHdr->mCapacity) // should never be greater than... |
michael@0 | 192 | return; |
michael@0 | 193 | |
michael@0 | 194 | size_type length = Length(); |
michael@0 | 195 | |
michael@0 | 196 | if (IsAutoArray() && GetAutoArrayBuffer(elemAlign)->mCapacity >= length) { |
michael@0 | 197 | Header* header = GetAutoArrayBuffer(elemAlign); |
michael@0 | 198 | |
michael@0 | 199 | // Copy data, but don't copy the header to avoid overwriting mCapacity |
michael@0 | 200 | header->mLength = length; |
michael@0 | 201 | Copy::CopyElements(header + 1, mHdr + 1, length, elemSize); |
michael@0 | 202 | |
michael@0 | 203 | Alloc::Free(mHdr); |
michael@0 | 204 | mHdr = header; |
michael@0 | 205 | return; |
michael@0 | 206 | } |
michael@0 | 207 | |
michael@0 | 208 | if (length == 0) { |
michael@0 | 209 | MOZ_ASSERT(!IsAutoArray(), "autoarray should have fit 0 elements"); |
michael@0 | 210 | Alloc::Free(mHdr); |
michael@0 | 211 | mHdr = EmptyHdr(); |
michael@0 | 212 | return; |
michael@0 | 213 | } |
michael@0 | 214 | |
michael@0 | 215 | size_type size = sizeof(Header) + length * elemSize; |
michael@0 | 216 | void *ptr = Alloc::Realloc(mHdr, size); |
michael@0 | 217 | if (!ptr) |
michael@0 | 218 | return; |
michael@0 | 219 | mHdr = static_cast<Header*>(ptr); |
michael@0 | 220 | mHdr->mCapacity = length; |
michael@0 | 221 | } |
michael@0 | 222 | |
michael@0 | 223 | template<class Alloc, class Copy> |
michael@0 | 224 | void |
michael@0 | 225 | nsTArray_base<Alloc, Copy>::ShiftData(index_type start, |
michael@0 | 226 | size_type oldLen, size_type newLen, |
michael@0 | 227 | size_type elemSize, size_t elemAlign) { |
michael@0 | 228 | if (oldLen == newLen) |
michael@0 | 229 | return; |
michael@0 | 230 | |
michael@0 | 231 | // Determine how many elements need to be shifted |
michael@0 | 232 | size_type num = mHdr->mLength - (start + oldLen); |
michael@0 | 233 | |
michael@0 | 234 | // Compute the resulting length of the array |
michael@0 | 235 | mHdr->mLength += newLen - oldLen; |
michael@0 | 236 | if (mHdr->mLength == 0) { |
michael@0 | 237 | ShrinkCapacity(elemSize, elemAlign); |
michael@0 | 238 | } else { |
michael@0 | 239 | // Maybe nothing needs to be shifted |
michael@0 | 240 | if (num == 0) |
michael@0 | 241 | return; |
michael@0 | 242 | // Perform shift (change units to bytes first) |
michael@0 | 243 | start *= elemSize; |
michael@0 | 244 | newLen *= elemSize; |
michael@0 | 245 | oldLen *= elemSize; |
michael@0 | 246 | char *base = reinterpret_cast<char*>(mHdr + 1) + start; |
michael@0 | 247 | Copy::MoveElements(base + newLen, base + oldLen, num, elemSize); |
michael@0 | 248 | } |
michael@0 | 249 | } |
michael@0 | 250 | |
michael@0 | 251 | template<class Alloc, class Copy> |
michael@0 | 252 | bool |
michael@0 | 253 | nsTArray_base<Alloc, Copy>::InsertSlotsAt(index_type index, size_type count, |
michael@0 | 254 | size_type elementSize, size_t elemAlign) { |
michael@0 | 255 | MOZ_ASSERT(index <= Length(), "Bogus insertion index"); |
michael@0 | 256 | size_type newLen = Length() + count; |
michael@0 | 257 | |
michael@0 | 258 | EnsureCapacity(newLen, elementSize); |
michael@0 | 259 | |
michael@0 | 260 | // Check for out of memory conditions |
michael@0 | 261 | if (Capacity() < newLen) |
michael@0 | 262 | return false; |
michael@0 | 263 | |
michael@0 | 264 | // Move the existing elements as needed. Note that this will |
michael@0 | 265 | // change our mLength, so no need to call IncrementLength. |
michael@0 | 266 | ShiftData(index, 0, count, elementSize, elemAlign); |
michael@0 | 267 | |
michael@0 | 268 | return true; |
michael@0 | 269 | } |
michael@0 | 270 | |
michael@0 | 271 | // nsTArray_base::IsAutoArrayRestorer is an RAII class which takes |
michael@0 | 272 | // |nsTArray_base &array| in its constructor. When it's destructed, it ensures |
michael@0 | 273 | // that |
michael@0 | 274 | // |
michael@0 | 275 | // * array.mIsAutoArray has the same value as it did when we started, and |
michael@0 | 276 | // * if array has an auto buffer and mHdr would otherwise point to sEmptyHdr, |
michael@0 | 277 | // array.mHdr points to array's auto buffer. |
michael@0 | 278 | |
michael@0 | 279 | template<class Alloc, class Copy> |
michael@0 | 280 | nsTArray_base<Alloc, Copy>::IsAutoArrayRestorer::IsAutoArrayRestorer( |
michael@0 | 281 | nsTArray_base<Alloc, Copy> &array, |
michael@0 | 282 | size_t elemAlign) |
michael@0 | 283 | : mArray(array), |
michael@0 | 284 | mElemAlign(elemAlign), |
michael@0 | 285 | mIsAuto(array.IsAutoArray()) |
michael@0 | 286 | { |
michael@0 | 287 | } |
michael@0 | 288 | |
michael@0 | 289 | template<class Alloc, class Copy> |
michael@0 | 290 | nsTArray_base<Alloc, Copy>::IsAutoArrayRestorer::~IsAutoArrayRestorer() { |
michael@0 | 291 | // Careful: We don't want to set mIsAutoArray = 1 on sEmptyHdr. |
michael@0 | 292 | if (mIsAuto && mArray.mHdr == mArray.EmptyHdr()) { |
michael@0 | 293 | // Call GetAutoArrayBufferUnsafe() because GetAutoArrayBuffer() asserts |
michael@0 | 294 | // that mHdr->mIsAutoArray is true, which surely isn't the case here. |
michael@0 | 295 | mArray.mHdr = mArray.GetAutoArrayBufferUnsafe(mElemAlign); |
michael@0 | 296 | mArray.mHdr->mLength = 0; |
michael@0 | 297 | } |
michael@0 | 298 | else if (mArray.mHdr != mArray.EmptyHdr()) { |
michael@0 | 299 | mArray.mHdr->mIsAutoArray = mIsAuto; |
michael@0 | 300 | } |
michael@0 | 301 | } |
michael@0 | 302 | |
michael@0 | 303 | template<class Alloc, class Copy> |
michael@0 | 304 | template<class Allocator> |
michael@0 | 305 | typename Alloc::ResultTypeProxy |
michael@0 | 306 | nsTArray_base<Alloc, Copy>::SwapArrayElements(nsTArray_base<Allocator, Copy>& other, |
michael@0 | 307 | size_type elemSize, size_t elemAlign) { |
michael@0 | 308 | |
michael@0 | 309 | // EnsureNotUsingAutoArrayBuffer will set mHdr = sEmptyHdr even if we have an |
michael@0 | 310 | // auto buffer. We need to point mHdr back to our auto buffer before we |
michael@0 | 311 | // return, otherwise we'll forget that we have an auto buffer at all! |
michael@0 | 312 | // IsAutoArrayRestorer takes care of this for us. |
michael@0 | 313 | |
michael@0 | 314 | IsAutoArrayRestorer ourAutoRestorer(*this, elemAlign); |
michael@0 | 315 | typename nsTArray_base<Allocator, Copy>::IsAutoArrayRestorer otherAutoRestorer(other, elemAlign); |
michael@0 | 316 | |
michael@0 | 317 | // If neither array uses an auto buffer which is big enough to store the |
michael@0 | 318 | // other array's elements, then ensure that both arrays use malloc'ed storage |
michael@0 | 319 | // and swap their mHdr pointers. |
michael@0 | 320 | if ((!UsesAutoArrayBuffer() || Capacity() < other.Length()) && |
michael@0 | 321 | (!other.UsesAutoArrayBuffer() || other.Capacity() < Length())) { |
michael@0 | 322 | |
michael@0 | 323 | if (!EnsureNotUsingAutoArrayBuffer(elemSize) || |
michael@0 | 324 | !other.EnsureNotUsingAutoArrayBuffer(elemSize)) { |
michael@0 | 325 | return Alloc::FailureResult(); |
michael@0 | 326 | } |
michael@0 | 327 | |
michael@0 | 328 | Header *temp = mHdr; |
michael@0 | 329 | mHdr = other.mHdr; |
michael@0 | 330 | other.mHdr = temp; |
michael@0 | 331 | |
michael@0 | 332 | return Alloc::SuccessResult(); |
michael@0 | 333 | } |
michael@0 | 334 | |
michael@0 | 335 | // Swap the two arrays by copying, since at least one is using an auto |
michael@0 | 336 | // buffer which is large enough to hold all of the other's elements. We'll |
michael@0 | 337 | // copy the shorter array into temporary storage. |
michael@0 | 338 | // |
michael@0 | 339 | // (We could do better than this in some circumstances. Suppose we're |
michael@0 | 340 | // swapping arrays X and Y. X has space for 2 elements in its auto buffer, |
michael@0 | 341 | // but currently has length 4, so it's using malloc'ed storage. Y has length |
michael@0 | 342 | // 2. When we swap X and Y, we don't need to use a temporary buffer; we can |
michael@0 | 343 | // write Y straight into X's auto buffer, write X's malloc'ed buffer on top |
michael@0 | 344 | // of Y, and then switch X to using its auto buffer.) |
michael@0 | 345 | |
michael@0 | 346 | if (!Alloc::Successful(EnsureCapacity(other.Length(), elemSize)) || |
michael@0 | 347 | !Allocator::Successful(other.EnsureCapacity(Length(), elemSize))) { |
michael@0 | 348 | return Alloc::FailureResult(); |
michael@0 | 349 | } |
michael@0 | 350 | |
michael@0 | 351 | // The EnsureCapacity calls above shouldn't have caused *both* arrays to |
michael@0 | 352 | // switch from their auto buffers to malloc'ed space. |
michael@0 | 353 | NS_ABORT_IF_FALSE(UsesAutoArrayBuffer() || |
michael@0 | 354 | other.UsesAutoArrayBuffer(), |
michael@0 | 355 | "One of the arrays should be using its auto buffer."); |
michael@0 | 356 | |
michael@0 | 357 | size_type smallerLength = XPCOM_MIN(Length(), other.Length()); |
michael@0 | 358 | size_type largerLength = XPCOM_MAX(Length(), other.Length()); |
michael@0 | 359 | void *smallerElements, *largerElements; |
michael@0 | 360 | if (Length() <= other.Length()) { |
michael@0 | 361 | smallerElements = Hdr() + 1; |
michael@0 | 362 | largerElements = other.Hdr() + 1; |
michael@0 | 363 | } |
michael@0 | 364 | else { |
michael@0 | 365 | smallerElements = other.Hdr() + 1; |
michael@0 | 366 | largerElements = Hdr() + 1; |
michael@0 | 367 | } |
michael@0 | 368 | |
michael@0 | 369 | // Allocate temporary storage for the smaller of the two arrays. We want to |
michael@0 | 370 | // allocate this space on the stack, if it's not too large. Sounds like a |
michael@0 | 371 | // job for AutoTArray! (One of the two arrays we're swapping is using an |
michael@0 | 372 | // auto buffer, so we're likely not allocating a lot of space here. But one |
michael@0 | 373 | // could, in theory, allocate a huge AutoTArray on the heap.) |
michael@0 | 374 | nsAutoArrayBase<nsTArray_Impl<uint8_t, Alloc>, 64> temp; |
michael@0 | 375 | if (!Alloc::Successful(temp.EnsureCapacity(smallerLength, elemSize))) { |
michael@0 | 376 | return Alloc::FailureResult(); |
michael@0 | 377 | } |
michael@0 | 378 | |
michael@0 | 379 | Copy::CopyElements(temp.Elements(), smallerElements, smallerLength, elemSize); |
michael@0 | 380 | Copy::CopyElements(smallerElements, largerElements, largerLength, elemSize); |
michael@0 | 381 | Copy::CopyElements(largerElements, temp.Elements(), smallerLength, elemSize); |
michael@0 | 382 | |
michael@0 | 383 | // Swap the arrays' lengths. |
michael@0 | 384 | NS_ABORT_IF_FALSE((other.Length() == 0 || mHdr != EmptyHdr()) && |
michael@0 | 385 | (Length() == 0 || other.mHdr != EmptyHdr()), |
michael@0 | 386 | "Don't set sEmptyHdr's length."); |
michael@0 | 387 | size_type tempLength = Length(); |
michael@0 | 388 | mHdr->mLength = other.Length(); |
michael@0 | 389 | other.mHdr->mLength = tempLength; |
michael@0 | 390 | |
michael@0 | 391 | return Alloc::SuccessResult(); |
michael@0 | 392 | } |
michael@0 | 393 | |
michael@0 | 394 | template<class Alloc, class Copy> |
michael@0 | 395 | bool |
michael@0 | 396 | nsTArray_base<Alloc, Copy>::EnsureNotUsingAutoArrayBuffer(size_type elemSize) { |
michael@0 | 397 | if (UsesAutoArrayBuffer()) { |
michael@0 | 398 | |
michael@0 | 399 | // If you call this on a 0-length array, we'll set that array's mHdr to |
michael@0 | 400 | // sEmptyHdr, in flagrant violation of the nsAutoTArray invariants. It's |
michael@0 | 401 | // up to you to set it back! (If you don't, the nsAutoTArray will forget |
michael@0 | 402 | // that it has an auto buffer.) |
michael@0 | 403 | if (Length() == 0) { |
michael@0 | 404 | mHdr = EmptyHdr(); |
michael@0 | 405 | return true; |
michael@0 | 406 | } |
michael@0 | 407 | |
michael@0 | 408 | size_type size = sizeof(Header) + Length() * elemSize; |
michael@0 | 409 | |
michael@0 | 410 | Header* header = static_cast<Header*>(Alloc::Malloc(size)); |
michael@0 | 411 | if (!header) |
michael@0 | 412 | return false; |
michael@0 | 413 | |
michael@0 | 414 | Copy::CopyHeaderAndElements(header, mHdr, Length(), elemSize); |
michael@0 | 415 | header->mCapacity = Length(); |
michael@0 | 416 | mHdr = header; |
michael@0 | 417 | } |
michael@0 | 418 | |
michael@0 | 419 | return true; |
michael@0 | 420 | } |