diff --git a/xpcom/glue/nsVoidArray.cpp b/xpcom/glue/nsVoidArray.cpp index e004efdad208..bce13b7773a4 100644 --- a/xpcom/glue/nsVoidArray.cpp +++ b/xpcom/glue/nsVoidArray.cpp @@ -20,87 +20,313 @@ * Contributor(s): */ #include "nsVoidArray.h" +#include "prmem.h" #include "nsCRT.h" #include "nsISizeOfHandler.h" #include "nsString.h" -static const PRInt32 kGrowArrayBy = 4; -static const PRInt32 kLinearThreshold = 16; +// These get allocated a lot; it used to be 4. See bug 67618 +static const PRInt32 kGrowArrayBy = 8; +static const PRInt32 kLinearThreshold = 24 * sizeof(void *); static const PRInt32 kGrowthFactor = 1; -inline PRInt32 -nsVoidArray::GetArraySize() const +// because we use this all over +#define SIZEOF_IMPL(n) (sizeof(Impl) + sizeof(void *) * ((n) - 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() { - return mImpl ? PRInt32(mImpl->mBits & kArraySizeMask) : 0; + 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 + inline void -nsVoidArray::SetArraySize(PRInt32 aSize) +nsVoidArray::SetArray(Impl *newImpl, PRInt32 aSize, PRInt32 aCount, PRBool owner) { - NS_PRECONDITION(mImpl, "can't set size"); - mImpl->mBits &= ~kArraySizeMask; - mImpl->mBits |= PRUint32(aSize & kArraySizeMask); + // old mImpl has been realloced and so we don't free/delete it + NS_PRECONDITION(newImpl, "can't set size"); + mImpl = newImpl; + mImpl->mCount = aCount; + mImpl->mBits = PRUint32(aSize & kArraySizeMask) | + (owner ? kArrayOwnerMask : 0); } -inline PRBool -nsVoidArray::IsArrayOwner() const +// 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. +PRBool nsVoidArray::SizeTo(PRInt32 aSize) { - return mImpl ? PRBool(mImpl->mBits & kArrayOwnerMask) : PR_FALSE; + PRUint32 oldsize = GetArraySize(); + + if (aSize == (PRInt32) oldsize) + return PR_TRUE; // no change + + if (aSize <= 0) + { + // free the array if allocated + if (mImpl) + { + if (IsArrayOwner()) + { + PR_Free(NS_REINTERPRET_CAST(char *, mImpl)); + mImpl = nsnull; + } + else + { + mImpl->mCount = 0; // nsAutoVoidArray + } + } + return PR_TRUE; + } + + if (mImpl && IsArrayOwner()) + { + // We currently own an array impl. Resize it appropriately. + if (aSize < mImpl->mCount) + { + // XXX Note: we could also just resize to mCount + return PR_TRUE; // can't make it that small, ignore request + } + + char* bytes = (char *) PR_Realloc(mImpl,SIZEOF_IMPL(aSize)); + Impl* newImpl = NS_REINTERPRET_CAST(Impl*, bytes); + if (!newImpl) + return PR_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,PR_TRUE); + return PR_TRUE; + } + + // just allocate an array + // allocate the exact size requested + char* bytes = (char *) PR_Malloc(SIZEOF_IMPL(aSize)); + Impl* newImpl = NS_REINTERPRET_CAST(Impl*, bytes); + if (!newImpl) + return PR_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 = PR_TRUE; +#endif + // We must be growing an nsAutoVoidArray - copy since we didn't + // realloc. + nsCRT::memcpy(newImpl->mArray, mImpl->mArray, + mImpl->mCount * sizeof(mImpl->mArray[0])); + } + + SetArray(newImpl,aSize,mImpl ? mImpl->mCount : 0,PR_TRUE); + // no memset; handled later in ReplaceElementAt if needed + return PR_TRUE; } - -inline void -nsVoidArray::SetArrayOwner(PRBool aOwner) +PRBool nsVoidArray::GrowArrayBy(PRInt32 aGrowBy) { - NS_PRECONDITION(mImpl, "can't set owner"); - if (aOwner) - mImpl->mBits |= kArrayOwnerMask; - else - mImpl->mBits &= ~kArrayOwnerMask; + // We have to grow the array. Grow by kGrowArrayBy 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 < kGrowArrayBy) + aGrowBy = kGrowArrayBy; + + PRUint32 newCount = Count() + aGrowBy; // Minimum increase + PRUint32 newSize = SIZEOF_IMPL(newCount); + + if (newSize >= (PRUint32) kLinearThreshold) + { + // newCount includes enough space for at least kGrowArrayBy new slots. + // Select the next power-of-two size in bytes above that. + // It's painful to find the biggest 1 bit. We check for a + // power-of-two here, and then double if it is one. + PRUint32 oldSize = SIZEOF_IMPL(mImpl->mBits & kArraySizeMask); + + if ((oldSize & (oldSize-1)) == 0) // oldSize = 2^n for some n + { + newSize = oldSize << 1; // easy 2^(n+1) + } + else // count bits and stuff. + { + PRUint32 bits = 0; + while (newSize >>= 1) + { + bits++; + } + bits++; // bump to the next power of two; + newSize = 1 << bits; + } + // Make sure we have enough space -- the array can grow by a lot + while ((newSize - sizeof(Impl))/sizeof(mImpl->mArray[0]) + 1 < newCount) + newSize <<= 1; + + // inverse of equation above. + newCount = (newSize - sizeof(Impl))/sizeof(mImpl->mArray[0]) + 1; + } + // frees old mImpl IF this succeeds + if (!SizeTo(newCount)) + return PR_FALSE; + + return PR_TRUE; } nsVoidArray::nsVoidArray() : mImpl(nsnull) { MOZ_COUNT_CTOR(nsVoidArray); +#if DEBUG_VOIDARRAY + mMaxCount = 0; + mMaxSize = 0; + mIsAuto = PR_FALSE; + ADD_TO_STATS(NumberOfSize,0); + MaxElements[0]++; +#endif } nsVoidArray::nsVoidArray(PRInt32 aCount) : mImpl(nsnull) { MOZ_COUNT_CTOR(nsVoidArray); - if (aCount) { - char* bytes = new char[sizeof(Impl) + sizeof(void*) * (aCount - 1)]; - mImpl = NS_REINTERPRET_CAST(Impl*, bytes); - if (mImpl) { - mImpl->mBits = 0; - SetArraySize(aCount); - mImpl->mCount = aCount; - nsCRT::memset(mImpl->mArray, 0, mImpl->mCount * sizeof(void*)); - SetArrayOwner(PR_TRUE); - } - } +#if DEBUG_VOIDARRAY + mMaxCount = 0; + mMaxSize = 0; + mIsAuto = PR_FALSE; + MaxElements[0]++; +#endif + SizeTo(aCount); } nsVoidArray& nsVoidArray::operator=(const nsVoidArray& other) { - if (mImpl && IsArrayOwner()) - delete[] NS_REINTERPRET_CAST(char*, mImpl); - PRInt32 otherCount = other.Count(); - if (otherCount) { - char* bytes = new char[sizeof(Impl) + sizeof(void*) * (otherCount - 1)]; - mImpl = NS_REINTERPRET_CAST(Impl*, bytes); - if (mImpl) { - mImpl->mBits = 0; - SetArraySize(otherCount); - mImpl->mCount = otherCount; - SetArrayOwner(PR_TRUE); + PRInt32 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 - nsCRT::memcpy(mImpl->mArray, other.mImpl->mArray, mImpl->mCount * sizeof(void*)); + nsCRT::memcpy(mImpl->mArray, other.mImpl->mArray, otherCount * sizeof(mImpl->mArray[0])); + mImpl->mCount = otherCount; } + else + { + // the old array can hold the new array + nsCRT::memcpy(mImpl->mArray, other.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 < (PRInt32)(sizeof(MaxElements)/sizeof(MaxElements[0]))) + { + MaxElements[mImpl->mCount]++; + MaxElements[mMaxCount]--; + mMaxCount = mImpl->mCount; + } +#endif } - else { + else + { + if (mImpl && IsArrayOwner()) + PR_Free(NS_REINTERPRET_CAST(char*, mImpl)); + mImpl = nsnull; } @@ -111,20 +337,22 @@ nsVoidArray::~nsVoidArray() { MOZ_COUNT_DTOR(nsVoidArray); if (mImpl && IsArrayOwner()) - delete[] NS_REINTERPRET_CAST(char*, mImpl); + PR_Free(NS_REINTERPRET_CAST(char*, mImpl)); } void nsVoidArray::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const { - if (aResult) { - *aResult = sizeof(*this) + (mImpl ? sizeof(Impl) + (sizeof(void*) * (GetArraySize() - 1)) : 0); + if (aResult) + { + *aResult = sizeof(*this) + (mImpl ? SIZEOF_IMPL(GetArraySize()) : 0); } } -void* nsVoidArray::ElementAt(PRInt32 aIndex) const +inline void* nsVoidArray::ElementAt(PRInt32 aIndex) const { - if (aIndex < 0 || aIndex >= Count()) { + if (aIndex < 0 || aIndex >= Count()) + { return nsnull; } return mImpl->mArray[aIndex]; @@ -132,11 +360,14 @@ void* nsVoidArray::ElementAt(PRInt32 aIndex) const PRInt32 nsVoidArray::IndexOf(void* aPossibleElement) const { - if (mImpl) { + if (mImpl) + { void** ap = mImpl->mArray; void** end = ap + mImpl->mCount; - while (ap < end) { - if (*ap == aPossibleElement) { + while (ap < end) + { + if (*ap == aPossibleElement) + { return ap - mImpl->mArray; } ap++; @@ -148,157 +379,222 @@ PRInt32 nsVoidArray::IndexOf(void* aPossibleElement) const PRBool nsVoidArray::InsertElementAt(void* aElement, PRInt32 aIndex) { PRInt32 oldCount = Count(); - if (PRUint32(aIndex) > PRUint32(oldCount)) { + if (PRUint32(aIndex) > PRUint32(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 PR_FALSE; } - if (oldCount >= GetArraySize()) { - // We have to grow the array. Grow by 4 slots if we're smaller - // than 16 elements, or a power of two if we're larger. - PRInt32 newCount = oldCount + ((oldCount >= kLinearThreshold) ? (oldCount / kGrowthFactor) : kGrowArrayBy); - - char* bytes = new char[sizeof(Impl) + sizeof(void*) * (newCount - 1)]; - Impl* newImpl = NS_REINTERPRET_CAST(Impl*, bytes); - - if (newImpl) { - if (aIndex != 0) - nsCRT::memcpy(newImpl->mArray, mImpl->mArray, aIndex * sizeof(void*)); - - PRInt32 slide = oldCount - aIndex; - if (0 != slide) { - // Slide data over to make room for the insertion - nsCRT::memcpy(newImpl->mArray + aIndex + 1, mImpl->mArray + aIndex, - slide * sizeof(void*)); - } - - if (IsArrayOwner()) - delete[] NS_REINTERPRET_CAST(char*, mImpl); - - mImpl = newImpl; - mImpl->mBits = 0; - SetArraySize(newCount); - mImpl->mCount = oldCount; - SetArrayOwner(PR_TRUE); - } + if (oldCount >= GetArraySize()) + { + if (!GrowArrayBy(1)) + return PR_FALSE; } - else { - // The array is already large enough - PRInt32 slide = oldCount - aIndex; - if (0 != slide) { - // Slide data over to make room for the insertion - nsCRT::memmove(mImpl->mArray + aIndex + 1, mImpl->mArray + aIndex, - slide * sizeof(void*)); - } + // else the array is already large enough + + PRInt32 slide = oldCount - aIndex; + if (0 != slide) + { + // Slide data over to make room for the insertion + nsCRT::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 < (PRInt32)(sizeof(MaxElements)/sizeof(MaxElements[0]))) + { + MaxElements[mImpl->mCount]++; + MaxElements[mMaxCount]--; + mMaxCount = mImpl->mCount; + } +#endif + + return PR_TRUE; +} + +PRBool nsVoidArray::InsertElementsAt(const nsVoidArray& other, PRInt32 aIndex) +{ + PRInt32 oldCount = Count(); + PRInt32 otherCount = other.Count(); + + if (PRUint32(aIndex) > PRUint32(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 PR_FALSE; + } + + if (oldCount + otherCount > GetArraySize()) + { + if (!GrowArrayBy(otherCount)) + return PR_FALSE;; + } + // else the array is already large enough + + PRInt32 slide = oldCount - aIndex; + if (0 != slide) + { + // Slide data over to make room for the insertion + nsCRT::memmove(mImpl->mArray + aIndex + otherCount, mImpl->mArray + aIndex, + slide * sizeof(mImpl->mArray[0])); + } + + for (PRInt32 i = 0; i < otherCount; i++) + { + // copy all the elements (destroys aIndex) + mImpl->mArray[aIndex++] = other.mImpl->mArray[i]; + mImpl->mCount++; + } + +#if DEBUG_VOIDARRAY + if (mImpl->mCount > mMaxCount && + mImpl->mCount < (PRInt32)(sizeof(MaxElements)/sizeof(MaxElements[0]))) + { + MaxElements[mImpl->mCount]++; + MaxElements[mMaxCount]--; + mMaxCount = mImpl->mCount; + } +#endif + return PR_TRUE; } PRBool nsVoidArray::ReplaceElementAt(void* aElement, PRInt32 aIndex) { - if (PRUint32(aIndex) >= PRUint32(GetArraySize())) { + // Unlike InsertElementAt, ReplaceElementAt can implicitly add more + // than just the one element to the array. + if (PRUint32(aIndex) >= PRUint32(GetArraySize())) + { PRInt32 oldCount = Count(); PRInt32 requestedCount = aIndex + 1; PRInt32 growDelta = requestedCount - oldCount; - PRInt32 newCount = oldCount + (growDelta > kGrowArrayBy ? growDelta : kGrowArrayBy); - char* bytes = new char[sizeof(Impl) + sizeof(void*) * (newCount - 1)]; - Impl* newImpl = NS_REINTERPRET_CAST(Impl*, bytes); - - if (newImpl) { - nsCRT::memset(newImpl->mArray, 0, newCount * sizeof(void*)); - - if (mImpl != nsnull && aIndex != 0) - nsCRT::memcpy(newImpl->mArray, mImpl->mArray, mImpl->mCount * sizeof(void*)); - - if (IsArrayOwner()) - delete[] NS_REINTERPRET_CAST(char*, mImpl); - - mImpl = newImpl; - mImpl->mBits = 0; - SetArraySize(newCount); - mImpl->mCount = oldCount; - SetArrayOwner(PR_TRUE); - } + // frees old mImpl IF this succeeds + if (!GrowArrayBy(growDelta)) + return PR_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. + nsCRT::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 < (PRInt32)(sizeof(MaxElements)/sizeof(MaxElements[0]))) + { + MaxElements[mImpl->mCount]++; + MaxElements[mMaxCount]--; + mMaxCount = mImpl->mCount; + } +#endif + } + return PR_TRUE; } -PRBool nsVoidArray::RemoveElementAt(PRInt32 aIndex) +// useful for doing LRU arrays +PRBool nsVoidArray::MoveElement(PRInt32 aFrom, PRInt32 aTo) +{ + void *tempElement; + + if (aTo == aFrom) + return PR_TRUE; + + if (aTo < 0 || aFrom < 0 || aTo >= Count() || aFrom >= Count()) + { + // can't extend the array when moving an element. Also catches mImpl = null + return PR_FALSE; + } + tempElement = mImpl->mArray[aFrom]; + + if (aTo < aFrom) + { + // Moving one element closer to the head; the elements inbetween move down + nsCRT::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 + nsCRT::memmove(mImpl->mArray + aFrom, mImpl->mArray + aFrom + 1, + (aTo-aFrom) * sizeof(mImpl->mArray[0])); + mImpl->mArray[aTo] = tempElement; + } + + return PR_TRUE; +} + +PRBool nsVoidArray::RemoveElementsAt(PRInt32 aIndex, PRInt32 aCount) { PRInt32 oldCount = Count(); - if (PRUint32(aIndex) >= PRUint32(oldCount)) { + if (PRUint32(aIndex) >= PRUint32(oldCount)) + { // An invalid index causes the replace to fail return PR_FALSE; } + // 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 - 1)) { - nsCRT::memmove(mImpl->mArray + aIndex, mImpl->mArray + aIndex + 1, - (oldCount - 1 - aIndex) * sizeof(void*)); + if (aIndex < (oldCount - aCount)) + { + nsCRT::memmove(mImpl->mArray + aIndex, mImpl->mArray + aIndex + aCount, + (oldCount - (aIndex + aCount)) * sizeof(mImpl->mArray[0])); } - mImpl->mCount--; + mImpl->mCount -= aCount; return PR_TRUE; } PRBool nsVoidArray::RemoveElement(void* aElement) { - if (mImpl) { - void** ep = mImpl->mArray; - void** end = ep + mImpl->mCount; - while (ep < end) { - void* e = *ep++; - if (e == aElement) { - ep--; - return RemoveElementAt(PRInt32(ep - mImpl->mArray)); - } - } - } + PRInt32 theIndex = IndexOf(aElement); + if (theIndex != -1) + return RemoveElementAt(theIndex); + return PR_FALSE; } void nsVoidArray::Clear() { - if (mImpl) { + if (mImpl) + { mImpl->mCount = 0; } } void nsVoidArray::Compact() { - if (mImpl) { - PRInt32 count = mImpl->mCount; - if (IsArrayOwner() && GetArraySize() > count) { - Impl* doomedImpl = mImpl; - - if (count > 0) { - char* bytes = new char[sizeof(Impl) + sizeof(void*) * (count - 1)]; - Impl* newImpl = NS_REINTERPRET_CAST(Impl*, bytes); - if (newImpl) - nsCRT::memcpy(newImpl->mArray, mImpl->mArray, count * sizeof(void*)); - - mImpl = newImpl; - mImpl->mBits = 0; - SetArraySize(count); - mImpl->mCount = count; - SetArrayOwner(PR_TRUE); - } - else { - mImpl = nsnull; - } - - delete[] NS_REINTERPRET_CAST(char*, doomedImpl); + 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. + if (GetArraySize() > Count()) + { + SizeTo(Count()); } } } @@ -321,9 +617,10 @@ VoidArrayComparator(const void* aElement1, const void* aElement2, void* aData) void nsVoidArray::Sort(nsVoidArrayComparatorFunc aFunc, void* aData) { - if (mImpl && mImpl->mCount > 1) { + if (mImpl && mImpl->mCount > 1) + { VoidArrayComparatorContext ctx = {aFunc, aData}; - NS_QuickSort(mImpl->mArray, mImpl->mCount, sizeof(void*), + NS_QuickSort(mImpl->mArray, mImpl->mCount, sizeof(mImpl->mArray[0]), VoidArrayComparator, &ctx); } } @@ -333,8 +630,10 @@ PRBool nsVoidArray::EnumerateForwards(nsVoidArrayEnumFunc aFunc, void* aData) PRInt32 index = -1; PRBool running = PR_TRUE; - if (mImpl) { - while (running && (++index < mImpl->mCount)) { + if (mImpl) + { + while (running && (++index < mImpl->mCount)) + { running = (*aFunc)(mImpl->mArray[index], aData); } } @@ -345,9 +644,11 @@ PRBool nsVoidArray::EnumerateBackwards(nsVoidArrayEnumFunc aFunc, void* aData) { PRBool running = PR_TRUE; - if (mImpl) { + if (mImpl) + { PRInt32 index = Count(); - while (running && (0 <= --index)) { + while (running && (0 <= --index)) + { running = (*aFunc)(mImpl->mArray[index], aData); } } @@ -360,11 +661,45 @@ PRBool nsVoidArray::EnumerateBackwards(nsVoidArrayEnumFunc aFunc, void* aData) nsAutoVoidArray::nsAutoVoidArray() : nsVoidArray() { - mImpl = NS_REINTERPRET_CAST(Impl*, mAutoBuf); - mImpl->mBits = 0; - SetArraySize(kAutoBufSize); - mImpl->mCount = 0; - SetArrayOwner(PR_FALSE); + // Don't need to clear it. Some users just call ReplaceElementAt(), + // but we'll clear it at that time if needed to save CPU cycles. +#if DEBUG_VOIDARRAY + mIsAuto = PR_TRUE; + ADD_TO_STATS(MaxAuto,0); +#endif + SetArray(NS_REINTERPRET_CAST(Impl*, mAutoBuf),kAutoBufSize,0,PR_FALSE); +} + +void nsAutoVoidArray::Clear() +{ + // We don't have to free on Clear, but since we have a built-in buffer, + // it's worth considering. + nsVoidArray::Clear(); + if (IsArrayOwner() && GetArraySize() > 4*kAutoBufSize) + SizeTo(0); // we override CompactTo - delete and repoint at auto array +} + +PRBool nsAutoVoidArray::SizeTo(PRInt32 aSize) +{ + if (!nsVoidArray::SizeTo(aSize)) + return PR_FALSE; + + if (!mImpl) + { + // reset the array to point to our autobuf + SetArray(NS_REINTERPRET_CAST(Impl*, mAutoBuf),kAutoBufSize,0,PR_FALSE); + } + return PR_TRUE; +} + +void nsAutoVoidArray::Compact() +{ + nsVoidArray::Compact(); + if (!mImpl) + { + // reset the array to point to our autobuf + SetArray(NS_REINTERPRET_CAST(Impl*, mAutoBuf),kAutoBufSize,0,PR_FALSE); + } } //---------------------------------------------------------------- @@ -375,6 +710,11 @@ nsStringArray::nsStringArray(void) { } +nsStringArray::nsStringArray(PRInt32 aCount) + : nsVoidArray(aCount) +{ +} + nsStringArray::~nsStringArray(void) { Clear(); @@ -387,7 +727,8 @@ nsStringArray::operator=(const nsStringArray& other) nsVoidArray::operator=(other); // Now copy the strings - for (PRInt32 i = Count() - 1; i >= 0; --i) { + for (PRInt32 i = Count() - 1; i >= 0; --i) + { nsString* oldString = NS_STATIC_CAST(nsString*, other.ElementAt(i)); mImpl->mArray[i] = new nsString(*oldString); } @@ -401,7 +742,8 @@ nsStringArray::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const PRUint32 sum = 0; nsVoidArray::SizeOf(aHandler, &sum); PRInt32 index = Count(); - while (0 <= --index) { + while (0 <= --index) + { nsString* string = NS_STATIC_CAST(nsString*, ElementAt(index)); PRUint32 size; string->SizeOf(aHandler, &size); @@ -413,10 +755,12 @@ void nsStringArray::StringAt(PRInt32 aIndex, nsAWritableString& aString) const { nsString* string = NS_STATIC_CAST(nsString*, nsVoidArray::ElementAt(aIndex)); - if (nsnull != string) { + if (nsnull != string) + { aString.Assign(*string); } - else { + else + { aString.Truncate(); } } @@ -430,12 +774,15 @@ nsStringArray::StringAt(PRInt32 aIndex) const PRInt32 nsStringArray::IndexOf(const nsAReadableString& aPossibleString) const { - if (mImpl) { + if (mImpl) + { void** ap = mImpl->mArray; void** end = ap + mImpl->mCount; - while (ap < end) { + while (ap < end) + { nsString* string = NS_STATIC_CAST(nsString*, *ap); - if (string->Equals(aPossibleString)) { + if (string->Equals(aPossibleString)) + { return ap - mImpl->mArray; } ap++; @@ -447,12 +794,15 @@ nsStringArray::IndexOf(const nsAReadableString& aPossibleString) const PRInt32 nsStringArray::IndexOfIgnoreCase(const nsAReadableString& aPossibleString) const { - if (mImpl) { + if (mImpl) + { void** ap = mImpl->mArray; void** end = ap + mImpl->mCount; - while (ap < end) { + while (ap < end) + { nsString* string = NS_STATIC_CAST(nsString*, *ap); - if (!Compare(*string, aPossibleString, nsCaseInsensitiveStringComparator())) { + if (!Compare(*string, aPossibleString, nsCaseInsensitiveStringComparator())) + { return ap - mImpl->mArray; } ap++; @@ -465,7 +815,8 @@ PRBool nsStringArray::InsertStringAt(const nsAReadableString& aString, PRInt32 aIndex) { nsString* string = new nsString(aString); - if (nsVoidArray::InsertElementAt(string, aIndex)) { + if (nsVoidArray::InsertElementAt(string, aIndex)) + { return PR_TRUE; } delete string; @@ -477,7 +828,8 @@ nsStringArray::ReplaceStringAt(const nsAReadableString& aString, PRInt32 aIndex) { nsString* string = NS_STATIC_CAST(nsString*, nsVoidArray::ElementAt(aIndex)); - if (nsnull != string) { + if (nsnull != string) + { *string = aString; return PR_TRUE; } @@ -488,7 +840,8 @@ PRBool nsStringArray::RemoveString(const nsAReadableString& aString) { PRInt32 index = IndexOf(aString); - if (-1 < index) { + if (-1 < index) + { return RemoveStringAt(index); } return PR_FALSE; @@ -498,7 +851,8 @@ PRBool nsStringArray::RemoveStringIgnoreCase(const nsAReadableString& aString) { PRInt32 index = IndexOfIgnoreCase(aString); - if (-1 < index) { + if (-1 < index) + { return RemoveStringAt(index); } return PR_FALSE; @@ -507,7 +861,8 @@ nsStringArray::RemoveStringIgnoreCase(const nsAReadableString& aString) PRBool nsStringArray::RemoveStringAt(PRInt32 aIndex) { nsString* string = StringAt(aIndex); - if (nsnull != string) { + if (nsnull != string) + { nsVoidArray::RemoveElementAt(aIndex); delete string; return PR_TRUE; @@ -519,7 +874,8 @@ void nsStringArray::Clear(void) { PRInt32 index = Count(); - while (0 <= --index) { + while (0 <= --index) + { nsString* string = NS_STATIC_CAST(nsString*, mImpl->mArray[index]); delete string; } @@ -559,8 +915,10 @@ nsStringArray::EnumerateForwards(nsStringArrayEnumFunc aFunc, void* aData) PRInt32 index = -1; PRBool running = PR_TRUE; - if (mImpl) { - while (running && (++index < mImpl->mCount)) { + if (mImpl) + { + while (running && (++index < mImpl->mCount)) + { running = (*aFunc)(*NS_STATIC_CAST(nsString*, mImpl->mArray[index]), aData); } } @@ -573,8 +931,10 @@ nsStringArray::EnumerateBackwards(nsStringArrayEnumFunc aFunc, void* aData) PRInt32 index = Count(); PRBool running = PR_TRUE; - if (mImpl) { - while (running && (0 <= --index)) { + if (mImpl) + { + while (running && (0 <= --index)) + { running = (*aFunc)(*NS_STATIC_CAST(nsString*, mImpl->mArray[index]), aData); } } @@ -591,6 +951,11 @@ nsCStringArray::nsCStringArray(void) { } +nsCStringArray::nsCStringArray(PRInt32 aCount) + : nsVoidArray(aCount) +{ +} + nsCStringArray::~nsCStringArray(void) { Clear(); @@ -603,7 +968,8 @@ nsCStringArray::operator=(const nsCStringArray& other) nsVoidArray::operator=(other); // Now copy the strings - for (PRInt32 i = Count() - 1; i >= 0; --i) { + for (PRInt32 i = Count() - 1; i >= 0; --i) + { nsCString* oldString = NS_STATIC_CAST(nsCString*, other.ElementAt(i)); mImpl->mArray[i] = new nsCString(*oldString); } @@ -617,7 +983,8 @@ nsCStringArray::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const PRUint32 sum = 0; nsVoidArray::SizeOf(aHandler, &sum); PRInt32 index = Count(); - while (0 <= --index) { + while (0 <= --index) + { nsCString* string = NS_STATIC_CAST(nsCString*, mImpl->mArray[index]); PRUint32 size; string->SizeOf(aHandler, &size); @@ -629,10 +996,12 @@ void nsCStringArray::CStringAt(PRInt32 aIndex, nsCString& aCString) const { nsCString* string = NS_STATIC_CAST(nsCString*, nsVoidArray::ElementAt(aIndex)); - if (nsnull != string) { + if (nsnull != string) + { aCString = *string; } - else { + else + { aCString.Truncate(); } } @@ -646,12 +1015,15 @@ nsCStringArray::CStringAt(PRInt32 aIndex) const PRInt32 nsCStringArray::IndexOf(const nsCString& aPossibleString) const { - if (mImpl) { + if (mImpl) + { void** ap = mImpl->mArray; void** end = ap + mImpl->mCount; - while (ap < end) { + while (ap < end) + { nsCString* string = NS_STATIC_CAST(nsCString*, *ap); - if (string->Equals(aPossibleString)) { + if (string->Equals(aPossibleString)) + { return ap - mImpl->mArray; } ap++; @@ -663,12 +1035,15 @@ nsCStringArray::IndexOf(const nsCString& aPossibleString) const PRInt32 nsCStringArray::IndexOfIgnoreCase(const nsCString& aPossibleString) const { - if (mImpl) { + if (mImpl) + { void** ap = mImpl->mArray; void** end = ap + mImpl->mCount; - while (ap < end) { + while (ap < end) + { nsCString* string = NS_STATIC_CAST(nsCString*, *ap); - if (string->EqualsIgnoreCase(aPossibleString)) { + if (string->EqualsIgnoreCase(aPossibleString)) + { return ap - mImpl->mArray; } ap++; @@ -681,7 +1056,8 @@ PRBool nsCStringArray::InsertCStringAt(const nsCString& aCString, PRInt32 aIndex) { nsCString* string = new nsCString(aCString); - if (nsVoidArray::InsertElementAt(string, aIndex)) { + if (nsVoidArray::InsertElementAt(string, aIndex)) + { return PR_TRUE; } delete string; @@ -692,7 +1068,8 @@ PRBool nsCStringArray::ReplaceCStringAt(const nsCString& aCString, PRInt32 aIndex) { nsCString* string = NS_STATIC_CAST(nsCString*, nsVoidArray::ElementAt(aIndex)); - if (nsnull != string) { + if (nsnull != string) + { *string = aCString; return PR_TRUE; } @@ -703,7 +1080,8 @@ PRBool nsCStringArray::RemoveCString(const nsCString& aCString) { PRInt32 index = IndexOf(aCString); - if (-1 < index) { + if (-1 < index) + { return RemoveCStringAt(index); } return PR_FALSE; @@ -713,7 +1091,8 @@ PRBool nsCStringArray::RemoveCStringIgnoreCase(const nsCString& aCString) { PRInt32 index = IndexOfIgnoreCase(aCString); - if (-1 < index) { + if (-1 < index) + { return RemoveCStringAt(index); } return PR_FALSE; @@ -722,7 +1101,8 @@ nsCStringArray::RemoveCStringIgnoreCase(const nsCString& aCString) PRBool nsCStringArray::RemoveCStringAt(PRInt32 aIndex) { nsCString* string = CStringAt(aIndex); - if (nsnull != string) { + if (nsnull != string) + { nsVoidArray::RemoveElementAt(aIndex); delete string; return PR_TRUE; @@ -734,7 +1114,8 @@ void nsCStringArray::Clear(void) { PRInt32 index = Count(); - while (0 <= --index) { + while (0 <= --index) + { nsCString* string = NS_STATIC_CAST(nsCString*, mImpl->mArray[index]); delete string; } @@ -773,9 +1154,11 @@ nsCStringArray::EnumerateForwards(nsCStringArrayEnumFunc aFunc, void* aData) { PRBool running = PR_TRUE; - if (mImpl) { + if (mImpl) + { PRInt32 index = -1; - while (running && (++index < mImpl->mCount)) { + while (running && (++index < mImpl->mCount)) + { running = (*aFunc)(*NS_STATIC_CAST(nsCString*, mImpl->mArray[index]), aData); } } @@ -787,9 +1170,11 @@ nsCStringArray::EnumerateBackwards(nsCStringArrayEnumFunc aFunc, void* aData) { PRBool running = PR_TRUE; - if (mImpl) { + if (mImpl) + { PRInt32 index = Count(); - while (running && (0 <= --index)) { + while (running && (0 <= --index)) + { running = (*aFunc)(*NS_STATIC_CAST(nsCString*, mImpl->mArray[index]), aData); } } diff --git a/xpcom/glue/nsVoidArray.h b/xpcom/glue/nsVoidArray.h index 9df167717942..92617644e8a5 100644 --- a/xpcom/glue/nsVoidArray.h +++ b/xpcom/glue/nsVoidArray.h @@ -23,6 +23,8 @@ #ifndef nsVoidArray_h___ #define nsVoidArray_h___ +//#define DEBUG_VOIDARRAY 1 + #include "nscore.h" #include "nsAWritableString.h" #include "nsQuickSort.h" @@ -45,11 +47,15 @@ public: nsVoidArray& operator=(const nsVoidArray& other); - void SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const; + virtual void SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const; - PRInt32 Count() const { + inline PRInt32 Count() const { return mImpl ? mImpl->mCount : 0; } + // returns the max number that can be held without allocating + inline PRInt32 GetArraySize() const { + return mImpl ? PRInt32(mImpl->mBits & kArraySizeMask) : 0; + } void* ElementAt(PRInt32 aIndex) const; void* operator[](PRInt32 aIndex) const { return ElementAt(aIndex); } @@ -57,18 +63,27 @@ public: PRInt32 IndexOf(void* aPossibleElement) const; PRBool InsertElementAt(void* aElement, PRInt32 aIndex); + PRBool InsertElementsAt(const nsVoidArray &other, PRInt32 aIndex); PRBool ReplaceElementAt(void* aElement, PRInt32 aIndex); + // useful for doing LRU arrays, sorting, etc + PRBool MoveElement(PRInt32 aFrom, PRInt32 aTo); + PRBool AppendElement(void* aElement) { return InsertElementAt(aElement, Count()); } PRBool RemoveElement(void* aElement); - PRBool RemoveElementAt(PRInt32 aIndex); - void Clear(); + PRBool RemoveElementsAt(PRInt32 aIndex, PRInt32 aCount); + PRBool RemoveElementAt(PRInt32 aIndex) { return RemoveElementsAt(aIndex,1); } - void Compact(); + virtual void Clear(); + + virtual PRBool SizeTo(PRInt32 aMin); + // Subtly different - Compact() tries to be smart about whether we + // should reallocate the array; SizeTo() just does it. + virtual void Compact(); void Sort(nsVoidArrayComparatorFunc aFunc, void* aData); @@ -76,10 +91,12 @@ public: PRBool EnumerateBackwards(nsVoidArrayEnumFunc aFunc, void* aData); protected: + virtual PRBool GrowArrayBy(PRInt32 aGrowBy); + struct Impl { /** - * Packed bits. The highest 31 bits are the array's size, which must - * always be 0 mod 2. The lowest bit is a flag that indicates + * Packed bits. The low 31 bits are the array's size. + * The highest bit is a flag that indicates * whether or not we "own" mArray, and must free() it when * destroyed. */ @@ -97,6 +114,11 @@ protected: }; Impl* mImpl; +#if DEBUG_VOIDARRAY + PRInt32 mMaxCount; + PRInt32 mMaxSize; + PRBool mIsAuto; +#endif enum { kArrayOwnerMask = 1 << 31, @@ -105,10 +127,10 @@ protected: // bit twiddlers - PRInt32 GetArraySize() const; - void SetArraySize(PRInt32 aSize); - PRBool IsArrayOwner() const; - void SetArrayOwner(PRBool aOwner); + void SetArray(Impl *newImpl, PRInt32 aSize, PRInt32 aCount, PRBool owner); + inline PRBool IsArrayOwner() const { + return mImpl ? PRBool(mImpl->mBits & kArrayOwnerMask) : PR_FALSE; + } private: /// Copy constructors are not allowed @@ -120,10 +142,13 @@ private: class NS_COM nsAutoVoidArray : public nsVoidArray { public: nsAutoVoidArray(); + void Clear(); + virtual PRBool SizeTo(PRInt32 aMin); + virtual void Compact(); + protected: - // The internal storage. Note that this value must be divisible by - // two because we use the LSB of mInfo to indicate array ownership. + // The internal storage enum { kAutoBufSize = 8 }; char mAutoBuf[sizeof(Impl) + (kAutoBufSize - 1) * sizeof(void*)]; }; @@ -140,6 +165,7 @@ class NS_COM nsStringArray: protected nsVoidArray { public: nsStringArray(void); + nsStringArray(PRInt32 aCount); // Storage for aCount elements will be pre-allocated virtual ~nsStringArray(void); nsStringArray& operator=(const nsStringArray& other); @@ -198,6 +224,7 @@ class NS_COM nsCStringArray: protected nsVoidArray { public: nsCStringArray(void); + nsCStringArray(PRInt32 aCount); // Storage for aCount elements will be pre-allocated virtual ~nsCStringArray(void); nsCStringArray& operator=(const nsCStringArray& other);