xpcom/glue/nsVoidArray.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/xpcom/glue/nsVoidArray.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1022 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; c-file-offsets: ((substatement-open . 0)) -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "mozilla/MathAlgorithms.h"
    1.10 +#include "mozilla/MemoryReporting.h"
    1.11 +#include <stdlib.h>
    1.12 +
    1.13 +#include "nsVoidArray.h"
    1.14 +#include "nsQuickSort.h"
    1.15 +#include "nsISupportsImpl.h" // for nsTraceRefcnt
    1.16 +#include "nsAlgorithm.h"
    1.17 +
    1.18 +/**
    1.19 + * Grow the array by at least this many elements at a time.
    1.20 + */
    1.21 +static const int32_t kMinGrowArrayBy = 8;
    1.22 +static const int32_t kMaxGrowArrayBy = 1024;
    1.23 +
    1.24 +/**
    1.25 + * This is the threshold (in bytes) of the mImpl struct, past which
    1.26 + * we'll force the array to grow geometrically
    1.27 + */
    1.28 +static const int32_t kLinearThreshold = 24 * sizeof(void *);
    1.29 +
    1.30 +/**
    1.31 + * Compute the number of bytes requires for the mImpl struct that will
    1.32 + * hold |n| elements.
    1.33 + */
    1.34 +#define SIZEOF_IMPL(n_) (sizeof(Impl) + sizeof(void *) * ((n_) - 1))
    1.35 +
    1.36 +/**
    1.37 + * Compute the number of elements that an mImpl struct of |n| bytes
    1.38 + * will hold.
    1.39 + */
    1.40 +#define CAPACITYOF_IMPL(n_) ((((n_) - sizeof(Impl)) / sizeof(void *)) + 1)
    1.41 +
    1.42 +#if DEBUG_VOIDARRAY
    1.43 +#define MAXVOID 10
    1.44 +
    1.45 +class VoidStats {
    1.46 +public:
    1.47 +  VoidStats();
    1.48 +  ~VoidStats();
    1.49 +
    1.50 +};
    1.51 +
    1.52 +static int sizesUsed; // number of the elements of the arrays used
    1.53 +static int sizesAlloced[MAXVOID]; // sizes of the allocations.  sorted
    1.54 +static int NumberOfSize[MAXVOID]; // number of this allocation size (1 per array)
    1.55 +static int AllocedOfSize[MAXVOID]; // number of this allocation size (each size for array used)
    1.56 +static int MaxAuto[MAXVOID];      // AutoArrays that maxed out at this size
    1.57 +static int GrowInPlace[MAXVOID];  // arrays this size that grew in-place via realloc
    1.58 +
    1.59 +// these are per-allocation  
    1.60 +static int MaxElements[2000];     // # of arrays that maxed out at each size.
    1.61 +
    1.62 +// statistics macros
    1.63 +#define ADD_TO_STATS(x,size) do {int i; for (i = 0; i < sizesUsed; i++) \
    1.64 +                                  { \
    1.65 +                                    if (sizesAlloced[i] == (int)(size)) \
    1.66 +                                    { ((x)[i])++; break; } \
    1.67 +                                  } \
    1.68 +                                  if (i >= sizesUsed && sizesUsed < MAXVOID) \
    1.69 +                                  { sizesAlloced[sizesUsed] = (size); \
    1.70 +                                    ((x)[sizesUsed++])++; break; \
    1.71 +                                  } \
    1.72 +                                } while (0)
    1.73 +
    1.74 +#define SUB_FROM_STATS(x,size) do {int i; for (i = 0; i < sizesUsed; i++) \
    1.75 +                                    { \
    1.76 +                                      if (sizesAlloced[i] == (int)(size)) \
    1.77 +                                      { ((x)[i])--; break; } \
    1.78 +                                    } \
    1.79 +                                  } while (0)
    1.80 +
    1.81 +
    1.82 +VoidStats::VoidStats()
    1.83 +{
    1.84 +  sizesUsed = 1;
    1.85 +  sizesAlloced[0] = 0;
    1.86 +}
    1.87 +
    1.88 +VoidStats::~VoidStats()
    1.89 +{
    1.90 +  int i;
    1.91 +  for (i = 0; i < sizesUsed; i++)
    1.92 +  {
    1.93 +    printf("Size %d:\n",sizesAlloced[i]);
    1.94 +    printf("\tNumber of VoidArrays this size (max):     %d\n",NumberOfSize[i]-MaxAuto[i]);
    1.95 +    printf("\tNumber of AutoVoidArrays this size (max): %d\n",MaxAuto[i]);
    1.96 +    printf("\tNumber of allocations this size (total):  %d\n",AllocedOfSize[i]);
    1.97 +    printf("\tNumber of GrowsInPlace this size (total): %d\n",GrowInPlace[i]);
    1.98 +  }
    1.99 +  printf("Max Size of VoidArray:\n");
   1.100 +  for (i = 0; i < (int)(sizeof(MaxElements)/sizeof(MaxElements[0])); i++)
   1.101 +  {
   1.102 +    if (MaxElements[i])
   1.103 +      printf("\t%d: %d\n",i,MaxElements[i]);
   1.104 +  }
   1.105 +}
   1.106 +
   1.107 +// Just so constructor/destructor's get called
   1.108 +VoidStats gVoidStats;
   1.109 +#endif
   1.110 +
   1.111 +void
   1.112 +nsVoidArray::SetArray(Impl *newImpl, int32_t aSize, int32_t aCount)
   1.113 +{
   1.114 +  // old mImpl has been realloced and so we don't free/delete it
   1.115 +  NS_PRECONDITION(newImpl, "can't set size");
   1.116 +  mImpl = newImpl;
   1.117 +  mImpl->mCount = aCount;
   1.118 +  mImpl->mSize = aSize;
   1.119 +}
   1.120 +
   1.121 +// This does all allocation/reallocation of the array.
   1.122 +// It also will compact down to N - good for things that might grow a lot
   1.123 +// at times,  but usually are smaller, like JS deferred GC releases.
   1.124 +bool nsVoidArray::SizeTo(int32_t aSize)
   1.125 +{
   1.126 +  uint32_t oldsize = GetArraySize();
   1.127 +
   1.128 +  if (aSize == (int32_t) oldsize)
   1.129 +    return true; // no change
   1.130 +
   1.131 +  if (aSize <= 0)
   1.132 +  {
   1.133 +    // free the array if allocated
   1.134 +    if (mImpl)
   1.135 +    {
   1.136 +      free(reinterpret_cast<char *>(mImpl));
   1.137 +      mImpl = nullptr;
   1.138 +    }
   1.139 +    return true;
   1.140 +  }
   1.141 +
   1.142 +  if (mImpl)
   1.143 +  {
   1.144 +    // We currently own an array impl. Resize it appropriately.
   1.145 +    if (aSize < mImpl->mCount)
   1.146 +    {
   1.147 +      // XXX Note: we could also just resize to mCount
   1.148 +      return true;  // can't make it that small, ignore request
   1.149 +    }
   1.150 +
   1.151 +    char* bytes = (char *) realloc(mImpl,SIZEOF_IMPL(aSize));
   1.152 +    Impl* newImpl = reinterpret_cast<Impl*>(bytes);
   1.153 +    if (!newImpl)
   1.154 +      return false;
   1.155 +
   1.156 +#if DEBUG_VOIDARRAY
   1.157 +    if (mImpl == newImpl)
   1.158 +      ADD_TO_STATS(GrowInPlace,oldsize);
   1.159 +    ADD_TO_STATS(AllocedOfSize,SIZEOF_IMPL(aSize));
   1.160 +    if (aSize > mMaxSize)
   1.161 +    {
   1.162 +      ADD_TO_STATS(NumberOfSize,SIZEOF_IMPL(aSize));
   1.163 +      if (oldsize)
   1.164 +        SUB_FROM_STATS(NumberOfSize,oldsize);
   1.165 +      mMaxSize = aSize;
   1.166 +      if (mIsAuto)
   1.167 +      {
   1.168 +        ADD_TO_STATS(MaxAuto,SIZEOF_IMPL(aSize));
   1.169 +        SUB_FROM_STATS(MaxAuto,oldsize);
   1.170 +      }
   1.171 +    }
   1.172 +#endif
   1.173 +    SetArray(newImpl, aSize, newImpl->mCount);
   1.174 +    return true;
   1.175 +  }
   1.176 +
   1.177 +  if ((uint32_t) aSize < oldsize) {
   1.178 +    // No point in allocating if it won't free the current Impl anyway.
   1.179 +    return true;
   1.180 +  }
   1.181 +
   1.182 +  // just allocate an array
   1.183 +  // allocate the exact size requested
   1.184 +  char* bytes = (char *) malloc(SIZEOF_IMPL(aSize));
   1.185 +  Impl* newImpl = reinterpret_cast<Impl*>(bytes);
   1.186 +  if (!newImpl)
   1.187 +    return false;
   1.188 +
   1.189 +#if DEBUG_VOIDARRAY
   1.190 +  ADD_TO_STATS(AllocedOfSize,SIZEOF_IMPL(aSize));
   1.191 +  if (aSize > mMaxSize)
   1.192 +  {
   1.193 +    ADD_TO_STATS(NumberOfSize,SIZEOF_IMPL(aSize));
   1.194 +    if (oldsize && !mImpl)
   1.195 +      SUB_FROM_STATS(NumberOfSize,oldsize);
   1.196 +    mMaxSize = aSize;
   1.197 +  }
   1.198 +#endif
   1.199 +  if (mImpl)
   1.200 +  {
   1.201 +#if DEBUG_VOIDARRAY
   1.202 +    ADD_TO_STATS(MaxAuto,SIZEOF_IMPL(aSize));
   1.203 +    SUB_FROM_STATS(MaxAuto,0);
   1.204 +    SUB_FROM_STATS(NumberOfSize,0);
   1.205 +    mIsAuto = true;
   1.206 +#endif
   1.207 +    // We must be growing an nsAutoVoidArray - copy since we didn't
   1.208 +    // realloc.
   1.209 +    memcpy(newImpl->mArray, mImpl->mArray,
   1.210 +                  mImpl->mCount * sizeof(mImpl->mArray[0]));
   1.211 +  }
   1.212 +
   1.213 +  SetArray(newImpl, aSize, mImpl ? mImpl->mCount : 0);
   1.214 +  // no memset; handled later in ReplaceElementAt if needed
   1.215 +  return true;
   1.216 +}
   1.217 +
   1.218 +bool nsVoidArray::GrowArrayBy(int32_t aGrowBy)
   1.219 +{
   1.220 +  // We have to grow the array. Grow by kMinGrowArrayBy slots if we're
   1.221 +  // smaller than kLinearThreshold bytes, or a power of two if we're
   1.222 +  // larger.  This is much more efficient with most memory allocators,
   1.223 +  // especially if it's very large, or of the allocator is binned.
   1.224 +  if (aGrowBy < kMinGrowArrayBy)
   1.225 +    aGrowBy = kMinGrowArrayBy;
   1.226 +
   1.227 +  uint32_t newCapacity = GetArraySize() + aGrowBy;  // Minimum increase
   1.228 +  uint32_t newSize = SIZEOF_IMPL(newCapacity);
   1.229 +
   1.230 +  if (newSize >= (uint32_t) kLinearThreshold)
   1.231 +  {
   1.232 +    // newCount includes enough space for at least kMinGrowArrayBy new
   1.233 +    // slots. Select the next power-of-two size in bytes above or
   1.234 +    // equal to that.
   1.235 +    // Also, limit the increase in size to about a VM page or two.
   1.236 +    if (GetArraySize() >= kMaxGrowArrayBy)
   1.237 +    {
   1.238 +      newCapacity = GetArraySize() + XPCOM_MAX(kMaxGrowArrayBy,aGrowBy);
   1.239 +      newSize = SIZEOF_IMPL(newCapacity);
   1.240 +    }
   1.241 +    else
   1.242 +    {
   1.243 +      newSize = mozilla::CeilingLog2(newSize);
   1.244 +      newCapacity = CAPACITYOF_IMPL(1u << newSize);
   1.245 +    }
   1.246 +  }
   1.247 +  // frees old mImpl IF this succeeds
   1.248 +  if (!SizeTo(newCapacity))
   1.249 +    return false;
   1.250 +
   1.251 +  return true;
   1.252 +}
   1.253 +
   1.254 +nsVoidArray::nsVoidArray()
   1.255 +  : mImpl(nullptr)
   1.256 +{
   1.257 +  MOZ_COUNT_CTOR(nsVoidArray);
   1.258 +#if DEBUG_VOIDARRAY
   1.259 +  mMaxCount = 0;
   1.260 +  mMaxSize = 0;
   1.261 +  mIsAuto = false;
   1.262 +  ADD_TO_STATS(NumberOfSize,0);
   1.263 +  MaxElements[0]++;
   1.264 +#endif
   1.265 +}
   1.266 +
   1.267 +nsVoidArray::nsVoidArray(int32_t aCount)
   1.268 +  : mImpl(nullptr)
   1.269 +{
   1.270 +  MOZ_COUNT_CTOR(nsVoidArray);
   1.271 +#if DEBUG_VOIDARRAY
   1.272 +  mMaxCount = 0;
   1.273 +  mMaxSize = 0;
   1.274 +  mIsAuto = false;
   1.275 +  MaxElements[0]++;
   1.276 +#endif
   1.277 +  SizeTo(aCount);
   1.278 +}
   1.279 +
   1.280 +nsVoidArray& nsVoidArray::operator=(const nsVoidArray& other)
   1.281 +{
   1.282 +  int32_t otherCount = other.Count();
   1.283 +  int32_t maxCount = GetArraySize();
   1.284 +  if (otherCount)
   1.285 +  {
   1.286 +    if (otherCount > maxCount)
   1.287 +    {
   1.288 +      // frees old mImpl IF this succeeds
   1.289 +      if (!GrowArrayBy(otherCount-maxCount))
   1.290 +        return *this;      // XXX The allocation failed - don't do anything
   1.291 +
   1.292 +      memcpy(mImpl->mArray, other.mImpl->mArray, otherCount * sizeof(mImpl->mArray[0]));
   1.293 +      mImpl->mCount = otherCount;
   1.294 +    }
   1.295 +    else
   1.296 +    {
   1.297 +      // the old array can hold the new array
   1.298 +      memcpy(mImpl->mArray, other.mImpl->mArray, otherCount * sizeof(mImpl->mArray[0]));
   1.299 +      mImpl->mCount = otherCount;
   1.300 +      // if it shrank a lot, compact it anyways
   1.301 +      if ((otherCount*2) < maxCount && maxCount > 100)
   1.302 +      {
   1.303 +        Compact();  // shrank by at least 50 entries
   1.304 +      }
   1.305 +    }
   1.306 +#if DEBUG_VOIDARRAY
   1.307 +     if (mImpl->mCount > mMaxCount &&
   1.308 +         mImpl->mCount < (int32_t)(sizeof(MaxElements)/sizeof(MaxElements[0])))
   1.309 +     {
   1.310 +       MaxElements[mImpl->mCount]++;
   1.311 +       MaxElements[mMaxCount]--;
   1.312 +       mMaxCount = mImpl->mCount;
   1.313 +     }
   1.314 +#endif
   1.315 +  }
   1.316 +  else
   1.317 +  {
   1.318 +    // Why do we drop the buffer here when we don't in Clear()?
   1.319 +    SizeTo(0);
   1.320 +  }
   1.321 +
   1.322 +  return *this;
   1.323 +}
   1.324 +
   1.325 +nsVoidArray::~nsVoidArray()
   1.326 +{
   1.327 +  MOZ_COUNT_DTOR(nsVoidArray);
   1.328 +  if (mImpl)
   1.329 +    free(reinterpret_cast<char*>(mImpl));
   1.330 +}
   1.331 +
   1.332 +bool nsVoidArray::SetCount(int32_t aNewCount)
   1.333 +{
   1.334 +  NS_ASSERTION(aNewCount >= 0,"SetCount(negative index)");
   1.335 +  if (aNewCount < 0)
   1.336 +    return false;
   1.337 +
   1.338 +  if (aNewCount == 0)
   1.339 +  {
   1.340 +    Clear();
   1.341 +    return true;
   1.342 +  }
   1.343 +
   1.344 +  if (uint32_t(aNewCount) > uint32_t(GetArraySize()))
   1.345 +  {
   1.346 +    int32_t oldCount = Count();
   1.347 +    int32_t growDelta = aNewCount - oldCount;
   1.348 +
   1.349 +    // frees old mImpl IF this succeeds
   1.350 +    if (!GrowArrayBy(growDelta))
   1.351 +      return false;
   1.352 +  }
   1.353 +
   1.354 +  if (aNewCount > mImpl->mCount)
   1.355 +  {
   1.356 +    // Make sure that new entries added to the array by this
   1.357 +    // SetCount are cleared to 0.  Some users of this assume that.
   1.358 +    // This code means we don't have to memset when we allocate an array.
   1.359 +    memset(&mImpl->mArray[mImpl->mCount], 0,
   1.360 +           (aNewCount - mImpl->mCount) * sizeof(mImpl->mArray[0]));
   1.361 +  }
   1.362 +
   1.363 +  mImpl->mCount = aNewCount;
   1.364 +
   1.365 +#if DEBUG_VOIDARRAY
   1.366 +  if (mImpl->mCount > mMaxCount &&
   1.367 +      mImpl->mCount < (int32_t)(sizeof(MaxElements)/sizeof(MaxElements[0])))
   1.368 +  {
   1.369 +    MaxElements[mImpl->mCount]++;
   1.370 +    MaxElements[mMaxCount]--;
   1.371 +    mMaxCount = mImpl->mCount;
   1.372 +  }
   1.373 +#endif
   1.374 +
   1.375 +  return true;
   1.376 +}
   1.377 +
   1.378 +int32_t nsVoidArray::IndexOf(void* aPossibleElement) const
   1.379 +{
   1.380 +  if (mImpl)
   1.381 +  {
   1.382 +    void** ap = mImpl->mArray;
   1.383 +    void** end = ap + mImpl->mCount;
   1.384 +    while (ap < end)
   1.385 +    {
   1.386 +      if (*ap == aPossibleElement)
   1.387 +      {
   1.388 +        return ap - mImpl->mArray;
   1.389 +      }
   1.390 +      ap++;
   1.391 +    }
   1.392 +  }
   1.393 +  return -1;
   1.394 +}
   1.395 +
   1.396 +bool nsVoidArray::InsertElementAt(void* aElement, int32_t aIndex)
   1.397 +{
   1.398 +  int32_t oldCount = Count();
   1.399 +  NS_ASSERTION(aIndex >= 0,"InsertElementAt(negative index)");
   1.400 +  if (uint32_t(aIndex) > uint32_t(oldCount))
   1.401 +  {
   1.402 +    // An invalid index causes the insertion to fail
   1.403 +    // Invalid indexes are ones that add more than one entry to the
   1.404 +    // array (i.e., they can append).
   1.405 +    return false;
   1.406 +  }
   1.407 +
   1.408 +  if (oldCount >= GetArraySize())
   1.409 +  {
   1.410 +    if (!GrowArrayBy(1))
   1.411 +      return false;
   1.412 +  }
   1.413 +  // else the array is already large enough
   1.414 +
   1.415 +  int32_t slide = oldCount - aIndex;
   1.416 +  if (0 != slide)
   1.417 +  {
   1.418 +    // Slide data over to make room for the insertion
   1.419 +    memmove(mImpl->mArray + aIndex + 1, mImpl->mArray + aIndex,
   1.420 +            slide * sizeof(mImpl->mArray[0]));
   1.421 +  }
   1.422 +
   1.423 +  mImpl->mArray[aIndex] = aElement;
   1.424 +  mImpl->mCount++;
   1.425 +
   1.426 +#if DEBUG_VOIDARRAY
   1.427 +  if (mImpl->mCount > mMaxCount &&
   1.428 +      mImpl->mCount < (int32_t)(sizeof(MaxElements)/sizeof(MaxElements[0])))
   1.429 +  {
   1.430 +    MaxElements[mImpl->mCount]++;
   1.431 +    MaxElements[mMaxCount]--;
   1.432 +    mMaxCount = mImpl->mCount;
   1.433 +  }
   1.434 +#endif
   1.435 +
   1.436 +  return true;
   1.437 +}
   1.438 +
   1.439 +bool nsVoidArray::InsertElementsAt(const nsVoidArray& other, int32_t aIndex)
   1.440 +{
   1.441 +  int32_t oldCount = Count();
   1.442 +  int32_t otherCount = other.Count();
   1.443 +
   1.444 +  NS_ASSERTION(aIndex >= 0,"InsertElementsAt(negative index)");
   1.445 +  if (uint32_t(aIndex) > uint32_t(oldCount))
   1.446 +  {
   1.447 +    // An invalid index causes the insertion to fail
   1.448 +    // Invalid indexes are ones that are more than one entry past the end of
   1.449 +    // the array (i.e., they can append).
   1.450 +    return false;
   1.451 +  }
   1.452 +
   1.453 +  if (oldCount + otherCount > GetArraySize())
   1.454 +  {
   1.455 +    if (!GrowArrayBy(otherCount))
   1.456 +      return false;;
   1.457 +  }
   1.458 +  // else the array is already large enough
   1.459 +
   1.460 +  int32_t slide = oldCount - aIndex;
   1.461 +  if (0 != slide)
   1.462 +  {
   1.463 +    // Slide data over to make room for the insertion
   1.464 +    memmove(mImpl->mArray + aIndex + otherCount, mImpl->mArray + aIndex,
   1.465 +            slide * sizeof(mImpl->mArray[0]));
   1.466 +  }
   1.467 +
   1.468 +  for (int32_t i = 0; i < otherCount; i++)
   1.469 +  {
   1.470 +    // copy all the elements (destroys aIndex)
   1.471 +    mImpl->mArray[aIndex++] = other.mImpl->mArray[i];
   1.472 +    mImpl->mCount++;
   1.473 +  }
   1.474 +
   1.475 +#if DEBUG_VOIDARRAY
   1.476 +  if (mImpl->mCount > mMaxCount &&
   1.477 +      mImpl->mCount < (int32_t)(sizeof(MaxElements)/sizeof(MaxElements[0])))
   1.478 +  {
   1.479 +    MaxElements[mImpl->mCount]++;
   1.480 +    MaxElements[mMaxCount]--;
   1.481 +    mMaxCount = mImpl->mCount;
   1.482 +  }
   1.483 +#endif
   1.484 +
   1.485 +  return true;
   1.486 +}
   1.487 +
   1.488 +bool nsVoidArray::ReplaceElementAt(void* aElement, int32_t aIndex)
   1.489 +{
   1.490 +  NS_ASSERTION(aIndex >= 0,"ReplaceElementAt(negative index)");
   1.491 +  if (aIndex < 0)
   1.492 +    return false;
   1.493 +
   1.494 +  // Unlike InsertElementAt, ReplaceElementAt can implicitly add more
   1.495 +  // than just the one element to the array.
   1.496 +  if (uint32_t(aIndex) >= uint32_t(GetArraySize()))
   1.497 +  {
   1.498 +    int32_t oldCount = Count();
   1.499 +    int32_t requestedCount = aIndex + 1;
   1.500 +    int32_t growDelta = requestedCount - oldCount;
   1.501 +
   1.502 +    // frees old mImpl IF this succeeds
   1.503 +    if (!GrowArrayBy(growDelta))
   1.504 +      return false;
   1.505 +  }
   1.506 +
   1.507 +  mImpl->mArray[aIndex] = aElement;
   1.508 +  if (aIndex >= mImpl->mCount)
   1.509 +  {
   1.510 +    // Make sure that any entries implicitly added to the array by this
   1.511 +    // ReplaceElementAt are cleared to 0.  Some users of this assume that.
   1.512 +    // This code means we don't have to memset when we allocate an array.
   1.513 +    if (aIndex > mImpl->mCount) // note: not >=
   1.514 +    {
   1.515 +      // For example, if mCount is 2, and we do a ReplaceElementAt for
   1.516 +      // element[5], then we need to set three entries ([2], [3], and [4])
   1.517 +      // to 0.
   1.518 +      memset(&mImpl->mArray[mImpl->mCount], 0,
   1.519 +             (aIndex - mImpl->mCount) * sizeof(mImpl->mArray[0]));
   1.520 +    }
   1.521 +    
   1.522 +     mImpl->mCount = aIndex + 1;
   1.523 +
   1.524 +#if DEBUG_VOIDARRAY
   1.525 +     if (mImpl->mCount > mMaxCount &&
   1.526 +         mImpl->mCount < (int32_t)(sizeof(MaxElements)/sizeof(MaxElements[0])))
   1.527 +     {
   1.528 +       MaxElements[mImpl->mCount]++;
   1.529 +       MaxElements[mMaxCount]--;
   1.530 +       mMaxCount = mImpl->mCount;
   1.531 +     }
   1.532 +#endif
   1.533 +  }
   1.534 +
   1.535 +  return true;
   1.536 +}
   1.537 +
   1.538 +// useful for doing LRU arrays
   1.539 +bool nsVoidArray::MoveElement(int32_t aFrom, int32_t aTo)
   1.540 +{
   1.541 +  void *tempElement;
   1.542 +
   1.543 +  if (aTo == aFrom)
   1.544 +    return true;
   1.545 +
   1.546 +  NS_ASSERTION(aTo >= 0 && aFrom >= 0,"MoveElement(negative index)");
   1.547 +  if (aTo >= Count() || aFrom >= Count())
   1.548 +  {
   1.549 +    // can't extend the array when moving an element.  Also catches mImpl = null
   1.550 +    return false;
   1.551 +  }
   1.552 +  tempElement = mImpl->mArray[aFrom];
   1.553 +
   1.554 +  if (aTo < aFrom)
   1.555 +  {
   1.556 +    // Moving one element closer to the head; the elements inbetween move down
   1.557 +    memmove(mImpl->mArray + aTo + 1, mImpl->mArray + aTo,
   1.558 +            (aFrom-aTo) * sizeof(mImpl->mArray[0]));
   1.559 +    mImpl->mArray[aTo] = tempElement;
   1.560 +  }
   1.561 +  else // already handled aFrom == aTo
   1.562 +  {
   1.563 +    // Moving one element closer to the tail; the elements inbetween move up
   1.564 +    memmove(mImpl->mArray + aFrom, mImpl->mArray + aFrom + 1,
   1.565 +            (aTo-aFrom) * sizeof(mImpl->mArray[0]));
   1.566 +    mImpl->mArray[aTo] = tempElement;
   1.567 +  }
   1.568 +
   1.569 +  return true;
   1.570 +}
   1.571 +
   1.572 +void nsVoidArray::RemoveElementsAt(int32_t aIndex, int32_t aCount)
   1.573 +{
   1.574 +  int32_t oldCount = Count();
   1.575 +  NS_ASSERTION(aIndex >= 0,"RemoveElementsAt(negative index)");
   1.576 +  if (uint32_t(aIndex) >= uint32_t(oldCount))
   1.577 +  {
   1.578 +    return;
   1.579 +  }
   1.580 +  // Limit to available entries starting at aIndex
   1.581 +  if (aCount + aIndex > oldCount)
   1.582 +    aCount = oldCount - aIndex;
   1.583 +
   1.584 +  // We don't need to move any elements if we're removing the
   1.585 +  // last element in the array
   1.586 +  if (aIndex < (oldCount - aCount))
   1.587 +  {
   1.588 +    memmove(mImpl->mArray + aIndex, mImpl->mArray + aIndex + aCount,
   1.589 +            (oldCount - (aIndex + aCount)) * sizeof(mImpl->mArray[0]));
   1.590 +  }
   1.591 +
   1.592 +  mImpl->mCount -= aCount;
   1.593 +  return;
   1.594 +}
   1.595 +
   1.596 +bool nsVoidArray::RemoveElement(void* aElement)
   1.597 +{
   1.598 +  int32_t theIndex = IndexOf(aElement);
   1.599 +  if (theIndex != -1)
   1.600 +  {
   1.601 +    RemoveElementAt(theIndex);
   1.602 +    return true;
   1.603 +  }
   1.604 +
   1.605 +  return false;
   1.606 +}
   1.607 +
   1.608 +void nsVoidArray::Clear()
   1.609 +{
   1.610 +  if (mImpl)
   1.611 +  {
   1.612 +    mImpl->mCount = 0;
   1.613 +  }
   1.614 +}
   1.615 +
   1.616 +void nsVoidArray::Compact()
   1.617 +{
   1.618 +  if (mImpl)
   1.619 +  {
   1.620 +    // XXX NOTE: this is quite inefficient in many cases if we're only
   1.621 +    // compacting by a little, but some callers care more about memory use.
   1.622 +    int32_t count = Count();
   1.623 +    if (GetArraySize() > count)
   1.624 +    {
   1.625 +      SizeTo(Count());
   1.626 +    }
   1.627 +  }
   1.628 +}
   1.629 +
   1.630 +// Needed because we want to pass the pointer to the item in the array
   1.631 +// to the comparator function, not a pointer to the pointer in the array.
   1.632 +struct VoidArrayComparatorContext {
   1.633 +  nsVoidArrayComparatorFunc mComparatorFunc;
   1.634 +  void* mData;
   1.635 +};
   1.636 +
   1.637 +static int
   1.638 +VoidArrayComparator(const void* aElement1, const void* aElement2, void* aData)
   1.639 +{
   1.640 +  VoidArrayComparatorContext* ctx = static_cast<VoidArrayComparatorContext*>(aData);
   1.641 +  return (*ctx->mComparatorFunc)(*static_cast<void* const*>(aElement1),
   1.642 +                                 *static_cast<void* const*>(aElement2),
   1.643 +                                  ctx->mData);
   1.644 +}
   1.645 +
   1.646 +void nsVoidArray::Sort(nsVoidArrayComparatorFunc aFunc, void* aData)
   1.647 +{
   1.648 +  if (mImpl && mImpl->mCount > 1)
   1.649 +  {
   1.650 +    VoidArrayComparatorContext ctx = {aFunc, aData};
   1.651 +    NS_QuickSort(mImpl->mArray, mImpl->mCount, sizeof(mImpl->mArray[0]),
   1.652 +                 VoidArrayComparator, &ctx);
   1.653 +  }
   1.654 +}
   1.655 +
   1.656 +bool nsVoidArray::EnumerateForwards(nsVoidArrayEnumFunc aFunc, void* aData)
   1.657 +{
   1.658 +  int32_t index = -1;
   1.659 +  bool    running = true;
   1.660 +
   1.661 +  if (mImpl) {
   1.662 +    while (running && (++index < mImpl->mCount)) {
   1.663 +      running = (*aFunc)(mImpl->mArray[index], aData);
   1.664 +    }
   1.665 +  }
   1.666 +  return running;
   1.667 +}
   1.668 +
   1.669 +bool nsVoidArray::EnumerateForwards(nsVoidArrayEnumFuncConst aFunc,
   1.670 +                                    void* aData) const
   1.671 +{
   1.672 +  int32_t index = -1;
   1.673 +  bool    running = true;
   1.674 +
   1.675 +  if (mImpl) {
   1.676 +    while (running && (++index < mImpl->mCount)) {
   1.677 +      running = (*aFunc)(mImpl->mArray[index], aData);
   1.678 +    }
   1.679 +  }
   1.680 +  return running;
   1.681 +}
   1.682 +
   1.683 +bool nsVoidArray::EnumerateBackwards(nsVoidArrayEnumFunc aFunc, void* aData)
   1.684 +{
   1.685 +  bool    running = true;
   1.686 +
   1.687 +  if (mImpl)
   1.688 +  {
   1.689 +    int32_t index = Count();
   1.690 +    while (running && (0 <= --index))
   1.691 +    {
   1.692 +      running = (*aFunc)(mImpl->mArray[index], aData);
   1.693 +    }
   1.694 +  }
   1.695 +  return running;
   1.696 +}
   1.697 +
   1.698 +struct SizeOfElementIncludingThisData
   1.699 +{
   1.700 +  size_t mSize;
   1.701 +  nsVoidArraySizeOfElementIncludingThisFunc mSizeOfElementIncludingThis;
   1.702 +  mozilla::MallocSizeOf mMallocSizeOf;
   1.703 +  void *mData;      // the arg passed by the user
   1.704 +};
   1.705 +
   1.706 +static bool
   1.707 +SizeOfElementIncludingThisEnumerator(const void *aElement, void *aData)
   1.708 +{
   1.709 +  SizeOfElementIncludingThisData *d = (SizeOfElementIncludingThisData *)aData;
   1.710 +  d->mSize += d->mSizeOfElementIncludingThis(aElement, d->mMallocSizeOf, d->mData);
   1.711 +  return true;
   1.712 +}
   1.713 +
   1.714 +size_t
   1.715 +nsVoidArray::SizeOfExcludingThis(
   1.716 +  nsVoidArraySizeOfElementIncludingThisFunc aSizeOfElementIncludingThis,
   1.717 +  mozilla::MallocSizeOf aMallocSizeOf, void* aData) const
   1.718 +{
   1.719 +  size_t n = 0;
   1.720 +  // Measure the element storage.
   1.721 +  if (mImpl) {
   1.722 +    n += aMallocSizeOf(mImpl);
   1.723 +  }
   1.724 +  // Measure things pointed to by the elements.
   1.725 +  if (aSizeOfElementIncludingThis) { 
   1.726 +    SizeOfElementIncludingThisData data2 =
   1.727 +      { 0, aSizeOfElementIncludingThis, aMallocSizeOf, aData };
   1.728 +    EnumerateForwards(SizeOfElementIncludingThisEnumerator, &data2);
   1.729 +    n += data2.mSize;
   1.730 +  }
   1.731 +  return n;
   1.732 +}
   1.733 +
   1.734 +//----------------------------------------------------------------------
   1.735 +// NOTE: nsSmallVoidArray elements MUST all have the low bit as 0.
   1.736 +// This means that normally it's only used for pointers, and in particular
   1.737 +// structures or objects.
   1.738 +nsSmallVoidArray::~nsSmallVoidArray()
   1.739 +{
   1.740 +  if (HasSingle())
   1.741 +  {
   1.742 +    // Have to null out mImpl before the nsVoidArray dtor runs.
   1.743 +    mImpl = nullptr;
   1.744 +  }
   1.745 +}
   1.746 +
   1.747 +nsSmallVoidArray& 
   1.748 +nsSmallVoidArray::operator=(nsSmallVoidArray& other)
   1.749 +{
   1.750 +  int32_t count = other.Count();
   1.751 +  switch (count) {
   1.752 +    case 0:
   1.753 +      Clear();
   1.754 +      break;
   1.755 +    case 1:
   1.756 +      Clear();
   1.757 +      AppendElement(other.ElementAt(0));
   1.758 +      break;
   1.759 +    default:
   1.760 +      if (GetArraySize() >= count || SizeTo(count)) {
   1.761 +        *AsArray() = *other.AsArray();
   1.762 +      }
   1.763 +  }
   1.764 +    
   1.765 +  return *this;
   1.766 +}
   1.767 +
   1.768 +int32_t
   1.769 +nsSmallVoidArray::GetArraySize() const
   1.770 +{
   1.771 +  if (HasSingle()) {
   1.772 +    return 1;
   1.773 +  }
   1.774 +
   1.775 +  return AsArray()->GetArraySize();
   1.776 +}
   1.777 +
   1.778 +int32_t
   1.779 +nsSmallVoidArray::Count() const
   1.780 +{
   1.781 +  if (HasSingle()) {
   1.782 +    return 1;
   1.783 +  }
   1.784 +
   1.785 +  return AsArray()->Count();
   1.786 +}
   1.787 +
   1.788 +void*
   1.789 +nsSmallVoidArray::FastElementAt(int32_t aIndex) const
   1.790 +{
   1.791 +  NS_ASSERTION(0 <= aIndex && aIndex < Count(), "nsSmallVoidArray::FastElementAt: index out of range");
   1.792 +
   1.793 +  if (HasSingle()) {
   1.794 +    return GetSingle();
   1.795 +  }
   1.796 +
   1.797 +  return AsArray()->FastElementAt(aIndex);
   1.798 +}
   1.799 +
   1.800 +int32_t
   1.801 +nsSmallVoidArray::IndexOf(void* aPossibleElement) const
   1.802 +{
   1.803 +  if (HasSingle()) {
   1.804 +    return aPossibleElement == GetSingle() ? 0 : -1;
   1.805 +  }
   1.806 +
   1.807 +  return AsArray()->IndexOf(aPossibleElement);
   1.808 +}
   1.809 +
   1.810 +bool
   1.811 +nsSmallVoidArray::InsertElementAt(void* aElement, int32_t aIndex)
   1.812 +{
   1.813 +  NS_ASSERTION(!(NS_PTR_TO_INT32(aElement) & 0x1),
   1.814 +               "Attempt to add element with 0x1 bit set to nsSmallVoidArray");
   1.815 +
   1.816 +  if (aIndex == 0 && IsEmpty()) {
   1.817 +    SetSingle(aElement);
   1.818 +
   1.819 +    return true;
   1.820 +  }
   1.821 +
   1.822 +  if (!EnsureArray()) {
   1.823 +    return false;
   1.824 +  }
   1.825 +
   1.826 +  return AsArray()->InsertElementAt(aElement, aIndex);
   1.827 +}
   1.828 +
   1.829 +bool nsSmallVoidArray::InsertElementsAt(const nsVoidArray &aOther, int32_t aIndex)
   1.830 +{
   1.831 +#ifdef DEBUG  
   1.832 +  for (int i = 0; i < aOther.Count(); i++) {
   1.833 +    NS_ASSERTION(!(NS_PTR_TO_INT32(aOther.ElementAt(i)) & 0x1),
   1.834 +                 "Attempt to add element with 0x1 bit set to nsSmallVoidArray");
   1.835 +  }
   1.836 +#endif
   1.837 +
   1.838 +  if (aIndex == 0 && IsEmpty() && aOther.Count() == 1) {
   1.839 +    SetSingle(aOther.FastElementAt(0));
   1.840 +    
   1.841 +    return true;
   1.842 +  }
   1.843 +
   1.844 +  if (!EnsureArray()) {
   1.845 +    return false;
   1.846 +  }
   1.847 +
   1.848 +  return AsArray()->InsertElementsAt(aOther, aIndex);
   1.849 +}
   1.850 +
   1.851 +bool
   1.852 +nsSmallVoidArray::ReplaceElementAt(void* aElement, int32_t aIndex)
   1.853 +{
   1.854 +  NS_ASSERTION(!(NS_PTR_TO_INT32(aElement) & 0x1),
   1.855 +               "Attempt to add element with 0x1 bit set to nsSmallVoidArray");
   1.856 +
   1.857 +  if (aIndex == 0 && (IsEmpty() || HasSingle())) {
   1.858 +    SetSingle(aElement);
   1.859 +    
   1.860 +    return true;
   1.861 +  }
   1.862 +
   1.863 +  if (!EnsureArray()) {
   1.864 +    return false;
   1.865 +  }
   1.866 +
   1.867 +  return AsArray()->ReplaceElementAt(aElement, aIndex);
   1.868 +}
   1.869 +
   1.870 +bool
   1.871 +nsSmallVoidArray::AppendElement(void* aElement)
   1.872 +{
   1.873 +  NS_ASSERTION(!(NS_PTR_TO_INT32(aElement) & 0x1),
   1.874 +               "Attempt to add element with 0x1 bit set to nsSmallVoidArray");
   1.875 +
   1.876 +  if (IsEmpty()) {
   1.877 +    SetSingle(aElement);
   1.878 +    
   1.879 +    return true;
   1.880 +  }
   1.881 +
   1.882 +  if (!EnsureArray()) {
   1.883 +    return false;
   1.884 +  }
   1.885 +
   1.886 +  return AsArray()->AppendElement(aElement);
   1.887 +}
   1.888 +
   1.889 +bool
   1.890 +nsSmallVoidArray::RemoveElement(void* aElement)
   1.891 +{
   1.892 +  if (HasSingle()) {
   1.893 +    if (aElement == GetSingle()) {
   1.894 +      mImpl = nullptr;
   1.895 +      return true;
   1.896 +    }
   1.897 +    
   1.898 +    return false;
   1.899 +  }
   1.900 +
   1.901 +  return AsArray()->RemoveElement(aElement);
   1.902 +}
   1.903 +
   1.904 +void
   1.905 +nsSmallVoidArray::RemoveElementAt(int32_t aIndex)
   1.906 +{
   1.907 +  if (HasSingle()) {
   1.908 +    if (aIndex == 0) {
   1.909 +      mImpl = nullptr;
   1.910 +    }
   1.911 +    
   1.912 +    return;
   1.913 +  }
   1.914 +
   1.915 +  AsArray()->RemoveElementAt(aIndex);
   1.916 +}
   1.917 +
   1.918 +void
   1.919 +nsSmallVoidArray::RemoveElementsAt(int32_t aIndex, int32_t aCount)
   1.920 +{
   1.921 +  if (HasSingle()) {
   1.922 +    if (aIndex == 0) {
   1.923 +      if (aCount > 0) {
   1.924 +        mImpl = nullptr;
   1.925 +      }
   1.926 +    }
   1.927 +
   1.928 +    return;
   1.929 +  }
   1.930 +
   1.931 +  AsArray()->RemoveElementsAt(aIndex, aCount);
   1.932 +}
   1.933 +
   1.934 +void
   1.935 +nsSmallVoidArray::Clear()
   1.936 +{
   1.937 +  if (HasSingle()) {
   1.938 +    mImpl = nullptr;
   1.939 +  }
   1.940 +  else {
   1.941 +    AsArray()->Clear();
   1.942 +  }
   1.943 +}
   1.944 +
   1.945 +bool
   1.946 +nsSmallVoidArray::SizeTo(int32_t aMin)
   1.947 +{
   1.948 +  if (!HasSingle()) {
   1.949 +    return AsArray()->SizeTo(aMin);
   1.950 +  }
   1.951 +
   1.952 +  if (aMin <= 0) {
   1.953 +    mImpl = nullptr;
   1.954 +
   1.955 +    return true;
   1.956 +  }
   1.957 +
   1.958 +  if (aMin == 1) {
   1.959 +    return true;
   1.960 +  }
   1.961 +
   1.962 +  void* single = GetSingle();
   1.963 +  mImpl = nullptr;
   1.964 +  if (!AsArray()->SizeTo(aMin)) {
   1.965 +    SetSingle(single);
   1.966 +
   1.967 +    return false;
   1.968 +  }
   1.969 +
   1.970 +  AsArray()->AppendElement(single);
   1.971 +
   1.972 +  return true;
   1.973 +}
   1.974 +
   1.975 +void
   1.976 +nsSmallVoidArray::Compact()
   1.977 +{
   1.978 +  if (!HasSingle()) {
   1.979 +    AsArray()->Compact();
   1.980 +  }
   1.981 +}
   1.982 +
   1.983 +void
   1.984 +nsSmallVoidArray::Sort(nsVoidArrayComparatorFunc aFunc, void* aData)
   1.985 +{
   1.986 +  if (!HasSingle()) {
   1.987 +    AsArray()->Sort(aFunc,aData);
   1.988 +  }
   1.989 +}
   1.990 +
   1.991 +bool
   1.992 +nsSmallVoidArray::EnumerateForwards(nsVoidArrayEnumFunc aFunc, void* aData)
   1.993 +{
   1.994 +  if (HasSingle()) {
   1.995 +    return (*aFunc)(GetSingle(), aData);
   1.996 +  }
   1.997 +  return AsArray()->EnumerateForwards(aFunc,aData);
   1.998 +}
   1.999 +
  1.1000 +bool
  1.1001 +nsSmallVoidArray::EnumerateBackwards(nsVoidArrayEnumFunc aFunc, void* aData)
  1.1002 +{
  1.1003 +  if (HasSingle()) {
  1.1004 +    return (*aFunc)(GetSingle(), aData);
  1.1005 +  }
  1.1006 +  return AsArray()->EnumerateBackwards(aFunc,aData);
  1.1007 +}
  1.1008 +
  1.1009 +bool
  1.1010 +nsSmallVoidArray::EnsureArray()
  1.1011 +{
  1.1012 +  if (!HasSingle()) {
  1.1013 +    return true;
  1.1014 +  }
  1.1015 +
  1.1016 +  void* single = GetSingle();
  1.1017 +  mImpl = nullptr;
  1.1018 +  if (!AsArray()->AppendElement(single)) {
  1.1019 +    SetSingle(single);
  1.1020 +
  1.1021 +    return false;
  1.1022 +  }
  1.1023 +
  1.1024 +  return true;
  1.1025 +}

mercurial