Raw File
nsVoidArray.cpp
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "mozilla/MathAlgorithms.h"
#include "mozilla/MemoryReporting.h"
#include <stdlib.h>

#include "nsVoidArray.h"
#include "nsQuickSort.h"
#include "nsISupportsImpl.h" // for nsTraceRefcnt
#include "nsAlgorithm.h"

/**
 * Grow the array by at least this many elements at a time.
 */
static const int32_t kMinGrowArrayBy = 8;
static const int32_t kMaxGrowArrayBy = 1024;

/**
 * This is the threshold (in bytes) of the mImpl struct, past which
 * we'll force the array to grow geometrically
 */
static const int32_t kLinearThreshold = 24 * sizeof(void*);

/**
 * Compute the number of bytes requires for the mImpl struct that will
 * hold |n| elements.
 */
#define SIZEOF_IMPL(n_) (sizeof(Impl) + sizeof(void *) * ((n_) - 1))

/**
 * Compute the number of elements that an mImpl struct of |n| bytes
 * will hold.
 */
#define CAPACITYOF_IMPL(n_) ((((n_) - sizeof(Impl)) / sizeof(void *)) + 1)

#if DEBUG_VOIDARRAY
#define MAXVOID 10

class VoidStats
{
public:
  VoidStats();
  ~VoidStats();
};

static int sizesUsed; // number of the elements of the arrays used
static int sizesAlloced[MAXVOID]; // sizes of the allocations.  sorted
static int NumberOfSize[MAXVOID]; // number of this allocation size (1 per array)
static int AllocedOfSize[MAXVOID]; // number of this allocation size (each size for array used)
static int MaxAuto[MAXVOID];      // AutoArrays that maxed out at this size
static int GrowInPlace[MAXVOID];  // arrays this size that grew in-place via realloc

// these are per-allocation
static int MaxElements[2000];     // # of arrays that maxed out at each size.

// statistics macros
#define ADD_TO_STATS(x,size) do {int i; for (i = 0; i < sizesUsed; i++) \
                                  { \
                                    if (sizesAlloced[i] == (int)(size)) \
                                    { ((x)[i])++; break; } \
                                  } \
                                  if (i >= sizesUsed && sizesUsed < MAXVOID) \
                                  { sizesAlloced[sizesUsed] = (size); \
                                    ((x)[sizesUsed++])++; break; \
                                  } \
                                } while (0)

#define SUB_FROM_STATS(x,size) do {int i; for (i = 0; i < sizesUsed; i++) \
                                    { \
                                      if (sizesAlloced[i] == (int)(size)) \
                                      { ((x)[i])--; break; } \
                                    } \
                                  } while (0)


VoidStats::VoidStats()
{
  sizesUsed = 1;
  sizesAlloced[0] = 0;
}

VoidStats::~VoidStats()
{
  int i;
  for (i = 0; i < sizesUsed; ++i) {
    printf("Size %d:\n",sizesAlloced[i]);
    printf("\tNumber of VoidArrays this size (max):     %d\n",NumberOfSize[i]-MaxAuto[i]);
    printf("\tNumber of AutoVoidArrays this size (max): %d\n",MaxAuto[i]);
    printf("\tNumber of allocations this size (total):  %d\n",AllocedOfSize[i]);
    printf("\tNumber of GrowsInPlace this size (total): %d\n",GrowInPlace[i]);
  }
  printf("Max Size of VoidArray:\n");
  for (i = 0; i < (int)(sizeof(MaxElements) / sizeof(MaxElements[0])); ++i) {
    if (MaxElements[i]) {
      printf("\t%d: %d\n", i, MaxElements[i]);
    }
  }
}

// Just so constructor/destructor's get called
VoidStats gVoidStats;
#endif

void
nsVoidArray::SetArray(Impl* aNewImpl, int32_t aSize, int32_t aCount)
{
  // old mImpl has been realloced and so we don't free/delete it
  NS_PRECONDITION(aNewImpl, "can't set size");
  mImpl = aNewImpl;
  mImpl->mCount = aCount;
  mImpl->mSize = aSize;
}

// This does all allocation/reallocation of the array.
// It also will compact down to N - good for things that might grow a lot
// at times,  but usually are smaller, like JS deferred GC releases.
bool
nsVoidArray::SizeTo(int32_t aSize)
{
  uint32_t oldsize = GetArraySize();

  if (aSize == (int32_t)oldsize) {
    return true; // no change
  }

  if (aSize <= 0) {
    // free the array if allocated
    if (mImpl) {
      free(reinterpret_cast<char*>(mImpl));
      mImpl = nullptr;
    }
    return true;
  }

  if (mImpl) {
    // We currently own an array impl. Resize it appropriately.
    if (aSize < mImpl->mCount) {
      // XXX Note: we could also just resize to mCount
      return true;  // can't make it that small, ignore request
    }

    char* bytes = (char*)realloc(mImpl, SIZEOF_IMPL(aSize));
    Impl* newImpl = reinterpret_cast<Impl*>(bytes);
    if (!newImpl) {
      return false;
    }

#if DEBUG_VOIDARRAY
    if (mImpl == newImpl) {
      ADD_TO_STATS(GrowInPlace, oldsize);
    }
    ADD_TO_STATS(AllocedOfSize, SIZEOF_IMPL(aSize));
    if (aSize > mMaxSize) {
      ADD_TO_STATS(NumberOfSize, SIZEOF_IMPL(aSize));
      if (oldsize) {
        SUB_FROM_STATS(NumberOfSize, oldsize);
      }
      mMaxSize = aSize;
      if (mIsAuto) {
        ADD_TO_STATS(MaxAuto, SIZEOF_IMPL(aSize));
        SUB_FROM_STATS(MaxAuto, oldsize);
      }
    }
#endif
    SetArray(newImpl, aSize, newImpl->mCount);
    return true;
  }

  if ((uint32_t)aSize < oldsize) {
    // No point in allocating if it won't free the current Impl anyway.
    return true;
  }

  // just allocate an array
  // allocate the exact size requested
  char* bytes = (char*)malloc(SIZEOF_IMPL(aSize));
  Impl* newImpl = reinterpret_cast<Impl*>(bytes);
  if (!newImpl) {
    return false;
  }

#if DEBUG_VOIDARRAY
  ADD_TO_STATS(AllocedOfSize, SIZEOF_IMPL(aSize));
  if (aSize > mMaxSize) {
    ADD_TO_STATS(NumberOfSize, SIZEOF_IMPL(aSize));
    if (oldsize && !mImpl) {
      SUB_FROM_STATS(NumberOfSize, oldsize);
    }
    mMaxSize = aSize;
  }
#endif
  if (mImpl) {
#if DEBUG_VOIDARRAY
    ADD_TO_STATS(MaxAuto, SIZEOF_IMPL(aSize));
    SUB_FROM_STATS(MaxAuto, 0);
    SUB_FROM_STATS(NumberOfSize, 0);
    mIsAuto = true;
#endif
    // We must be growing an nsAutoVoidArray - copy since we didn't
    // realloc.
    memcpy(newImpl->mArray, mImpl->mArray,
           mImpl->mCount * sizeof(mImpl->mArray[0]));
  }

  SetArray(newImpl, aSize, mImpl ? mImpl->mCount : 0);
  // no memset; handled later in ReplaceElementAt if needed
  return true;
}

bool
nsVoidArray::GrowArrayBy(int32_t aGrowBy)
{
  // We have to grow the array. Grow by kMinGrowArrayBy slots if we're
  // smaller than kLinearThreshold bytes, or a power of two if we're
  // larger.  This is much more efficient with most memory allocators,
  // especially if it's very large, or of the allocator is binned.
  if (aGrowBy < kMinGrowArrayBy) {
    aGrowBy = kMinGrowArrayBy;
  }

  uint32_t newCapacity = GetArraySize() + aGrowBy;  // Minimum increase
  uint32_t newSize = SIZEOF_IMPL(newCapacity);

  if (newSize >= (uint32_t)kLinearThreshold) {
    // newCount includes enough space for at least kMinGrowArrayBy new
    // slots. Select the next power-of-two size in bytes above or
    // equal to that.
    // Also, limit the increase in size to about a VM page or two.
    if (GetArraySize() >= kMaxGrowArrayBy) {
      newCapacity = GetArraySize() + XPCOM_MAX(kMaxGrowArrayBy, aGrowBy);
      newSize = SIZEOF_IMPL(newCapacity);
    } else {
      newSize = mozilla::CeilingLog2(newSize);
      newCapacity = CAPACITYOF_IMPL(1u << newSize);
    }
  }
  // frees old mImpl IF this succeeds
  if (!SizeTo(newCapacity)) {
    return false;
  }

  return true;
}

nsVoidArray::nsVoidArray()
  : mImpl(nullptr)
{
  MOZ_COUNT_CTOR(nsVoidArray);
#if DEBUG_VOIDARRAY
  mMaxCount = 0;
  mMaxSize = 0;
  mIsAuto = false;
  ADD_TO_STATS(NumberOfSize, 0);
  MaxElements[0]++;
#endif
}

nsVoidArray::nsVoidArray(int32_t aCount)
  : mImpl(nullptr)
{
  MOZ_COUNT_CTOR(nsVoidArray);
#if DEBUG_VOIDARRAY
  mMaxCount = 0;
  mMaxSize = 0;
  mIsAuto = false;
  MaxElements[0]++;
#endif
  SizeTo(aCount);
}

nsVoidArray&
nsVoidArray::operator=(const nsVoidArray& aOther)
{
  int32_t otherCount = aOther.Count();
  int32_t maxCount = GetArraySize();
  if (otherCount) {
    if (otherCount > maxCount) {
      // frees old mImpl IF this succeeds
      if (!GrowArrayBy(otherCount - maxCount)) {
        return *this;      // XXX The allocation failed - don't do anything
      }

      memcpy(mImpl->mArray, aOther.mImpl->mArray,
             otherCount * sizeof(mImpl->mArray[0]));
      mImpl->mCount = otherCount;
    } else {
      // the old array can hold the new array
      memcpy(mImpl->mArray, aOther.mImpl->mArray,
             otherCount * sizeof(mImpl->mArray[0]));
      mImpl->mCount = otherCount;
      // if it shrank a lot, compact it anyways
      if ((otherCount * 2) < maxCount && maxCount > 100) {
        Compact();  // shrank by at least 50 entries
      }
    }
#if DEBUG_VOIDARRAY
    if (mImpl->mCount > mMaxCount &&
        mImpl->mCount < (int32_t)(sizeof(MaxElements) / sizeof(MaxElements[0]))) {
      MaxElements[mImpl->mCount]++;
      MaxElements[mMaxCount]--;
      mMaxCount = mImpl->mCount;
    }
#endif
  } else {
    // Why do we drop the buffer here when we don't in Clear()?
    SizeTo(0);
  }

  return *this;
}

nsVoidArray::~nsVoidArray()
{
  MOZ_COUNT_DTOR(nsVoidArray);
  if (mImpl) {
    free(reinterpret_cast<char*>(mImpl));
  }
}

bool
nsVoidArray::SetCount(int32_t aNewCount)
{
  NS_ASSERTION(aNewCount >= 0, "SetCount(negative index)");
  if (aNewCount < 0) {
    return false;
  }

  if (aNewCount == 0) {
    Clear();
    return true;
  }

  if (uint32_t(aNewCount) > uint32_t(GetArraySize())) {
    int32_t oldCount = Count();
    int32_t growDelta = aNewCount - oldCount;

    // frees old mImpl IF this succeeds
    if (!GrowArrayBy(growDelta)) {
      return false;
    }
  }

  if (aNewCount > mImpl->mCount) {
    // Make sure that new entries added to the array by this
    // SetCount are cleared to 0.  Some users of this assume that.
    // This code means we don't have to memset when we allocate an array.
    memset(&mImpl->mArray[mImpl->mCount], 0,
           (aNewCount - mImpl->mCount) * sizeof(mImpl->mArray[0]));
  }

  mImpl->mCount = aNewCount;

#if DEBUG_VOIDARRAY
  if (mImpl->mCount > mMaxCount &&
      mImpl->mCount < (int32_t)(sizeof(MaxElements) / sizeof(MaxElements[0]))) {
    MaxElements[mImpl->mCount]++;
    MaxElements[mMaxCount]--;
    mMaxCount = mImpl->mCount;
  }
#endif

  return true;
}

int32_t
nsVoidArray::IndexOf(void* aPossibleElement) const
{
  if (mImpl) {
    void** ap = mImpl->mArray;
    void** end = ap + mImpl->mCount;
    while (ap < end) {
      if (*ap == aPossibleElement) {
        return ap - mImpl->mArray;
      }
      ap++;
    }
  }
  return -1;
}

bool
nsVoidArray::InsertElementAt(void* aElement, int32_t aIndex)
{
  int32_t oldCount = Count();
  NS_ASSERTION(aIndex >= 0, "InsertElementAt(negative index)");
  if (uint32_t(aIndex) > uint32_t(oldCount)) {
    // An invalid index causes the insertion to fail
    // Invalid indexes are ones that add more than one entry to the
    // array (i.e., they can append).
    return false;
  }

  if (oldCount >= GetArraySize()) {
    if (!GrowArrayBy(1)) {
      return false;
    }
  }
  // else the array is already large enough

  int32_t slide = oldCount - aIndex;
  if (0 != slide) {
    // Slide data over to make room for the insertion
    memmove(mImpl->mArray + aIndex + 1, mImpl->mArray + aIndex,
            slide * sizeof(mImpl->mArray[0]));
  }

  mImpl->mArray[aIndex] = aElement;
  mImpl->mCount++;

#if DEBUG_VOIDARRAY
  if (mImpl->mCount > mMaxCount &&
      mImpl->mCount < (int32_t)(sizeof(MaxElements) / sizeof(MaxElements[0]))) {
    MaxElements[mImpl->mCount]++;
    MaxElements[mMaxCount]--;
    mMaxCount = mImpl->mCount;
  }
#endif

  return true;
}

bool
nsVoidArray::InsertElementsAt(const nsVoidArray& aOther, int32_t aIndex)
{
  int32_t oldCount = Count();
  int32_t otherCount = aOther.Count();

  NS_ASSERTION(aIndex >= 0, "InsertElementsAt(negative index)");
  if (uint32_t(aIndex) > uint32_t(oldCount)) {
    // An invalid index causes the insertion to fail
    // Invalid indexes are ones that are more than one entry past the end of
    // the array (i.e., they can append).
    return false;
  }

  if (oldCount + otherCount > GetArraySize()) {
    if (!GrowArrayBy(otherCount)) {
      return false;
    }
  }
  // else the array is already large enough

  int32_t slide = oldCount - aIndex;
  if (slide != 0) {
    // Slide data over to make room for the insertion
    memmove(mImpl->mArray + aIndex + otherCount, mImpl->mArray + aIndex,
            slide * sizeof(mImpl->mArray[0]));
  }

  for (int32_t i = 0; i < otherCount; ++i) {
    // copy all the elements (destroys aIndex)
    mImpl->mArray[aIndex++] = aOther.mImpl->mArray[i];
    mImpl->mCount++;
  }

#if DEBUG_VOIDARRAY
  if (mImpl->mCount > mMaxCount &&
      mImpl->mCount < (int32_t)(sizeof(MaxElements) / sizeof(MaxElements[0]))) {
    MaxElements[mImpl->mCount]++;
    MaxElements[mMaxCount]--;
    mMaxCount = mImpl->mCount;
  }
#endif

  return true;
}

bool
nsVoidArray::ReplaceElementAt(void* aElement, int32_t aIndex)
{
  NS_ASSERTION(aIndex >= 0, "ReplaceElementAt(negative index)");
  if (aIndex < 0) {
    return false;
  }

  // Unlike InsertElementAt, ReplaceElementAt can implicitly add more
  // than just the one element to the array.
  if (uint32_t(aIndex) >= uint32_t(GetArraySize())) {
    int32_t oldCount = Count();
    int32_t requestedCount = aIndex + 1;
    int32_t growDelta = requestedCount - oldCount;

    // frees old mImpl IF this succeeds
    if (!GrowArrayBy(growDelta)) {
      return false;
    }
  }

  mImpl->mArray[aIndex] = aElement;
  if (aIndex >= mImpl->mCount) {
    // Make sure that any entries implicitly added to the array by this
    // ReplaceElementAt are cleared to 0.  Some users of this assume that.
    // This code means we don't have to memset when we allocate an array.
    if (aIndex > mImpl->mCount) { // note: not >=
      // For example, if mCount is 2, and we do a ReplaceElementAt for
      // element[5], then we need to set three entries ([2], [3], and [4])
      // to 0.
      memset(&mImpl->mArray[mImpl->mCount], 0,
             (aIndex - mImpl->mCount) * sizeof(mImpl->mArray[0]));
    }

    mImpl->mCount = aIndex + 1;

#if DEBUG_VOIDARRAY
    if (mImpl->mCount > mMaxCount &&
        mImpl->mCount < (int32_t)(sizeof(MaxElements) / sizeof(MaxElements[0]))) {
      MaxElements[mImpl->mCount]++;
      MaxElements[mMaxCount]--;
      mMaxCount = mImpl->mCount;
    }
#endif
  }

  return true;
}

// useful for doing LRU arrays
bool
nsVoidArray::MoveElement(int32_t aFrom, int32_t aTo)
{
  void* tempElement;

  if (aTo == aFrom) {
    return true;
  }

  NS_ASSERTION(aTo >= 0 && aFrom >= 0, "MoveElement(negative index)");
  if (aTo >= Count() || aFrom >= Count()) {
    // can't extend the array when moving an element.  Also catches mImpl = null
    return false;
  }
  tempElement = mImpl->mArray[aFrom];

  if (aTo < aFrom) {
    // Moving one element closer to the head; the elements inbetween move down
    memmove(mImpl->mArray + aTo + 1, mImpl->mArray + aTo,
            (aFrom - aTo) * sizeof(mImpl->mArray[0]));
    mImpl->mArray[aTo] = tempElement;
  } else { // already handled aFrom == aTo
    // Moving one element closer to the tail; the elements inbetween move up
    memmove(mImpl->mArray + aFrom, mImpl->mArray + aFrom + 1,
            (aTo - aFrom) * sizeof(mImpl->mArray[0]));
    mImpl->mArray[aTo] = tempElement;
  }

  return true;
}

void
nsVoidArray::RemoveElementsAt(int32_t aIndex, int32_t aCount)
{
  int32_t oldCount = Count();
  NS_ASSERTION(aIndex >= 0, "RemoveElementsAt(negative index)");
  if (uint32_t(aIndex) >= uint32_t(oldCount)) {
    return;
  }
  // Limit to available entries starting at aIndex
  if (aCount + aIndex > oldCount) {
    aCount = oldCount - aIndex;
  }

  // We don't need to move any elements if we're removing the
  // last element in the array
  if (aIndex < (oldCount - aCount)) {
    memmove(mImpl->mArray + aIndex, mImpl->mArray + aIndex + aCount,
            (oldCount - (aIndex + aCount)) * sizeof(mImpl->mArray[0]));
  }

  mImpl->mCount -= aCount;
  return;
}

bool
nsVoidArray::RemoveElement(void* aElement)
{
  int32_t theIndex = IndexOf(aElement);
  if (theIndex != -1) {
    RemoveElementAt(theIndex);
    return true;
  }

  return false;
}

void
nsVoidArray::Clear()
{
  if (mImpl) {
    mImpl->mCount = 0;
  }
}

void
nsVoidArray::Compact()
{
  if (mImpl) {
    // XXX NOTE: this is quite inefficient in many cases if we're only
    // compacting by a little, but some callers care more about memory use.
    int32_t count = Count();
    if (GetArraySize() > count) {
      SizeTo(Count());
    }
  }
}

// Needed because we want to pass the pointer to the item in the array
// to the comparator function, not a pointer to the pointer in the array.
struct VoidArrayComparatorContext
{
  nsVoidArrayComparatorFunc mComparatorFunc;
  void* mData;
};

static int
VoidArrayComparator(const void* aElement1, const void* aElement2, void* aData)
{
  VoidArrayComparatorContext* ctx = static_cast<VoidArrayComparatorContext*>(aData);
  return (*ctx->mComparatorFunc)(*static_cast<void* const*>(aElement1),
                                 *static_cast<void* const*>(aElement2),
                                 ctx->mData);
}

void
nsVoidArray::Sort(nsVoidArrayComparatorFunc aFunc, void* aData)
{
  if (mImpl && mImpl->mCount > 1) {
    VoidArrayComparatorContext ctx = {aFunc, aData};
    NS_QuickSort(mImpl->mArray, mImpl->mCount, sizeof(mImpl->mArray[0]),
                 VoidArrayComparator, &ctx);
  }
}

bool
nsVoidArray::EnumerateForwards(nsVoidArrayEnumFunc aFunc, void* aData)
{
  int32_t index = -1;
  bool    running = true;

  if (mImpl) {
    while (running && (++index < mImpl->mCount)) {
      running = (*aFunc)(mImpl->mArray[index], aData);
    }
  }
  return running;
}

bool
nsVoidArray::EnumerateForwards(nsVoidArrayEnumFuncConst aFunc,
                               void* aData) const
{
  int32_t index = -1;
  bool    running = true;

  if (mImpl) {
    while (running && (++index < mImpl->mCount)) {
      running = (*aFunc)(mImpl->mArray[index], aData);
    }
  }
  return running;
}

bool
nsVoidArray::EnumerateBackwards(nsVoidArrayEnumFunc aFunc, void* aData)
{
  bool    running = true;

  if (mImpl) {
    int32_t index = Count();
    while (running && (--index >= 0)) {
      running = (*aFunc)(mImpl->mArray[index], aData);
    }
  }
  return running;
}

struct SizeOfElementIncludingThisData
{
  size_t mSize;
  nsVoidArraySizeOfElementIncludingThisFunc mSizeOfElementIncludingThis;
  mozilla::MallocSizeOf mMallocSizeOf;
  void* mData;  // the arg passed by the user
};

static bool
SizeOfElementIncludingThisEnumerator(const void* aElement, void* aData)
{
  SizeOfElementIncludingThisData* d = (SizeOfElementIncludingThisData*)aData;
  d->mSize += d->mSizeOfElementIncludingThis(aElement, d->mMallocSizeOf, d->mData);
  return true;
}

size_t
nsVoidArray::SizeOfExcludingThis(
  nsVoidArraySizeOfElementIncludingThisFunc aSizeOfElementIncludingThis,
  mozilla::MallocSizeOf aMallocSizeOf, void* aData) const
{
  size_t n = 0;
  // Measure the element storage.
  if (mImpl) {
    n += aMallocSizeOf(mImpl);
  }
  // Measure things pointed to by the elements.
  if (aSizeOfElementIncludingThis) {
    SizeOfElementIncludingThisData data2 =
    { 0, aSizeOfElementIncludingThis, aMallocSizeOf, aData };
    EnumerateForwards(SizeOfElementIncludingThisEnumerator, &data2);
    n += data2.mSize;
  }
  return n;
}

//----------------------------------------------------------------------
// NOTE: nsSmallVoidArray elements MUST all have the low bit as 0.
// This means that normally it's only used for pointers, and in particular
// structures or objects.
nsSmallVoidArray::~nsSmallVoidArray()
{
  if (HasSingle()) {
    // Have to null out mImpl before the nsVoidArray dtor runs.
    mImpl = nullptr;
  }
}

nsSmallVoidArray&
nsSmallVoidArray::operator=(nsSmallVoidArray& aOther)
{
  int32_t count = aOther.Count();
  switch (count) {
    case 0:
      Clear();
      break;
    case 1:
      Clear();
      AppendElement(aOther.ElementAt(0));
      break;
    default:
      if (GetArraySize() >= count || SizeTo(count)) {
        *AsArray() = *aOther.AsArray();
      }
  }

  return *this;
}

int32_t
nsSmallVoidArray::GetArraySize() const
{
  if (HasSingle()) {
    return 1;
  }

  return AsArray()->GetArraySize();
}

int32_t
nsSmallVoidArray::Count() const
{
  if (HasSingle()) {
    return 1;
  }

  return AsArray()->Count();
}

void*
nsSmallVoidArray::FastElementAt(int32_t aIndex) const
{
  NS_ASSERTION(aIndex >= 0 && aIndex < Count(),
               "nsSmallVoidArray::FastElementAt: index out of range");

  if (HasSingle()) {
    return GetSingle();
  }

  return AsArray()->FastElementAt(aIndex);
}

int32_t
nsSmallVoidArray::IndexOf(void* aPossibleElement) const
{
  if (HasSingle()) {
    return aPossibleElement == GetSingle() ? 0 : -1;
  }

  return AsArray()->IndexOf(aPossibleElement);
}

bool
nsSmallVoidArray::InsertElementAt(void* aElement, int32_t aIndex)
{
  NS_ASSERTION(!(NS_PTR_TO_INT32(aElement) & 0x1),
               "Attempt to add element with 0x1 bit set to nsSmallVoidArray");

  if (aIndex == 0 && IsEmpty()) {
    SetSingle(aElement);

    return true;
  }

  if (!EnsureArray()) {
    return false;
  }

  return AsArray()->InsertElementAt(aElement, aIndex);
}

bool
nsSmallVoidArray::InsertElementsAt(const nsVoidArray& aOther, int32_t aIndex)
{
#ifdef DEBUG
  for (int i = 0; i < aOther.Count(); ++i) {
    NS_ASSERTION(!(NS_PTR_TO_INT32(aOther.ElementAt(i)) & 0x1),
                 "Attempt to add element with 0x1 bit set to nsSmallVoidArray");
  }
#endif

  if (aIndex == 0 && IsEmpty() && aOther.Count() == 1) {
    SetSingle(aOther.FastElementAt(0));

    return true;
  }

  if (!EnsureArray()) {
    return false;
  }

  return AsArray()->InsertElementsAt(aOther, aIndex);
}

bool
nsSmallVoidArray::ReplaceElementAt(void* aElement, int32_t aIndex)
{
  NS_ASSERTION(!(NS_PTR_TO_INT32(aElement) & 0x1),
               "Attempt to add element with 0x1 bit set to nsSmallVoidArray");

  if (aIndex == 0 && (IsEmpty() || HasSingle())) {
    SetSingle(aElement);

    return true;
  }

  if (!EnsureArray()) {
    return false;
  }

  return AsArray()->ReplaceElementAt(aElement, aIndex);
}

bool
nsSmallVoidArray::AppendElement(void* aElement)
{
  NS_ASSERTION(!(NS_PTR_TO_INT32(aElement) & 0x1),
               "Attempt to add element with 0x1 bit set to nsSmallVoidArray");

  if (IsEmpty()) {
    SetSingle(aElement);

    return true;
  }

  if (!EnsureArray()) {
    return false;
  }

  return AsArray()->AppendElement(aElement);
}

bool
nsSmallVoidArray::RemoveElement(void* aElement)
{
  if (HasSingle()) {
    if (aElement == GetSingle()) {
      mImpl = nullptr;
      return true;
    }

    return false;
  }

  return AsArray()->RemoveElement(aElement);
}

void
nsSmallVoidArray::RemoveElementAt(int32_t aIndex)
{
  if (HasSingle()) {
    if (aIndex == 0) {
      mImpl = nullptr;
    }

    return;
  }

  AsArray()->RemoveElementAt(aIndex);
}

void
nsSmallVoidArray::RemoveElementsAt(int32_t aIndex, int32_t aCount)
{
  if (HasSingle()) {
    if (aIndex == 0) {
      if (aCount > 0) {
        mImpl = nullptr;
      }
    }

    return;
  }

  AsArray()->RemoveElementsAt(aIndex, aCount);
}

void
nsSmallVoidArray::Clear()
{
  if (HasSingle()) {
    mImpl = nullptr;
  } else {
    AsArray()->Clear();
  }
}

bool
nsSmallVoidArray::SizeTo(int32_t aMin)
{
  if (!HasSingle()) {
    return AsArray()->SizeTo(aMin);
  }

  if (aMin <= 0) {
    mImpl = nullptr;

    return true;
  }

  if (aMin == 1) {
    return true;
  }

  void* single = GetSingle();
  mImpl = nullptr;
  if (!AsArray()->SizeTo(aMin)) {
    SetSingle(single);

    return false;
  }

  AsArray()->AppendElement(single);

  return true;
}

void
nsSmallVoidArray::Compact()
{
  if (!HasSingle()) {
    AsArray()->Compact();
  }
}

void
nsSmallVoidArray::Sort(nsVoidArrayComparatorFunc aFunc, void* aData)
{
  if (!HasSingle()) {
    AsArray()->Sort(aFunc, aData);
  }
}

bool
nsSmallVoidArray::EnumerateForwards(nsVoidArrayEnumFunc aFunc, void* aData)
{
  if (HasSingle()) {
    return (*aFunc)(GetSingle(), aData);
  }
  return AsArray()->EnumerateForwards(aFunc, aData);
}

bool
nsSmallVoidArray::EnumerateBackwards(nsVoidArrayEnumFunc aFunc, void* aData)
{
  if (HasSingle()) {
    return (*aFunc)(GetSingle(), aData);
  }
  return AsArray()->EnumerateBackwards(aFunc, aData);
}

bool
nsSmallVoidArray::EnsureArray()
{
  if (!HasSingle()) {
    return true;
  }

  void* single = GetSingle();
  mImpl = nullptr;
  if (!AsArray()->AppendElement(single)) {
    SetSingle(single);

    return false;
  }

  return true;
}
back to top