michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include "mozilla/MathAlgorithms.h" michael@0: #include "nsSupportsArray.h" michael@0: #include "nsSupportsArrayEnumerator.h" michael@0: #include "nsIObjectInputStream.h" michael@0: #include "nsIObjectOutputStream.h" michael@0: michael@0: #if DEBUG_SUPPORTSARRAY michael@0: #define MAXSUPPORTS 20 michael@0: michael@0: class SupportsStats { michael@0: public: michael@0: SupportsStats(); michael@0: ~SupportsStats(); michael@0: michael@0: }; michael@0: michael@0: static int sizesUsed; // number of the elements of the arrays used michael@0: static int sizesAlloced[MAXSUPPORTS]; // sizes of the allocations. sorted michael@0: static int NumberOfSize[MAXSUPPORTS]; // number of this allocation size (1 per array) michael@0: static int AllocedOfSize[MAXSUPPORTS]; // number of this allocation size (each size for array used) michael@0: static int GrowInPlace[MAXSUPPORTS]; michael@0: michael@0: // these are per-allocation michael@0: static int MaxElements[3000]; michael@0: michael@0: // very evil michael@0: #define ADD_TO_STATS(x,size) do {int i; for (i = 0; i < sizesUsed; i++) \ michael@0: { \ michael@0: if (sizesAlloced[i] == (int)(size)) \ michael@0: { ((x)[i])++; break; } \ michael@0: } \ michael@0: if (i >= sizesUsed && sizesUsed < MAXSUPPORTS) \ michael@0: { sizesAlloced[sizesUsed] = (size); \ michael@0: ((x)[sizesUsed++])++; break; \ michael@0: } \ michael@0: } while (0); michael@0: michael@0: #define SUB_FROM_STATS(x,size) do {int i; for (i = 0; i < sizesUsed; i++) \ michael@0: { \ michael@0: if (sizesAlloced[i] == (int)(size)) \ michael@0: { ((x)[i])--; break; } \ michael@0: } \ michael@0: } while (0); michael@0: michael@0: michael@0: SupportsStats::SupportsStats() michael@0: { michael@0: sizesUsed = 1; michael@0: sizesAlloced[0] = 0; michael@0: } michael@0: michael@0: SupportsStats::~SupportsStats() michael@0: { michael@0: int i; michael@0: for (i = 0; i < sizesUsed; i++) michael@0: { michael@0: printf("Size %d:\n",sizesAlloced[i]); michael@0: printf("\tNumber of SupportsArrays this size (max): %d\n",NumberOfSize[i]); michael@0: printf("\tNumber of allocations this size (total): %d\n",AllocedOfSize[i]); michael@0: printf("\tNumber of GrowsInPlace this size (total): %d\n",GrowInPlace[i]); michael@0: } michael@0: printf("Max Size of SupportsArray:\n"); michael@0: for (i = 0; i < (int)(sizeof(MaxElements)/sizeof(MaxElements[0])); i++) michael@0: { michael@0: if (MaxElements[i]) michael@0: printf("\t%d: %d\n",i,MaxElements[i]); michael@0: } michael@0: } michael@0: michael@0: // Just so constructor/destructor get called michael@0: SupportsStats gSupportsStats; michael@0: #endif michael@0: michael@0: nsresult michael@0: nsQueryElementAt::operator()( const nsIID& aIID, void** aResult ) const michael@0: { michael@0: nsresult status = mCollection michael@0: ? mCollection->QueryElementAt(mIndex, aIID, aResult) michael@0: : NS_ERROR_NULL_POINTER; michael@0: michael@0: if ( mErrorPtr ) michael@0: *mErrorPtr = status; michael@0: michael@0: return status; michael@0: } michael@0: michael@0: static const int32_t kGrowArrayBy = 8; michael@0: static const int32_t kLinearThreshold = 16 * sizeof(nsISupports *); michael@0: michael@0: nsSupportsArray::nsSupportsArray() michael@0: { michael@0: mArray = mAutoArray; michael@0: mArraySize = kAutoArraySize; michael@0: mCount = 0; michael@0: #if DEBUG_SUPPORTSARRAY michael@0: mMaxCount = 0; michael@0: mMaxSize = 0; michael@0: ADD_TO_STATS(NumberOfSize,kAutoArraySize*sizeof(mArray[0])); michael@0: MaxElements[0]++; michael@0: #endif michael@0: } michael@0: michael@0: nsSupportsArray::~nsSupportsArray() michael@0: { michael@0: DeleteArray(); michael@0: } michael@0: michael@0: void nsSupportsArray::GrowArrayBy(int32_t aGrowBy) michael@0: { michael@0: // We have to grow the array. Grow by kGrowArrayBy slots if we're smaller michael@0: // than kLinearThreshold bytes, or a power of two if we're larger. michael@0: // This is much more efficient with most memory allocators, especially michael@0: // if it's very large, or of the allocator is binned. michael@0: if (aGrowBy < kGrowArrayBy) michael@0: aGrowBy = kGrowArrayBy; michael@0: michael@0: uint32_t newCount = mArraySize + aGrowBy; // Minimum increase michael@0: uint32_t newSize = sizeof(mArray[0]) * newCount; michael@0: michael@0: if (newSize >= (uint32_t) kLinearThreshold) michael@0: { michael@0: // newCount includes enough space for at least kGrowArrayBy new slots. michael@0: // Select the next power-of-two size in bytes above that if newSize is michael@0: // not a power of two. michael@0: if (newSize & (newSize - 1)) michael@0: newSize = 1u << mozilla::CeilingLog2(newSize); michael@0: michael@0: newCount = newSize / sizeof(mArray[0]); michael@0: } michael@0: // XXX This would be far more efficient in many allocators if we used michael@0: // XXX PR_Realloc(), etc michael@0: nsISupports** oldArray = mArray; michael@0: michael@0: mArray = new nsISupports*[newCount]; michael@0: mArraySize = newCount; michael@0: michael@0: #if DEBUG_SUPPORTSARRAY michael@0: if (oldArray == mArray) // can't happen without use of realloc michael@0: ADD_TO_STATS(GrowInPlace,mCount); michael@0: ADD_TO_STATS(AllocedOfSize,mArraySize*sizeof(mArray[0])); michael@0: if (mArraySize > mMaxSize) michael@0: { michael@0: ADD_TO_STATS(NumberOfSize,mArraySize*sizeof(mArray[0])); michael@0: if (oldArray != &(mAutoArray[0])) michael@0: SUB_FROM_STATS(NumberOfSize,mCount*sizeof(mArray[0])); michael@0: mMaxSize = mArraySize; michael@0: } michael@0: #endif michael@0: if (oldArray) { // need to move old data michael@0: if (0 < mCount) { michael@0: ::memcpy(mArray, oldArray, mCount * sizeof(nsISupports*)); michael@0: } michael@0: if (oldArray != &(mAutoArray[0])) { michael@0: delete[] oldArray; michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsSupportsArray::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) michael@0: { michael@0: if (aOuter) michael@0: return NS_ERROR_NO_AGGREGATION; michael@0: michael@0: nsCOMPtr it = new nsSupportsArray(); michael@0: michael@0: return it->QueryInterface(aIID, aResult); michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsSupportsArray, nsISupportsArray, nsICollection, nsISerializable) michael@0: michael@0: NS_IMETHODIMP michael@0: nsSupportsArray::Read(nsIObjectInputStream *aStream) michael@0: { michael@0: nsresult rv; michael@0: michael@0: uint32_t newArraySize; michael@0: rv = aStream->Read32(&newArraySize); michael@0: michael@0: if (newArraySize <= kAutoArraySize) { michael@0: if (mArray != mAutoArray) { michael@0: delete[] mArray; michael@0: mArray = mAutoArray; michael@0: } michael@0: newArraySize = kAutoArraySize; michael@0: } michael@0: else { michael@0: if (newArraySize <= mArraySize) { michael@0: // Keep non-default-size mArray, it's more than big enough. michael@0: newArraySize = mArraySize; michael@0: } michael@0: else { michael@0: nsISupports** array = new nsISupports*[newArraySize]; michael@0: if (mArray != mAutoArray) michael@0: delete[] mArray; michael@0: mArray = array; michael@0: } michael@0: } michael@0: mArraySize = newArraySize; michael@0: michael@0: rv = aStream->Read32(&mCount); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: NS_ASSERTION(mCount <= mArraySize, "overlarge mCount!"); michael@0: if (mCount > mArraySize) michael@0: mCount = mArraySize; michael@0: michael@0: for (uint32_t i = 0; i < mCount; i++) { michael@0: rv = aStream->ReadObject(true, &mArray[i]); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSupportsArray::Write(nsIObjectOutputStream *aStream) michael@0: { michael@0: nsresult rv; michael@0: michael@0: rv = aStream->Write32(mArraySize); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = aStream->Write32(mCount); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: for (uint32_t i = 0; i < mCount; i++) { michael@0: rv = aStream->WriteObject(mArray[i], true); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void nsSupportsArray::DeleteArray(void) michael@0: { michael@0: Clear(); michael@0: if (mArray != &(mAutoArray[0])) { michael@0: delete[] mArray; michael@0: mArray = mAutoArray; michael@0: mArraySize = kAutoArraySize; michael@0: } michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP_(bool) michael@0: nsSupportsArray::Equals(const nsISupportsArray* aOther) michael@0: { michael@0: if (aOther) { michael@0: uint32_t countOther; michael@0: nsISupportsArray* other = const_cast(aOther); michael@0: nsresult rv = other->Count(&countOther); michael@0: if (NS_FAILED( rv )) michael@0: return false; michael@0: michael@0: if (mCount == countOther) { michael@0: uint32_t index = mCount; michael@0: nsCOMPtr otherElem; michael@0: while (index--) { michael@0: if (NS_FAILED(other->GetElementAt(index, getter_AddRefs(otherElem)))) michael@0: return false; michael@0: if (mArray[index] != otherElem) michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSupportsArray::GetElementAt(uint32_t aIndex, nsISupports **aOutPtr) michael@0: { michael@0: *aOutPtr = nullptr; michael@0: if (aIndex < mCount) { michael@0: NS_IF_ADDREF(*aOutPtr = mArray[aIndex]); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(int32_t) michael@0: nsSupportsArray::IndexOf(const nsISupports* aPossibleElement) michael@0: { michael@0: return IndexOfStartingAt(aPossibleElement, 0); michael@0: } michael@0: michael@0: NS_IMETHODIMP_(int32_t) michael@0: nsSupportsArray::IndexOfStartingAt(const nsISupports* aPossibleElement, michael@0: uint32_t aStartIndex) michael@0: { michael@0: if (aStartIndex < mCount) { michael@0: const nsISupports** start = (const nsISupports**)mArray; // work around goofy compiler behavior michael@0: const nsISupports** ep = (start + aStartIndex); michael@0: const nsISupports** end = (start + mCount); michael@0: while (ep < end) { michael@0: if (aPossibleElement == *ep) { michael@0: return (ep - start); michael@0: } michael@0: ep++; michael@0: } michael@0: } michael@0: return -1; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(int32_t) michael@0: nsSupportsArray::LastIndexOf(const nsISupports* aPossibleElement) michael@0: { michael@0: if (0 < mCount) { michael@0: const nsISupports** start = (const nsISupports**)mArray; // work around goofy compiler behavior michael@0: const nsISupports** ep = (start + mCount); michael@0: while (start <= --ep) { michael@0: if (aPossibleElement == *ep) { michael@0: return (ep - start); michael@0: } michael@0: } michael@0: } michael@0: return -1; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(bool) michael@0: nsSupportsArray::InsertElementAt(nsISupports* aElement, uint32_t aIndex) michael@0: { michael@0: if (aIndex <= mCount) { michael@0: if (mArraySize < (mCount + 1)) { michael@0: // need to grow the array michael@0: GrowArrayBy(1); michael@0: } michael@0: michael@0: // Could be slightly more efficient if GrowArrayBy knew about the michael@0: // split, but the difference is trivial. michael@0: uint32_t slide = (mCount - aIndex); michael@0: if (0 < slide) { michael@0: ::memmove(mArray + aIndex + 1, mArray + aIndex, slide * sizeof(nsISupports*)); michael@0: } michael@0: michael@0: mArray[aIndex] = aElement; michael@0: NS_IF_ADDREF(aElement); michael@0: mCount++; michael@0: michael@0: #if DEBUG_SUPPORTSARRAY michael@0: if (mCount > mMaxCount && michael@0: mCount < (int32_t)(sizeof(MaxElements)/sizeof(MaxElements[0]))) michael@0: { michael@0: MaxElements[mCount]++; michael@0: MaxElements[mMaxCount]--; michael@0: mMaxCount = mCount; michael@0: } michael@0: #endif michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(bool) michael@0: nsSupportsArray::InsertElementsAt(nsISupportsArray* aElements, uint32_t aIndex) michael@0: { michael@0: if (!aElements) { michael@0: return false; michael@0: } michael@0: uint32_t countElements; michael@0: if (NS_FAILED( aElements->Count( &countElements ) )) michael@0: return false; michael@0: michael@0: if (aIndex <= mCount) { michael@0: if (mArraySize < (mCount + countElements)) { michael@0: // need to grow the array michael@0: GrowArrayBy(countElements); michael@0: } michael@0: michael@0: // Could be slightly more efficient if GrowArrayBy knew about the michael@0: // split, but the difference is trivial. michael@0: uint32_t slide = (mCount - aIndex); michael@0: if (0 < slide) { michael@0: ::memmove(mArray + aIndex + countElements, mArray + aIndex, michael@0: slide * sizeof(nsISupports*)); michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < countElements; ++i, ++mCount) { michael@0: // use GetElementAt to copy and do AddRef for us michael@0: if (NS_FAILED( aElements->GetElementAt( i, mArray + aIndex + i) )) michael@0: return false; michael@0: } michael@0: michael@0: #if DEBUG_SUPPORTSARRAY michael@0: if (mCount > mMaxCount && michael@0: mCount < (int32_t)(sizeof(MaxElements)/sizeof(MaxElements[0]))) michael@0: { michael@0: MaxElements[mCount]++; michael@0: MaxElements[mMaxCount]--; michael@0: mMaxCount = mCount; michael@0: } michael@0: #endif michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(bool) michael@0: nsSupportsArray::ReplaceElementAt(nsISupports* aElement, uint32_t aIndex) michael@0: { michael@0: if (aIndex < mCount) { michael@0: NS_IF_ADDREF(aElement); // addref first in case it's the same object! michael@0: NS_IF_RELEASE(mArray[aIndex]); michael@0: mArray[aIndex] = aElement; michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(bool) michael@0: nsSupportsArray::RemoveElementsAt(uint32_t aIndex, uint32_t aCount) michael@0: { michael@0: if (aIndex + aCount <= mCount) { michael@0: for (uint32_t i = 0; i < aCount; i++) michael@0: NS_IF_RELEASE(mArray[aIndex+i]); michael@0: mCount -= aCount; michael@0: int32_t slide = (mCount - aIndex); michael@0: if (0 < slide) { michael@0: ::memmove(mArray + aIndex, mArray + aIndex + aCount, michael@0: slide * sizeof(nsISupports*)); michael@0: } michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(bool) michael@0: nsSupportsArray::RemoveElement(const nsISupports* aElement, uint32_t aStartIndex) michael@0: { michael@0: int32_t theIndex = IndexOfStartingAt(aElement,aStartIndex); michael@0: if (theIndex >= 0) michael@0: return RemoveElementAt(theIndex); michael@0: michael@0: return false; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(bool) michael@0: nsSupportsArray::RemoveLastElement(const nsISupports* aElement) michael@0: { michael@0: int32_t theIndex = LastIndexOf(aElement); michael@0: if (theIndex >= 0) michael@0: return RemoveElementAt(theIndex); michael@0: michael@0: return false; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(bool) michael@0: nsSupportsArray::MoveElement(int32_t aFrom, int32_t aTo) michael@0: { michael@0: nsISupports *tempElement; michael@0: michael@0: if (aTo == aFrom) michael@0: return true; michael@0: michael@0: if (aTo < 0 || aFrom < 0 || michael@0: (uint32_t) aTo >= mCount || (uint32_t) aFrom >= mCount) michael@0: { michael@0: // can't extend the array when moving an element. Also catches mImpl = null michael@0: return false; michael@0: } michael@0: tempElement = mArray[aFrom]; michael@0: michael@0: if (aTo < aFrom) michael@0: { michael@0: // Moving one element closer to the head; the elements inbetween move down michael@0: ::memmove(mArray + aTo + 1, mArray + aTo, michael@0: (aFrom-aTo) * sizeof(mArray[0])); michael@0: mArray[aTo] = tempElement; michael@0: } michael@0: else // already handled aFrom == aTo michael@0: { michael@0: // Moving one element closer to the tail; the elements inbetween move up michael@0: ::memmove(mArray + aFrom, mArray + aFrom + 1, michael@0: (aTo-aFrom) * sizeof(mArray[0])); michael@0: mArray[aTo] = tempElement; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSupportsArray::Clear(void) michael@0: { michael@0: if (0 < mCount) { michael@0: do { michael@0: --mCount; michael@0: NS_IF_RELEASE(mArray[mCount]); michael@0: } while (0 != mCount); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSupportsArray::Compact(void) michael@0: { michael@0: #if DEBUG_SUPPORTSARRAY michael@0: uint32_t oldArraySize = mArraySize; michael@0: #endif michael@0: if ((mArraySize != mCount) && (kAutoArraySize < mArraySize)) { michael@0: nsISupports** oldArray = mArray; michael@0: if (mCount <= kAutoArraySize) { michael@0: mArray = mAutoArray; michael@0: mArraySize = kAutoArraySize; michael@0: } michael@0: else { michael@0: mArray = new nsISupports*[mCount]; michael@0: if (!mArray) { michael@0: mArray = oldArray; michael@0: return NS_OK; michael@0: } michael@0: mArraySize = mCount; michael@0: } michael@0: #if DEBUG_SUPPORTSARRAY michael@0: if (oldArray == mArray && michael@0: oldArray != &(mAutoArray[0])) // can't happen without use of realloc michael@0: ADD_TO_STATS(GrowInPlace,oldArraySize); michael@0: if (oldArray != &(mAutoArray[0])) michael@0: ADD_TO_STATS(AllocedOfSize,mArraySize*sizeof(mArray[0])); michael@0: #endif michael@0: ::memcpy(mArray, oldArray, mCount * sizeof(nsISupports*)); michael@0: delete[] oldArray; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(bool) michael@0: nsSupportsArray::SizeTo(int32_t aSize) michael@0: { michael@0: #if DEBUG_SUPPORTSARRAY michael@0: uint32_t oldArraySize = mArraySize; michael@0: #endif michael@0: NS_ASSERTION(aSize >= 0, "negative aSize!"); michael@0: michael@0: // XXX for aSize < mCount we could resize to mCount michael@0: if (mArraySize == (uint32_t) aSize || (uint32_t) aSize < mCount) michael@0: return true; // nothing to do michael@0: michael@0: // switch back to autoarray if possible michael@0: nsISupports** oldArray = mArray; michael@0: if ((uint32_t) aSize <= kAutoArraySize) { michael@0: mArray = mAutoArray; michael@0: mArraySize = kAutoArraySize; michael@0: } michael@0: else { michael@0: mArray = new nsISupports*[aSize]; michael@0: if (!mArray) { michael@0: mArray = oldArray; michael@0: return false; michael@0: } michael@0: mArraySize = aSize; michael@0: } michael@0: #if DEBUG_SUPPORTSARRAY michael@0: if (oldArray == mArray && michael@0: oldArray != &(mAutoArray[0])) // can't happen without use of realloc michael@0: ADD_TO_STATS(GrowInPlace,oldArraySize); michael@0: if (oldArray != &(mAutoArray[0])) michael@0: ADD_TO_STATS(AllocedOfSize,mArraySize*sizeof(mArray[0])); michael@0: #endif michael@0: ::memcpy(mArray, oldArray, mCount * sizeof(nsISupports*)); michael@0: if (oldArray != mAutoArray) michael@0: delete[] oldArray; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSupportsArray::Enumerate(nsIEnumerator* *result) michael@0: { michael@0: nsSupportsArrayEnumerator* e = new nsSupportsArrayEnumerator(this); michael@0: if (!e) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: *result = e; michael@0: NS_ADDREF(e); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSupportsArray::Clone(nsISupportsArray** aResult) michael@0: { michael@0: nsCOMPtr newArray; michael@0: nsresult rv = NS_NewISupportsArray(getter_AddRefs(newArray)); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: michael@0: uint32_t count = 0; michael@0: Count(&count); michael@0: for (uint32_t i = 0; i < count; i++) { michael@0: if (!newArray->InsertElementAt(mArray[i], i)) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: } michael@0: michael@0: newArray.forget(aResult); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: NS_NewISupportsArray(nsISupportsArray** aInstancePtrResult) michael@0: { michael@0: nsresult rv; michael@0: rv = nsSupportsArray::Create(nullptr, NS_GET_IID(nsISupportsArray), michael@0: (void**)aInstancePtrResult); michael@0: return rv; michael@0: }