xpcom/glue/nsVoidArray.cpp

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; c-file-offsets: ((substatement-open . 0)) -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "mozilla/MathAlgorithms.h"
     7 #include "mozilla/MemoryReporting.h"
     8 #include <stdlib.h>
    10 #include "nsVoidArray.h"
    11 #include "nsQuickSort.h"
    12 #include "nsISupportsImpl.h" // for nsTraceRefcnt
    13 #include "nsAlgorithm.h"
    15 /**
    16  * Grow the array by at least this many elements at a time.
    17  */
    18 static const int32_t kMinGrowArrayBy = 8;
    19 static const int32_t kMaxGrowArrayBy = 1024;
    21 /**
    22  * This is the threshold (in bytes) of the mImpl struct, past which
    23  * we'll force the array to grow geometrically
    24  */
    25 static const int32_t kLinearThreshold = 24 * sizeof(void *);
    27 /**
    28  * Compute the number of bytes requires for the mImpl struct that will
    29  * hold |n| elements.
    30  */
    31 #define SIZEOF_IMPL(n_) (sizeof(Impl) + sizeof(void *) * ((n_) - 1))
    33 /**
    34  * Compute the number of elements that an mImpl struct of |n| bytes
    35  * will hold.
    36  */
    37 #define CAPACITYOF_IMPL(n_) ((((n_) - sizeof(Impl)) / sizeof(void *)) + 1)
    39 #if DEBUG_VOIDARRAY
    40 #define MAXVOID 10
    42 class VoidStats {
    43 public:
    44   VoidStats();
    45   ~VoidStats();
    47 };
    49 static int sizesUsed; // number of the elements of the arrays used
    50 static int sizesAlloced[MAXVOID]; // sizes of the allocations.  sorted
    51 static int NumberOfSize[MAXVOID]; // number of this allocation size (1 per array)
    52 static int AllocedOfSize[MAXVOID]; // number of this allocation size (each size for array used)
    53 static int MaxAuto[MAXVOID];      // AutoArrays that maxed out at this size
    54 static int GrowInPlace[MAXVOID];  // arrays this size that grew in-place via realloc
    56 // these are per-allocation  
    57 static int MaxElements[2000];     // # of arrays that maxed out at each size.
    59 // statistics macros
    60 #define ADD_TO_STATS(x,size) do {int i; for (i = 0; i < sizesUsed; i++) \
    61                                   { \
    62                                     if (sizesAlloced[i] == (int)(size)) \
    63                                     { ((x)[i])++; break; } \
    64                                   } \
    65                                   if (i >= sizesUsed && sizesUsed < MAXVOID) \
    66                                   { sizesAlloced[sizesUsed] = (size); \
    67                                     ((x)[sizesUsed++])++; break; \
    68                                   } \
    69                                 } while (0)
    71 #define SUB_FROM_STATS(x,size) do {int i; for (i = 0; i < sizesUsed; i++) \
    72                                     { \
    73                                       if (sizesAlloced[i] == (int)(size)) \
    74                                       { ((x)[i])--; break; } \
    75                                     } \
    76                                   } while (0)
    79 VoidStats::VoidStats()
    80 {
    81   sizesUsed = 1;
    82   sizesAlloced[0] = 0;
    83 }
    85 VoidStats::~VoidStats()
    86 {
    87   int i;
    88   for (i = 0; i < sizesUsed; i++)
    89   {
    90     printf("Size %d:\n",sizesAlloced[i]);
    91     printf("\tNumber of VoidArrays this size (max):     %d\n",NumberOfSize[i]-MaxAuto[i]);
    92     printf("\tNumber of AutoVoidArrays this size (max): %d\n",MaxAuto[i]);
    93     printf("\tNumber of allocations this size (total):  %d\n",AllocedOfSize[i]);
    94     printf("\tNumber of GrowsInPlace this size (total): %d\n",GrowInPlace[i]);
    95   }
    96   printf("Max Size of VoidArray:\n");
    97   for (i = 0; i < (int)(sizeof(MaxElements)/sizeof(MaxElements[0])); i++)
    98   {
    99     if (MaxElements[i])
   100       printf("\t%d: %d\n",i,MaxElements[i]);
   101   }
   102 }
   104 // Just so constructor/destructor's get called
   105 VoidStats gVoidStats;
   106 #endif
   108 void
   109 nsVoidArray::SetArray(Impl *newImpl, int32_t aSize, int32_t aCount)
   110 {
   111   // old mImpl has been realloced and so we don't free/delete it
   112   NS_PRECONDITION(newImpl, "can't set size");
   113   mImpl = newImpl;
   114   mImpl->mCount = aCount;
   115   mImpl->mSize = aSize;
   116 }
   118 // This does all allocation/reallocation of the array.
   119 // It also will compact down to N - good for things that might grow a lot
   120 // at times,  but usually are smaller, like JS deferred GC releases.
   121 bool nsVoidArray::SizeTo(int32_t aSize)
   122 {
   123   uint32_t oldsize = GetArraySize();
   125   if (aSize == (int32_t) oldsize)
   126     return true; // no change
   128   if (aSize <= 0)
   129   {
   130     // free the array if allocated
   131     if (mImpl)
   132     {
   133       free(reinterpret_cast<char *>(mImpl));
   134       mImpl = nullptr;
   135     }
   136     return true;
   137   }
   139   if (mImpl)
   140   {
   141     // We currently own an array impl. Resize it appropriately.
   142     if (aSize < mImpl->mCount)
   143     {
   144       // XXX Note: we could also just resize to mCount
   145       return true;  // can't make it that small, ignore request
   146     }
   148     char* bytes = (char *) realloc(mImpl,SIZEOF_IMPL(aSize));
   149     Impl* newImpl = reinterpret_cast<Impl*>(bytes);
   150     if (!newImpl)
   151       return false;
   153 #if DEBUG_VOIDARRAY
   154     if (mImpl == newImpl)
   155       ADD_TO_STATS(GrowInPlace,oldsize);
   156     ADD_TO_STATS(AllocedOfSize,SIZEOF_IMPL(aSize));
   157     if (aSize > mMaxSize)
   158     {
   159       ADD_TO_STATS(NumberOfSize,SIZEOF_IMPL(aSize));
   160       if (oldsize)
   161         SUB_FROM_STATS(NumberOfSize,oldsize);
   162       mMaxSize = aSize;
   163       if (mIsAuto)
   164       {
   165         ADD_TO_STATS(MaxAuto,SIZEOF_IMPL(aSize));
   166         SUB_FROM_STATS(MaxAuto,oldsize);
   167       }
   168     }
   169 #endif
   170     SetArray(newImpl, aSize, newImpl->mCount);
   171     return true;
   172   }
   174   if ((uint32_t) aSize < oldsize) {
   175     // No point in allocating if it won't free the current Impl anyway.
   176     return true;
   177   }
   179   // just allocate an array
   180   // allocate the exact size requested
   181   char* bytes = (char *) malloc(SIZEOF_IMPL(aSize));
   182   Impl* newImpl = reinterpret_cast<Impl*>(bytes);
   183   if (!newImpl)
   184     return false;
   186 #if DEBUG_VOIDARRAY
   187   ADD_TO_STATS(AllocedOfSize,SIZEOF_IMPL(aSize));
   188   if (aSize > mMaxSize)
   189   {
   190     ADD_TO_STATS(NumberOfSize,SIZEOF_IMPL(aSize));
   191     if (oldsize && !mImpl)
   192       SUB_FROM_STATS(NumberOfSize,oldsize);
   193     mMaxSize = aSize;
   194   }
   195 #endif
   196   if (mImpl)
   197   {
   198 #if DEBUG_VOIDARRAY
   199     ADD_TO_STATS(MaxAuto,SIZEOF_IMPL(aSize));
   200     SUB_FROM_STATS(MaxAuto,0);
   201     SUB_FROM_STATS(NumberOfSize,0);
   202     mIsAuto = true;
   203 #endif
   204     // We must be growing an nsAutoVoidArray - copy since we didn't
   205     // realloc.
   206     memcpy(newImpl->mArray, mImpl->mArray,
   207                   mImpl->mCount * sizeof(mImpl->mArray[0]));
   208   }
   210   SetArray(newImpl, aSize, mImpl ? mImpl->mCount : 0);
   211   // no memset; handled later in ReplaceElementAt if needed
   212   return true;
   213 }
   215 bool nsVoidArray::GrowArrayBy(int32_t aGrowBy)
   216 {
   217   // We have to grow the array. Grow by kMinGrowArrayBy slots if we're
   218   // smaller than kLinearThreshold bytes, or a power of two if we're
   219   // larger.  This is much more efficient with most memory allocators,
   220   // especially if it's very large, or of the allocator is binned.
   221   if (aGrowBy < kMinGrowArrayBy)
   222     aGrowBy = kMinGrowArrayBy;
   224   uint32_t newCapacity = GetArraySize() + aGrowBy;  // Minimum increase
   225   uint32_t newSize = SIZEOF_IMPL(newCapacity);
   227   if (newSize >= (uint32_t) kLinearThreshold)
   228   {
   229     // newCount includes enough space for at least kMinGrowArrayBy new
   230     // slots. Select the next power-of-two size in bytes above or
   231     // equal to that.
   232     // Also, limit the increase in size to about a VM page or two.
   233     if (GetArraySize() >= kMaxGrowArrayBy)
   234     {
   235       newCapacity = GetArraySize() + XPCOM_MAX(kMaxGrowArrayBy,aGrowBy);
   236       newSize = SIZEOF_IMPL(newCapacity);
   237     }
   238     else
   239     {
   240       newSize = mozilla::CeilingLog2(newSize);
   241       newCapacity = CAPACITYOF_IMPL(1u << newSize);
   242     }
   243   }
   244   // frees old mImpl IF this succeeds
   245   if (!SizeTo(newCapacity))
   246     return false;
   248   return true;
   249 }
   251 nsVoidArray::nsVoidArray()
   252   : mImpl(nullptr)
   253 {
   254   MOZ_COUNT_CTOR(nsVoidArray);
   255 #if DEBUG_VOIDARRAY
   256   mMaxCount = 0;
   257   mMaxSize = 0;
   258   mIsAuto = false;
   259   ADD_TO_STATS(NumberOfSize,0);
   260   MaxElements[0]++;
   261 #endif
   262 }
   264 nsVoidArray::nsVoidArray(int32_t aCount)
   265   : mImpl(nullptr)
   266 {
   267   MOZ_COUNT_CTOR(nsVoidArray);
   268 #if DEBUG_VOIDARRAY
   269   mMaxCount = 0;
   270   mMaxSize = 0;
   271   mIsAuto = false;
   272   MaxElements[0]++;
   273 #endif
   274   SizeTo(aCount);
   275 }
   277 nsVoidArray& nsVoidArray::operator=(const nsVoidArray& other)
   278 {
   279   int32_t otherCount = other.Count();
   280   int32_t maxCount = GetArraySize();
   281   if (otherCount)
   282   {
   283     if (otherCount > maxCount)
   284     {
   285       // frees old mImpl IF this succeeds
   286       if (!GrowArrayBy(otherCount-maxCount))
   287         return *this;      // XXX The allocation failed - don't do anything
   289       memcpy(mImpl->mArray, other.mImpl->mArray, otherCount * sizeof(mImpl->mArray[0]));
   290       mImpl->mCount = otherCount;
   291     }
   292     else
   293     {
   294       // the old array can hold the new array
   295       memcpy(mImpl->mArray, other.mImpl->mArray, otherCount * sizeof(mImpl->mArray[0]));
   296       mImpl->mCount = otherCount;
   297       // if it shrank a lot, compact it anyways
   298       if ((otherCount*2) < maxCount && maxCount > 100)
   299       {
   300         Compact();  // shrank by at least 50 entries
   301       }
   302     }
   303 #if DEBUG_VOIDARRAY
   304      if (mImpl->mCount > mMaxCount &&
   305          mImpl->mCount < (int32_t)(sizeof(MaxElements)/sizeof(MaxElements[0])))
   306      {
   307        MaxElements[mImpl->mCount]++;
   308        MaxElements[mMaxCount]--;
   309        mMaxCount = mImpl->mCount;
   310      }
   311 #endif
   312   }
   313   else
   314   {
   315     // Why do we drop the buffer here when we don't in Clear()?
   316     SizeTo(0);
   317   }
   319   return *this;
   320 }
   322 nsVoidArray::~nsVoidArray()
   323 {
   324   MOZ_COUNT_DTOR(nsVoidArray);
   325   if (mImpl)
   326     free(reinterpret_cast<char*>(mImpl));
   327 }
   329 bool nsVoidArray::SetCount(int32_t aNewCount)
   330 {
   331   NS_ASSERTION(aNewCount >= 0,"SetCount(negative index)");
   332   if (aNewCount < 0)
   333     return false;
   335   if (aNewCount == 0)
   336   {
   337     Clear();
   338     return true;
   339   }
   341   if (uint32_t(aNewCount) > uint32_t(GetArraySize()))
   342   {
   343     int32_t oldCount = Count();
   344     int32_t growDelta = aNewCount - oldCount;
   346     // frees old mImpl IF this succeeds
   347     if (!GrowArrayBy(growDelta))
   348       return false;
   349   }
   351   if (aNewCount > mImpl->mCount)
   352   {
   353     // Make sure that new entries added to the array by this
   354     // SetCount are cleared to 0.  Some users of this assume that.
   355     // This code means we don't have to memset when we allocate an array.
   356     memset(&mImpl->mArray[mImpl->mCount], 0,
   357            (aNewCount - mImpl->mCount) * sizeof(mImpl->mArray[0]));
   358   }
   360   mImpl->mCount = aNewCount;
   362 #if DEBUG_VOIDARRAY
   363   if (mImpl->mCount > mMaxCount &&
   364       mImpl->mCount < (int32_t)(sizeof(MaxElements)/sizeof(MaxElements[0])))
   365   {
   366     MaxElements[mImpl->mCount]++;
   367     MaxElements[mMaxCount]--;
   368     mMaxCount = mImpl->mCount;
   369   }
   370 #endif
   372   return true;
   373 }
   375 int32_t nsVoidArray::IndexOf(void* aPossibleElement) const
   376 {
   377   if (mImpl)
   378   {
   379     void** ap = mImpl->mArray;
   380     void** end = ap + mImpl->mCount;
   381     while (ap < end)
   382     {
   383       if (*ap == aPossibleElement)
   384       {
   385         return ap - mImpl->mArray;
   386       }
   387       ap++;
   388     }
   389   }
   390   return -1;
   391 }
   393 bool nsVoidArray::InsertElementAt(void* aElement, int32_t aIndex)
   394 {
   395   int32_t oldCount = Count();
   396   NS_ASSERTION(aIndex >= 0,"InsertElementAt(negative index)");
   397   if (uint32_t(aIndex) > uint32_t(oldCount))
   398   {
   399     // An invalid index causes the insertion to fail
   400     // Invalid indexes are ones that add more than one entry to the
   401     // array (i.e., they can append).
   402     return false;
   403   }
   405   if (oldCount >= GetArraySize())
   406   {
   407     if (!GrowArrayBy(1))
   408       return false;
   409   }
   410   // else the array is already large enough
   412   int32_t slide = oldCount - aIndex;
   413   if (0 != slide)
   414   {
   415     // Slide data over to make room for the insertion
   416     memmove(mImpl->mArray + aIndex + 1, mImpl->mArray + aIndex,
   417             slide * sizeof(mImpl->mArray[0]));
   418   }
   420   mImpl->mArray[aIndex] = aElement;
   421   mImpl->mCount++;
   423 #if DEBUG_VOIDARRAY
   424   if (mImpl->mCount > mMaxCount &&
   425       mImpl->mCount < (int32_t)(sizeof(MaxElements)/sizeof(MaxElements[0])))
   426   {
   427     MaxElements[mImpl->mCount]++;
   428     MaxElements[mMaxCount]--;
   429     mMaxCount = mImpl->mCount;
   430   }
   431 #endif
   433   return true;
   434 }
   436 bool nsVoidArray::InsertElementsAt(const nsVoidArray& other, int32_t aIndex)
   437 {
   438   int32_t oldCount = Count();
   439   int32_t otherCount = other.Count();
   441   NS_ASSERTION(aIndex >= 0,"InsertElementsAt(negative index)");
   442   if (uint32_t(aIndex) > uint32_t(oldCount))
   443   {
   444     // An invalid index causes the insertion to fail
   445     // Invalid indexes are ones that are more than one entry past the end of
   446     // the array (i.e., they can append).
   447     return false;
   448   }
   450   if (oldCount + otherCount > GetArraySize())
   451   {
   452     if (!GrowArrayBy(otherCount))
   453       return false;;
   454   }
   455   // else the array is already large enough
   457   int32_t slide = oldCount - aIndex;
   458   if (0 != slide)
   459   {
   460     // Slide data over to make room for the insertion
   461     memmove(mImpl->mArray + aIndex + otherCount, mImpl->mArray + aIndex,
   462             slide * sizeof(mImpl->mArray[0]));
   463   }
   465   for (int32_t i = 0; i < otherCount; i++)
   466   {
   467     // copy all the elements (destroys aIndex)
   468     mImpl->mArray[aIndex++] = other.mImpl->mArray[i];
   469     mImpl->mCount++;
   470   }
   472 #if DEBUG_VOIDARRAY
   473   if (mImpl->mCount > mMaxCount &&
   474       mImpl->mCount < (int32_t)(sizeof(MaxElements)/sizeof(MaxElements[0])))
   475   {
   476     MaxElements[mImpl->mCount]++;
   477     MaxElements[mMaxCount]--;
   478     mMaxCount = mImpl->mCount;
   479   }
   480 #endif
   482   return true;
   483 }
   485 bool nsVoidArray::ReplaceElementAt(void* aElement, int32_t aIndex)
   486 {
   487   NS_ASSERTION(aIndex >= 0,"ReplaceElementAt(negative index)");
   488   if (aIndex < 0)
   489     return false;
   491   // Unlike InsertElementAt, ReplaceElementAt can implicitly add more
   492   // than just the one element to the array.
   493   if (uint32_t(aIndex) >= uint32_t(GetArraySize()))
   494   {
   495     int32_t oldCount = Count();
   496     int32_t requestedCount = aIndex + 1;
   497     int32_t growDelta = requestedCount - oldCount;
   499     // frees old mImpl IF this succeeds
   500     if (!GrowArrayBy(growDelta))
   501       return false;
   502   }
   504   mImpl->mArray[aIndex] = aElement;
   505   if (aIndex >= mImpl->mCount)
   506   {
   507     // Make sure that any entries implicitly added to the array by this
   508     // ReplaceElementAt are cleared to 0.  Some users of this assume that.
   509     // This code means we don't have to memset when we allocate an array.
   510     if (aIndex > mImpl->mCount) // note: not >=
   511     {
   512       // For example, if mCount is 2, and we do a ReplaceElementAt for
   513       // element[5], then we need to set three entries ([2], [3], and [4])
   514       // to 0.
   515       memset(&mImpl->mArray[mImpl->mCount], 0,
   516              (aIndex - mImpl->mCount) * sizeof(mImpl->mArray[0]));
   517     }
   519      mImpl->mCount = aIndex + 1;
   521 #if DEBUG_VOIDARRAY
   522      if (mImpl->mCount > mMaxCount &&
   523          mImpl->mCount < (int32_t)(sizeof(MaxElements)/sizeof(MaxElements[0])))
   524      {
   525        MaxElements[mImpl->mCount]++;
   526        MaxElements[mMaxCount]--;
   527        mMaxCount = mImpl->mCount;
   528      }
   529 #endif
   530   }
   532   return true;
   533 }
   535 // useful for doing LRU arrays
   536 bool nsVoidArray::MoveElement(int32_t aFrom, int32_t aTo)
   537 {
   538   void *tempElement;
   540   if (aTo == aFrom)
   541     return true;
   543   NS_ASSERTION(aTo >= 0 && aFrom >= 0,"MoveElement(negative index)");
   544   if (aTo >= Count() || aFrom >= Count())
   545   {
   546     // can't extend the array when moving an element.  Also catches mImpl = null
   547     return false;
   548   }
   549   tempElement = mImpl->mArray[aFrom];
   551   if (aTo < aFrom)
   552   {
   553     // Moving one element closer to the head; the elements inbetween move down
   554     memmove(mImpl->mArray + aTo + 1, mImpl->mArray + aTo,
   555             (aFrom-aTo) * sizeof(mImpl->mArray[0]));
   556     mImpl->mArray[aTo] = tempElement;
   557   }
   558   else // already handled aFrom == aTo
   559   {
   560     // Moving one element closer to the tail; the elements inbetween move up
   561     memmove(mImpl->mArray + aFrom, mImpl->mArray + aFrom + 1,
   562             (aTo-aFrom) * sizeof(mImpl->mArray[0]));
   563     mImpl->mArray[aTo] = tempElement;
   564   }
   566   return true;
   567 }
   569 void nsVoidArray::RemoveElementsAt(int32_t aIndex, int32_t aCount)
   570 {
   571   int32_t oldCount = Count();
   572   NS_ASSERTION(aIndex >= 0,"RemoveElementsAt(negative index)");
   573   if (uint32_t(aIndex) >= uint32_t(oldCount))
   574   {
   575     return;
   576   }
   577   // Limit to available entries starting at aIndex
   578   if (aCount + aIndex > oldCount)
   579     aCount = oldCount - aIndex;
   581   // We don't need to move any elements if we're removing the
   582   // last element in the array
   583   if (aIndex < (oldCount - aCount))
   584   {
   585     memmove(mImpl->mArray + aIndex, mImpl->mArray + aIndex + aCount,
   586             (oldCount - (aIndex + aCount)) * sizeof(mImpl->mArray[0]));
   587   }
   589   mImpl->mCount -= aCount;
   590   return;
   591 }
   593 bool nsVoidArray::RemoveElement(void* aElement)
   594 {
   595   int32_t theIndex = IndexOf(aElement);
   596   if (theIndex != -1)
   597   {
   598     RemoveElementAt(theIndex);
   599     return true;
   600   }
   602   return false;
   603 }
   605 void nsVoidArray::Clear()
   606 {
   607   if (mImpl)
   608   {
   609     mImpl->mCount = 0;
   610   }
   611 }
   613 void nsVoidArray::Compact()
   614 {
   615   if (mImpl)
   616   {
   617     // XXX NOTE: this is quite inefficient in many cases if we're only
   618     // compacting by a little, but some callers care more about memory use.
   619     int32_t count = Count();
   620     if (GetArraySize() > count)
   621     {
   622       SizeTo(Count());
   623     }
   624   }
   625 }
   627 // Needed because we want to pass the pointer to the item in the array
   628 // to the comparator function, not a pointer to the pointer in the array.
   629 struct VoidArrayComparatorContext {
   630   nsVoidArrayComparatorFunc mComparatorFunc;
   631   void* mData;
   632 };
   634 static int
   635 VoidArrayComparator(const void* aElement1, const void* aElement2, void* aData)
   636 {
   637   VoidArrayComparatorContext* ctx = static_cast<VoidArrayComparatorContext*>(aData);
   638   return (*ctx->mComparatorFunc)(*static_cast<void* const*>(aElement1),
   639                                  *static_cast<void* const*>(aElement2),
   640                                   ctx->mData);
   641 }
   643 void nsVoidArray::Sort(nsVoidArrayComparatorFunc aFunc, void* aData)
   644 {
   645   if (mImpl && mImpl->mCount > 1)
   646   {
   647     VoidArrayComparatorContext ctx = {aFunc, aData};
   648     NS_QuickSort(mImpl->mArray, mImpl->mCount, sizeof(mImpl->mArray[0]),
   649                  VoidArrayComparator, &ctx);
   650   }
   651 }
   653 bool nsVoidArray::EnumerateForwards(nsVoidArrayEnumFunc aFunc, void* aData)
   654 {
   655   int32_t index = -1;
   656   bool    running = true;
   658   if (mImpl) {
   659     while (running && (++index < mImpl->mCount)) {
   660       running = (*aFunc)(mImpl->mArray[index], aData);
   661     }
   662   }
   663   return running;
   664 }
   666 bool nsVoidArray::EnumerateForwards(nsVoidArrayEnumFuncConst aFunc,
   667                                     void* aData) const
   668 {
   669   int32_t index = -1;
   670   bool    running = true;
   672   if (mImpl) {
   673     while (running && (++index < mImpl->mCount)) {
   674       running = (*aFunc)(mImpl->mArray[index], aData);
   675     }
   676   }
   677   return running;
   678 }
   680 bool nsVoidArray::EnumerateBackwards(nsVoidArrayEnumFunc aFunc, void* aData)
   681 {
   682   bool    running = true;
   684   if (mImpl)
   685   {
   686     int32_t index = Count();
   687     while (running && (0 <= --index))
   688     {
   689       running = (*aFunc)(mImpl->mArray[index], aData);
   690     }
   691   }
   692   return running;
   693 }
   695 struct SizeOfElementIncludingThisData
   696 {
   697   size_t mSize;
   698   nsVoidArraySizeOfElementIncludingThisFunc mSizeOfElementIncludingThis;
   699   mozilla::MallocSizeOf mMallocSizeOf;
   700   void *mData;      // the arg passed by the user
   701 };
   703 static bool
   704 SizeOfElementIncludingThisEnumerator(const void *aElement, void *aData)
   705 {
   706   SizeOfElementIncludingThisData *d = (SizeOfElementIncludingThisData *)aData;
   707   d->mSize += d->mSizeOfElementIncludingThis(aElement, d->mMallocSizeOf, d->mData);
   708   return true;
   709 }
   711 size_t
   712 nsVoidArray::SizeOfExcludingThis(
   713   nsVoidArraySizeOfElementIncludingThisFunc aSizeOfElementIncludingThis,
   714   mozilla::MallocSizeOf aMallocSizeOf, void* aData) const
   715 {
   716   size_t n = 0;
   717   // Measure the element storage.
   718   if (mImpl) {
   719     n += aMallocSizeOf(mImpl);
   720   }
   721   // Measure things pointed to by the elements.
   722   if (aSizeOfElementIncludingThis) { 
   723     SizeOfElementIncludingThisData data2 =
   724       { 0, aSizeOfElementIncludingThis, aMallocSizeOf, aData };
   725     EnumerateForwards(SizeOfElementIncludingThisEnumerator, &data2);
   726     n += data2.mSize;
   727   }
   728   return n;
   729 }
   731 //----------------------------------------------------------------------
   732 // NOTE: nsSmallVoidArray elements MUST all have the low bit as 0.
   733 // This means that normally it's only used for pointers, and in particular
   734 // structures or objects.
   735 nsSmallVoidArray::~nsSmallVoidArray()
   736 {
   737   if (HasSingle())
   738   {
   739     // Have to null out mImpl before the nsVoidArray dtor runs.
   740     mImpl = nullptr;
   741   }
   742 }
   744 nsSmallVoidArray& 
   745 nsSmallVoidArray::operator=(nsSmallVoidArray& other)
   746 {
   747   int32_t count = other.Count();
   748   switch (count) {
   749     case 0:
   750       Clear();
   751       break;
   752     case 1:
   753       Clear();
   754       AppendElement(other.ElementAt(0));
   755       break;
   756     default:
   757       if (GetArraySize() >= count || SizeTo(count)) {
   758         *AsArray() = *other.AsArray();
   759       }
   760   }
   762   return *this;
   763 }
   765 int32_t
   766 nsSmallVoidArray::GetArraySize() const
   767 {
   768   if (HasSingle()) {
   769     return 1;
   770   }
   772   return AsArray()->GetArraySize();
   773 }
   775 int32_t
   776 nsSmallVoidArray::Count() const
   777 {
   778   if (HasSingle()) {
   779     return 1;
   780   }
   782   return AsArray()->Count();
   783 }
   785 void*
   786 nsSmallVoidArray::FastElementAt(int32_t aIndex) const
   787 {
   788   NS_ASSERTION(0 <= aIndex && aIndex < Count(), "nsSmallVoidArray::FastElementAt: index out of range");
   790   if (HasSingle()) {
   791     return GetSingle();
   792   }
   794   return AsArray()->FastElementAt(aIndex);
   795 }
   797 int32_t
   798 nsSmallVoidArray::IndexOf(void* aPossibleElement) const
   799 {
   800   if (HasSingle()) {
   801     return aPossibleElement == GetSingle() ? 0 : -1;
   802   }
   804   return AsArray()->IndexOf(aPossibleElement);
   805 }
   807 bool
   808 nsSmallVoidArray::InsertElementAt(void* aElement, int32_t aIndex)
   809 {
   810   NS_ASSERTION(!(NS_PTR_TO_INT32(aElement) & 0x1),
   811                "Attempt to add element with 0x1 bit set to nsSmallVoidArray");
   813   if (aIndex == 0 && IsEmpty()) {
   814     SetSingle(aElement);
   816     return true;
   817   }
   819   if (!EnsureArray()) {
   820     return false;
   821   }
   823   return AsArray()->InsertElementAt(aElement, aIndex);
   824 }
   826 bool nsSmallVoidArray::InsertElementsAt(const nsVoidArray &aOther, int32_t aIndex)
   827 {
   828 #ifdef DEBUG  
   829   for (int i = 0; i < aOther.Count(); i++) {
   830     NS_ASSERTION(!(NS_PTR_TO_INT32(aOther.ElementAt(i)) & 0x1),
   831                  "Attempt to add element with 0x1 bit set to nsSmallVoidArray");
   832   }
   833 #endif
   835   if (aIndex == 0 && IsEmpty() && aOther.Count() == 1) {
   836     SetSingle(aOther.FastElementAt(0));
   838     return true;
   839   }
   841   if (!EnsureArray()) {
   842     return false;
   843   }
   845   return AsArray()->InsertElementsAt(aOther, aIndex);
   846 }
   848 bool
   849 nsSmallVoidArray::ReplaceElementAt(void* aElement, int32_t aIndex)
   850 {
   851   NS_ASSERTION(!(NS_PTR_TO_INT32(aElement) & 0x1),
   852                "Attempt to add element with 0x1 bit set to nsSmallVoidArray");
   854   if (aIndex == 0 && (IsEmpty() || HasSingle())) {
   855     SetSingle(aElement);
   857     return true;
   858   }
   860   if (!EnsureArray()) {
   861     return false;
   862   }
   864   return AsArray()->ReplaceElementAt(aElement, aIndex);
   865 }
   867 bool
   868 nsSmallVoidArray::AppendElement(void* aElement)
   869 {
   870   NS_ASSERTION(!(NS_PTR_TO_INT32(aElement) & 0x1),
   871                "Attempt to add element with 0x1 bit set to nsSmallVoidArray");
   873   if (IsEmpty()) {
   874     SetSingle(aElement);
   876     return true;
   877   }
   879   if (!EnsureArray()) {
   880     return false;
   881   }
   883   return AsArray()->AppendElement(aElement);
   884 }
   886 bool
   887 nsSmallVoidArray::RemoveElement(void* aElement)
   888 {
   889   if (HasSingle()) {
   890     if (aElement == GetSingle()) {
   891       mImpl = nullptr;
   892       return true;
   893     }
   895     return false;
   896   }
   898   return AsArray()->RemoveElement(aElement);
   899 }
   901 void
   902 nsSmallVoidArray::RemoveElementAt(int32_t aIndex)
   903 {
   904   if (HasSingle()) {
   905     if (aIndex == 0) {
   906       mImpl = nullptr;
   907     }
   909     return;
   910   }
   912   AsArray()->RemoveElementAt(aIndex);
   913 }
   915 void
   916 nsSmallVoidArray::RemoveElementsAt(int32_t aIndex, int32_t aCount)
   917 {
   918   if (HasSingle()) {
   919     if (aIndex == 0) {
   920       if (aCount > 0) {
   921         mImpl = nullptr;
   922       }
   923     }
   925     return;
   926   }
   928   AsArray()->RemoveElementsAt(aIndex, aCount);
   929 }
   931 void
   932 nsSmallVoidArray::Clear()
   933 {
   934   if (HasSingle()) {
   935     mImpl = nullptr;
   936   }
   937   else {
   938     AsArray()->Clear();
   939   }
   940 }
   942 bool
   943 nsSmallVoidArray::SizeTo(int32_t aMin)
   944 {
   945   if (!HasSingle()) {
   946     return AsArray()->SizeTo(aMin);
   947   }
   949   if (aMin <= 0) {
   950     mImpl = nullptr;
   952     return true;
   953   }
   955   if (aMin == 1) {
   956     return true;
   957   }
   959   void* single = GetSingle();
   960   mImpl = nullptr;
   961   if (!AsArray()->SizeTo(aMin)) {
   962     SetSingle(single);
   964     return false;
   965   }
   967   AsArray()->AppendElement(single);
   969   return true;
   970 }
   972 void
   973 nsSmallVoidArray::Compact()
   974 {
   975   if (!HasSingle()) {
   976     AsArray()->Compact();
   977   }
   978 }
   980 void
   981 nsSmallVoidArray::Sort(nsVoidArrayComparatorFunc aFunc, void* aData)
   982 {
   983   if (!HasSingle()) {
   984     AsArray()->Sort(aFunc,aData);
   985   }
   986 }
   988 bool
   989 nsSmallVoidArray::EnumerateForwards(nsVoidArrayEnumFunc aFunc, void* aData)
   990 {
   991   if (HasSingle()) {
   992     return (*aFunc)(GetSingle(), aData);
   993   }
   994   return AsArray()->EnumerateForwards(aFunc,aData);
   995 }
   997 bool
   998 nsSmallVoidArray::EnumerateBackwards(nsVoidArrayEnumFunc aFunc, void* aData)
   999 {
  1000   if (HasSingle()) {
  1001     return (*aFunc)(GetSingle(), aData);
  1003   return AsArray()->EnumerateBackwards(aFunc,aData);
  1006 bool
  1007 nsSmallVoidArray::EnsureArray()
  1009   if (!HasSingle()) {
  1010     return true;
  1013   void* single = GetSingle();
  1014   mImpl = nullptr;
  1015   if (!AsArray()->AppendElement(single)) {
  1016     SetSingle(single);
  1018     return false;
  1021   return true;

mercurial