xpcom/glue/nsTArray-inl.h

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial