diff --git a/browser/modules/ContentWebRTC.jsm b/browser/modules/ContentWebRTC.jsm index 2e0b6c602101..fc4a16b67628 100644 --- a/browser/modules/ContentWebRTC.jsm +++ b/browser/modules/ContentWebRTC.jsm @@ -73,10 +73,10 @@ this.ContentWebRTC = { let devices = contentWindow.pendingGetUserMediaRequests.get(callID); forgetGUMRequest(contentWindow, callID); - let allowedDevices = Cc["@mozilla.org/array;1"] - .createInstance(Ci.nsIMutableArray); + let allowedDevices = Cc["@mozilla.org/supports-array;1"] + .createInstance(Ci.nsISupportsArray); for (let deviceIndex of aMessage.data.devices) - allowedDevices.appendElement(devices[deviceIndex], /*weak =*/ false); + allowedDevices.AppendElement(devices[deviceIndex]); Services.obs.notifyObservers(allowedDevices, "getUserMedia:response:allow", callID); break; @@ -261,8 +261,8 @@ function forgetPendingListsEventually(aContentWindow) { } function updateIndicators() { - let contentWindowArray = MediaManagerService.activeMediaCaptureWindows; - let count = contentWindowArray.length; + let contentWindowSupportsArray = MediaManagerService.activeMediaCaptureWindows; + let count = contentWindowSupportsArray.Count(); let state = { showGlobalIndicator: count > 0, @@ -280,7 +280,7 @@ function updateIndicators() { // sending duplicate notifications. let contentWindows = new Set(); for (let i = 0; i < count; ++i) { - contentWindows.add(contentWindowArray.queryElementAt(i, Ci.nsISupports).top); + contentWindows.add(contentWindowSupportsArray.GetElementAt(i).top); } for (let contentWindow of contentWindows) { diff --git a/dom/media/MediaManager.cpp b/dom/media/MediaManager.cpp index 72071cde5bca..0f7fec5b3358 100644 --- a/dom/media/MediaManager.cpp +++ b/dom/media/MediaManager.cpp @@ -10,7 +10,6 @@ #include "mozilla/dom/MediaStreamTrack.h" #include "GetUserMediaRequest.h" #include "MediaStreamListener.h" -#include "nsArray.h" #include "nsContentUtils.h" #include "nsHashPropertyBag.h" #ifdef MOZ_WIDGET_GONK @@ -21,6 +20,7 @@ #include "nsIScriptGlobalObject.h" #include "nsIPermissionManager.h" #include "nsIPopupWindowManager.h" +#include "nsISupportsArray.h" #include "nsIDocShell.h" #include "nsIDocument.h" #include "nsISupportsPrimitives.h" @@ -2406,10 +2406,14 @@ if (privileged) { return; } - nsCOMPtr devicesCopy = nsArray::Create(); // before we give up devices below + nsCOMPtr devicesCopy; // before we give up devices below if (!askPermission) { + nsresult rv = NS_NewISupportsArray(getter_AddRefs(devicesCopy)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } for (auto& device : **devices) { - nsresult rv = devicesCopy->AppendElement(device, /*weak =*/ false); + rv = devicesCopy->AppendElement(device); if (NS_WARN_IF(NS_FAILED(rv))) { return; } @@ -3027,15 +3031,15 @@ MediaManager::Observe(nsISupports* aSubject, const char* aTopic, if (aSubject) { // A particular device or devices were chosen by the user. // NOTE: does not allow setting a device to null; assumes nullptr - nsCOMPtr array(do_QueryInterface(aSubject)); + nsCOMPtr array(do_QueryInterface(aSubject)); MOZ_ASSERT(array); uint32_t len = 0; - array->GetLength(&len); + array->Count(&len); bool videoFound = false, audioFound = false; for (uint32_t i = 0; i < len; i++) { - nsCOMPtr device; - array->QueryElementAt(i, NS_GET_IID(nsIMediaDevice), - getter_AddRefs(device)); + nsCOMPtr supports; + array->GetElementAt(i,getter_AddRefs(supports)); + nsCOMPtr device(do_QueryInterface(supports)); MOZ_ASSERT(device); // shouldn't be returning anything else... if (device) { nsString type; @@ -3129,11 +3133,14 @@ MediaManager::Observe(nsISupports* aSubject, const char* aTopic, } nsresult -MediaManager::GetActiveMediaCaptureWindows(nsIArray** aArray) +MediaManager::GetActiveMediaCaptureWindows(nsISupportsArray** aArray) { MOZ_ASSERT(aArray); - - nsCOMPtr array = nsArray::Create(); + nsISupportsArray* array; + nsresult rv = NS_NewISupportsArray(&array); // AddRefs + if (NS_FAILED(rv)) { + return rv; + } for (auto iter = mActiveWindows.Iter(); !iter.Done(); iter.Next()) { const uint64_t& id = iter.Key(); @@ -3164,11 +3171,11 @@ MediaManager::GetActiveMediaCaptureWindows(nsIArray** aArray) } } if (capturing) { - array->AppendElement(window, /*weak =*/ false); + array->AppendElement(window); } } - array.forget(aArray); + *aArray = array; return NS_OK; } @@ -3327,14 +3334,14 @@ MediaManager::IterateWindowListeners(nsPIDOMWindowInner* aWindow, void MediaManager::StopMediaStreams() { - nsCOMPtr array; + nsCOMPtr array; GetActiveMediaCaptureWindows(getter_AddRefs(array)); uint32_t len; - array->GetLength(&len); + array->Count(&len); for (uint32_t i = 0; i < len; i++) { - nsCOMPtr win; - array->QueryElementAt(i, NS_GET_IID(nsPIDOMWindowInner), - getter_AddRefs(win)); + nsCOMPtr window; + array->GetElementAt(i, getter_AddRefs(window)); + nsCOMPtr win(do_QueryInterface(window)); if (win) { OnNavigation(win->WindowID()); } @@ -3346,14 +3353,14 @@ MediaManager::IsActivelyCapturingOrHasAPermission(uint64_t aWindowId) { // Does page currently have a gUM stream active? - nsCOMPtr array; + nsCOMPtr array; GetActiveMediaCaptureWindows(getter_AddRefs(array)); uint32_t len; - array->GetLength(&len); + array->Count(&len); for (uint32_t i = 0; i < len; i++) { - nsCOMPtr win; - array->QueryElementAt(i, NS_GET_IID(nsPIDOMWindowInner), - getter_AddRefs(win)); + nsCOMPtr window; + array->GetElementAt(i, getter_AddRefs(window)); + nsCOMPtr win(do_QueryInterface(window)); if (win && win->WindowID() == aWindowId) { return true; } diff --git a/dom/media/MediaPermissionGonk.cpp b/dom/media/MediaPermissionGonk.cpp index 2a9cbf331c43..5cefc96f2b47 100644 --- a/dom/media/MediaPermissionGonk.cpp +++ b/dom/media/MediaPermissionGonk.cpp @@ -5,12 +5,12 @@ #include "MediaManager.h" #include "MediaPermissionGonk.h" -#include "nsArray.h" #include "nsCOMPtr.h" #include "nsIContentPermissionPrompt.h" #include "nsIDocument.h" #include "nsIDOMNavigatorUserMedia.h" #include "nsIStringEnumerator.h" +#include "nsISupportsArray.h" #include "nsJSUtils.h" #include "nsQueryObject.h" #include "nsPIDOMWindow.h" @@ -67,10 +67,12 @@ static nsresult NotifyPermissionAllow(const nsAString &aCallID, nsTArray > &aDevices) { nsresult rv; - nsCOMPtr array = nsArray::Create(); + nsCOMPtr array; + rv = NS_NewISupportsArray(getter_AddRefs(array)); + NS_ENSURE_SUCCESS(rv, rv); for (uint32_t i = 0; i < aDevices.Length(); ++i) { - rv = array->AppendElement(aDevices.ElementAt(i), /*weak =*/ false); + rv = array->AppendElement(aDevices.ElementAt(i)); NS_ENSURE_SUCCESS(rv, rv); } diff --git a/dom/media/nsIMediaManager.idl b/dom/media/nsIMediaManager.idl index 6eadbe02d61d..aa947a8fa216 100644 --- a/dom/media/nsIMediaManager.idl +++ b/dom/media/nsIMediaManager.idl @@ -4,7 +4,7 @@ #include "nsISupports.idl" -interface nsIArray; +interface nsISupportsArray; interface nsIDOMWindow; %{C++ @@ -16,7 +16,7 @@ interface nsIDOMWindow; interface nsIMediaManagerService : nsISupports { /* return a array of inner windows that have active captures */ - readonly attribute nsIArray activeMediaCaptureWindows; + readonly attribute nsISupportsArray activeMediaCaptureWindows; /* Get the capture state for the given window and all descendant windows (iframes, etc) */ void mediaCaptureWindowState(in nsIDOMWindow aWindow, out boolean aVideo, out boolean aAudio, diff --git a/mobile/android/chrome/content/WebrtcUI.js b/mobile/android/chrome/content/WebrtcUI.js index 475d05bd2c05..afdc90e83c97 100644 --- a/mobile/android/chrome/content/WebrtcUI.js +++ b/mobile/android/chrome/content/WebrtcUI.js @@ -58,7 +58,7 @@ var WebrtcUI = { notify: function() { let windows = MediaManagerService.activeMediaCaptureWindows; - let count = windows.length; + let count = windows.Count(); let msg = {}; if (count == 0) { if (this._notificationId) { @@ -76,7 +76,7 @@ var WebrtcUI = { let cameraActive = false; let audioActive = false; for (let i = 0; i < count; i++) { - let win = windows.queryElementAt(i, Ci.nsIDOMWindow); + let win = windows.GetElementAt(i); let hasAudio = {}; let hasVideo = {}; MediaManagerService.mediaCaptureWindowState(win, hasVideo, hasAudio); @@ -150,19 +150,19 @@ var WebrtcUI = { { label: Strings.browser.GetStringFromName("getUserMedia.shareRequest.label"), callback: function(checked /* ignored */, inputs) { - let allowedDevices = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray); + let allowedDevices = Cc["@mozilla.org/supports-array;1"].createInstance(Ci.nsISupportsArray); let audioId = 0; if (inputs && inputs.audioDevice != undefined) audioId = inputs.audioDevice; if (audioDevices[audioId]) - allowedDevices.appendElement(audioDevices[audioId], /*weak =*/ false); + allowedDevices.AppendElement(audioDevices[audioId]); let videoId = 0; if (inputs && inputs.videoSource != undefined) videoId = inputs.videoSource; if (videoDevices[videoId]) { - allowedDevices.appendElement(videoDevices[videoId], /*weak =*/ false); + allowedDevices.AppendElement(videoDevices[videoId]); let perms = Services.perms; // Although the lifetime is "session" it will be removed upon // use so it's more of a one-shot. diff --git a/xpcom/ds/nsISupportsArray.idl b/xpcom/ds/nsISupportsArray.idl index cdf7fbc3fb56..2cc90186100f 100644 --- a/xpcom/ds/nsISupportsArray.idl +++ b/xpcom/ds/nsISupportsArray.idl @@ -33,11 +33,17 @@ class nsISupportsArray; [scriptable, uuid(241addc8-3608-4e73-8083-2fd6fa09eba2)] interface nsISupportsArray : nsICollection { + [notxpcom] boolean Equals([const] in nsISupportsArray other); + [notxpcom] long IndexOf([const] in nsISupports aPossibleElement); + [notxpcom] long IndexOfStartingAt([const] in nsISupports aPossibleElement, + in unsigned long aStartIndex); [notxpcom] long LastIndexOf([const] in nsISupports aPossibleElement); // xpcom-compatible versions long GetIndexOf(in nsISupports aPossibleElement); + long GetIndexOfStartingAt(in nsISupports aPossibleElement, + in unsigned long aStartIndex); long GetLastIndexOf(in nsISupports aPossibleElement); [notxpcom] boolean InsertElementAt(in nsISupports aElement, @@ -46,14 +52,25 @@ interface nsISupportsArray : nsICollection { in unsigned long aIndex); [notxpcom] boolean RemoveElementAt(in unsigned long aIndex); + [notxpcom] boolean RemoveLastElement([const] in nsISupports aElement); // xpcom-compatible versions + void DeleteLastElement(in nsISupports aElement); void DeleteElementAt(in unsigned long aIndex); void Compact(); nsISupportsArray clone(); + + [notxpcom] boolean MoveElement(in long aFrom, + in long aTo); + + [notxpcom] boolean RemoveElementsAt(in unsigned long aIndex, + in unsigned long aCount); + + [notxpcom] boolean SizeTo(in long aSize); + }; %{C++ diff --git a/xpcom/ds/nsSupportsArray.cpp b/xpcom/ds/nsSupportsArray.cpp index 9dad104225f9..4e6a7d772489 100644 --- a/xpcom/ds/nsSupportsArray.cpp +++ b/xpcom/ds/nsSupportsArray.cpp @@ -6,11 +6,79 @@ #include #include - -#include "nsIObjectInputStream.h" -#include "nsIObjectOutputStream.h" +#include "mozilla/CheckedInt.h" +#include "mozilla/MathAlgorithms.h" #include "nsSupportsArray.h" #include "nsSupportsArrayEnumerator.h" +#include "nsIObjectInputStream.h" +#include "nsIObjectOutputStream.h" + +#if DEBUG_SUPPORTSARRAY +#define MAXSUPPORTS 20 + +class SupportsStats +{ +public: + SupportsStats(); + ~SupportsStats(); + +}; + +static int sizesUsed; // number of the elements of the arrays used +static int sizesAlloced[MAXSUPPORTS]; // sizes of the allocations. sorted +static int NumberOfSize[MAXSUPPORTS]; // number of this allocation size (1 per array) +static int AllocedOfSize[MAXSUPPORTS]; // number of this allocation size (each size for array used) +static int GrowInPlace[MAXSUPPORTS]; + +// these are per-allocation +static int MaxElements[3000]; + +// very evil +#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 < MAXSUPPORTS) \ + { 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); + + +SupportsStats::SupportsStats() +{ + sizesUsed = 1; + sizesAlloced[0] = 0; +} + +SupportsStats::~SupportsStats() +{ + int i; + for (i = 0; i < sizesUsed; ++i) { + printf("Size %d:\n", sizesAlloced[i]); + printf("\tNumber of SupportsArrays this size (max): %d\n", NumberOfSize[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 SupportsArray:\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 get called +SupportsStats gSupportsStats; +#endif nsresult nsQueryElementAt::operator()(const nsIID& aIID, void** aResult) const @@ -28,11 +96,88 @@ nsQueryElementAt::operator()(const nsIID& aIID, void** aResult) const nsSupportsArray::nsSupportsArray() { + mArray = mAutoArray; + mArraySize = kAutoArraySize; + mCount = 0; +#if DEBUG_SUPPORTSARRAY + mMaxCount = 0; + mMaxSize = 0; + ADD_TO_STATS(NumberOfSize, kAutoArraySize * sizeof(mArray[0])); + MaxElements[0]++; +#endif } nsSupportsArray::~nsSupportsArray() { - Clear(); + DeleteArray(); +} + +bool +nsSupportsArray::GrowArrayBy(uint32_t aGrowBy) +{ + const uint32_t kGrowArrayBy = 8; + const uint32_t kLinearThreshold = 16 * sizeof(nsISupports*); + + // 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; + } + + CheckedUint32 newCount(mArraySize); + newCount += aGrowBy; // Minimum increase + CheckedUint32 newSize(sizeof(mArray[0])); + newSize *= newCount; + + if (!newSize.isValid()) { + return false; + } + + if (newSize.value() >= kLinearThreshold) { + // newCount includes enough space for at least kGrowArrayBy new slots. + // Select the next power-of-two size in bytes above that if newSize is + // not a power of two. + if (newSize.value() & (newSize.value() - 1)) { + newSize = UINT64_C(1) << mozilla::CeilingLog2(newSize.value()); + if (!newSize.isValid()) { + return false; + } + } + + newCount = newSize / sizeof(mArray[0]); + } + // XXX This would be far more efficient in many allocators if we used + // XXX PR_Realloc(), etc + nsISupports** oldArray = mArray; + + mArray = new nsISupports*[newCount.value()]; + mArraySize = newCount.value(); + +#if DEBUG_SUPPORTSARRAY + if (oldArray == mArray) { // can't happen without use of realloc + ADD_TO_STATS(GrowInPlace, mCount); + } + ADD_TO_STATS(AllocedOfSize, mArraySize * sizeof(mArray[0])); + if (mArraySize > mMaxSize) { + ADD_TO_STATS(NumberOfSize, mArraySize * sizeof(mArray[0])); + if (oldArray != &(mAutoArray[0])) { + SUB_FROM_STATS(NumberOfSize, mCount * sizeof(mArray[0])); + } + mMaxSize = mArraySize; + } +#endif + if (oldArray) { // need to move old data + if (0 < mCount) { + ::memcpy(mArray, oldArray, mCount * sizeof(nsISupports*)); + } + if (oldArray != &(mAutoArray[0])) { + delete[] oldArray; + } + } + + return true; } nsresult @@ -53,8 +198,6 @@ NS_IMPL_ISUPPORTS(nsSupportsArray, nsISupportsArray, nsICollection, NS_IMETHODIMP nsSupportsArray::Read(nsIObjectInputStream* aStream) { - // TODO(ER): This used to leak when resizing the array. Not sure if that was - // intentional, I'm guessing not. nsresult rv; uint32_t newArraySize; @@ -63,40 +206,43 @@ nsSupportsArray::Read(nsIObjectInputStream* aStream) return rv; } - uint32_t count; - rv = aStream->Read32(&count); + if (newArraySize <= kAutoArraySize) { + if (mArray != mAutoArray) { + delete[] mArray; + mArray = mAutoArray; + } + newArraySize = kAutoArraySize; + } else { + if (newArraySize <= mArraySize) { + // Keep non-default-size mArray, it's more than big enough. + newArraySize = mArraySize; + } else { + nsISupports** array = new nsISupports*[newArraySize]; + if (mArray != mAutoArray) { + delete[] mArray; + } + mArray = array; + } + } + mArraySize = newArraySize; + + rv = aStream->Read32(&mCount); if (NS_FAILED(rv)) { return rv; } - NS_ASSERTION(count <= newArraySize, "overlarge mCount!"); - if (count > newArraySize) { - count = newArraySize; + NS_ASSERTION(mCount <= mArraySize, "overlarge mCount!"); + if (mCount > mArraySize) { + mCount = mArraySize; } - // Don't clear out our array until we know we have enough space for the new - // one and have successfully copied everything out of the stream. - ISupportsArray tmp; - if (!tmp.SetCapacity(newArraySize, mozilla::fallible)) { - return NS_ERROR_OUT_OF_MEMORY; - } - - auto elems = tmp.AppendElements(count, mozilla::fallible); - for (uint32_t i = 0; i < count; i++) { - rv = aStream->ReadObject(true, &elems[i]); + for (uint32_t i = 0; i < mCount; i++) { + rv = aStream->ReadObject(true, &mArray[i]); if (NS_FAILED(rv)) { return rv; } } - // Now clear out existing refs and replace with the new array. - for (auto& item : mArray) { - NS_IF_RELEASE(item); - } - - mArray.Clear(); - mArray.SwapElements(tmp); - return NS_OK; } @@ -105,18 +251,18 @@ nsSupportsArray::Write(nsIObjectOutputStream* aStream) { nsresult rv; - rv = aStream->Write32(mArray.Capacity()); + rv = aStream->Write32(mArraySize); if (NS_FAILED(rv)) { return rv; } - rv = aStream->Write32(mArray.Length()); + rv = aStream->Write32(mCount); if (NS_FAILED(rv)) { return rv; } - for (auto& item : mArray) { - rv = aStream->WriteObject(item, true); + for (uint32_t i = 0; i < mCount; i++) { + rv = aStream->WriteObject(mArray[i], true); if (NS_FAILED(rv)) { return rv; } @@ -125,42 +271,141 @@ nsSupportsArray::Write(nsIObjectOutputStream* aStream) return NS_OK; } +void +nsSupportsArray::DeleteArray(void) +{ + Clear(); + if (mArray != &(mAutoArray[0])) { + delete[] mArray; + mArray = mAutoArray; + mArraySize = kAutoArraySize; + } +} + + +NS_IMETHODIMP_(bool) +nsSupportsArray::Equals(const nsISupportsArray* aOther) +{ + if (aOther) { + uint32_t countOther; + nsISupportsArray* other = const_cast(aOther); + nsresult rv = other->Count(&countOther); + if (NS_FAILED(rv)) { + return false; + } + + if (mCount == countOther) { + uint32_t index = mCount; + nsCOMPtr otherElem; + while (index--) { + if (NS_FAILED(other->GetElementAt(index, getter_AddRefs(otherElem)))) { + return false; + } + if (mArray[index] != otherElem) { + return false; + } + } + return true; + } + } + return false; +} + NS_IMETHODIMP nsSupportsArray::GetElementAt(uint32_t aIndex, nsISupports** aOutPtr) { - NS_IF_ADDREF(*aOutPtr = mArray.SafeElementAt(aIndex, nullptr)); + *aOutPtr = nullptr; + if (aIndex < mCount) { + NS_IF_ADDREF(*aOutPtr = mArray[aIndex]); + } return NS_OK; } NS_IMETHODIMP_(int32_t) nsSupportsArray::IndexOf(const nsISupports* aPossibleElement) { - return mArray.IndexOf(aPossibleElement); + return IndexOfStartingAt(aPossibleElement, 0); +} + +NS_IMETHODIMP_(int32_t) +nsSupportsArray::IndexOfStartingAt(const nsISupports* aPossibleElement, + uint32_t aStartIndex) +{ + if (aStartIndex < mCount) { + const nsISupports** start = (const nsISupports**)mArray; // work around goofy compiler behavior + const nsISupports** ep = (start + aStartIndex); + const nsISupports** end = (start + mCount); + while (ep < end) { + if (aPossibleElement == *ep) { + return (ep - start); + } + ep++; + } + } + return -1; } NS_IMETHODIMP_(int32_t) nsSupportsArray::LastIndexOf(const nsISupports* aPossibleElement) { - return mArray.LastIndexOf(aPossibleElement); + if (0 < mCount) { + const nsISupports** start = (const nsISupports**)mArray; // work around goofy compiler behavior + const nsISupports** ep = (start + mCount); + while (start <= --ep) { + if (aPossibleElement == *ep) { + return (ep - start); + } + } + } + return -1; } NS_IMETHODIMP_(bool) nsSupportsArray::InsertElementAt(nsISupports* aElement, uint32_t aIndex) { + if (aIndex <= mCount) { + CheckedUint32 newCount(mCount); + newCount += 1; + if (!newCount.isValid()) { + return false; + } - if (aIndex > mArray.Length() || - !mArray.InsertElementAt(aIndex, aElement, mozilla::fallible)) { - return false; + if (mArraySize < newCount.value()) { + // need to grow the array + if (!GrowArrayBy(1)) { + return false; + } + } + + // Could be slightly more efficient if GrowArrayBy knew about the + // split, but the difference is trivial. + uint32_t slide = (mCount - aIndex); + if (0 < slide) { + ::memmove(mArray + aIndex + 1, mArray + aIndex, + slide * sizeof(nsISupports*)); + } + + mArray[aIndex] = aElement; + NS_IF_ADDREF(aElement); + mCount++; + +#if DEBUG_SUPPORTSARRAY + if (mCount > mMaxCount && + mCount < (int32_t)(sizeof(MaxElements) / sizeof(MaxElements[0]))) { + MaxElements[mCount]++; + MaxElements[mMaxCount]--; + mMaxCount = mCount; + } +#endif + return true; } - - NS_IF_ADDREF(aElement); - return true; + return false; } NS_IMETHODIMP_(bool) nsSupportsArray::ReplaceElementAt(nsISupports* aElement, uint32_t aIndex) { - if (aIndex < mArray.Length()) { + if (aIndex < mCount) { NS_IF_ADDREF(aElement); // addref first in case it's the same object! NS_IF_RELEASE(mArray[aIndex]); mArray[aIndex] = aElement; @@ -170,11 +415,18 @@ nsSupportsArray::ReplaceElementAt(nsISupports* aElement, uint32_t aIndex) } NS_IMETHODIMP_(bool) -nsSupportsArray::RemoveElementAt(uint32_t aIndex) +nsSupportsArray::RemoveElementsAt(uint32_t aIndex, uint32_t aCount) { - if (aIndex + 1 <= mArray.Length()) { - NS_IF_RELEASE(mArray[aIndex]); - mArray.RemoveElementAt(aIndex); + if (aIndex + aCount <= mCount) { + for (uint32_t i = 0; i < aCount; i++) { + NS_IF_RELEASE(mArray[aIndex + i]); + } + mCount -= aCount; + int32_t slide = (mCount - aIndex); + if (0 < slide) { + ::memmove(mArray + aIndex, mArray + aIndex + aCount, + slide * sizeof(nsISupports*)); + } return true; } return false; @@ -183,7 +435,7 @@ nsSupportsArray::RemoveElementAt(uint32_t aIndex) NS_IMETHODIMP nsSupportsArray::RemoveElement(nsISupports* aElement) { - int32_t theIndex = IndexOf(aElement); + int32_t theIndex = IndexOfStartingAt(aElement, 0); if (theIndex >= 0) { return RemoveElementAt(theIndex) ? NS_OK : NS_ERROR_FAILURE; } @@ -191,25 +443,137 @@ nsSupportsArray::RemoveElement(nsISupports* aElement) return NS_ERROR_FAILURE; } +NS_IMETHODIMP_(bool) +nsSupportsArray::RemoveLastElement(const nsISupports* aElement) +{ + int32_t theIndex = LastIndexOf(aElement); + if (theIndex >= 0) { + return RemoveElementAt(theIndex); + } + + return false; +} + +NS_IMETHODIMP_(bool) +nsSupportsArray::MoveElement(int32_t aFrom, int32_t aTo) +{ + nsISupports* tempElement; + + if (aTo == aFrom) { + return true; + } + + if (aTo < 0 || aFrom < 0 || + (uint32_t)aTo >= mCount || (uint32_t)aFrom >= mCount) { + // can't extend the array when moving an element. Also catches mImpl = null + return false; + } + tempElement = mArray[aFrom]; + + if (aTo < aFrom) { + // Moving one element closer to the head; the elements inbetween move down + ::memmove(mArray + aTo + 1, mArray + aTo, + (aFrom - aTo) * sizeof(mArray[0])); + mArray[aTo] = tempElement; + } else { // already handled aFrom == aTo + // Moving one element closer to the tail; the elements inbetween move up + ::memmove(mArray + aFrom, mArray + aFrom + 1, + (aTo - aFrom) * sizeof(mArray[0])); + mArray[aTo] = tempElement; + } + + return true; +} + NS_IMETHODIMP nsSupportsArray::Clear(void) { - for (auto& item : mArray) { - NS_IF_RELEASE(item); + if (0 < mCount) { + do { + --mCount; + NS_IF_RELEASE(mArray[mCount]); + } while (0 != mCount); } - - mArray.Clear(); - return NS_OK; } NS_IMETHODIMP nsSupportsArray::Compact(void) { - mArray.Compact(); +#if DEBUG_SUPPORTSARRAY + uint32_t oldArraySize = mArraySize; +#endif + if ((mArraySize != mCount) && (kAutoArraySize < mArraySize)) { + nsISupports** oldArray = mArray; + if (mCount <= kAutoArraySize) { + mArray = mAutoArray; + mArraySize = kAutoArraySize; + } else { + mArray = new nsISupports*[mCount]; + if (!mArray) { + mArray = oldArray; + return NS_OK; + } + mArraySize = mCount; + } +#if DEBUG_SUPPORTSARRAY + if (oldArray == mArray && + oldArray != &(mAutoArray[0])) { // can't happen without use of realloc + ADD_TO_STATS(GrowInPlace, oldArraySize); + } + if (oldArray != &(mAutoArray[0])) { + ADD_TO_STATS(AllocedOfSize, mArraySize * sizeof(mArray[0])); + } +#endif + ::memcpy(mArray, oldArray, mCount * sizeof(nsISupports*)); + delete[] oldArray; + } return NS_OK; } +NS_IMETHODIMP_(bool) +nsSupportsArray::SizeTo(int32_t aSize) +{ +#if DEBUG_SUPPORTSARRAY + uint32_t oldArraySize = mArraySize; +#endif + NS_ASSERTION(aSize >= 0, "negative aSize!"); + + // XXX for aSize < mCount we could resize to mCount + if (mArraySize == (uint32_t)aSize || (uint32_t)aSize < mCount) { + return true; // nothing to do + } + + // switch back to autoarray if possible + nsISupports** oldArray = mArray; + if ((uint32_t)aSize <= kAutoArraySize) { + mArray = mAutoArray; + mArraySize = kAutoArraySize; + } else { + mArray = new nsISupports*[aSize]; + if (!mArray) { + mArray = oldArray; + return false; + } + mArraySize = aSize; + } +#if DEBUG_SUPPORTSARRAY + if (oldArray == mArray && + oldArray != &(mAutoArray[0])) { // can't happen without use of realloc + ADD_TO_STATS(GrowInPlace, oldArraySize); + } + if (oldArray != &(mAutoArray[0])) { + ADD_TO_STATS(AllocedOfSize, mArraySize * sizeof(mArray[0])); + } +#endif + ::memcpy(mArray, oldArray, mCount * sizeof(nsISupports*)); + if (oldArray != mAutoArray) { + delete[] oldArray; + } + + return true; +} + NS_IMETHODIMP nsSupportsArray::Enumerate(nsIEnumerator** aResult) { @@ -228,10 +592,10 @@ nsSupportsArray::Clone(nsISupportsArray** aResult) return rv; } - for (auto& item : mArray) { - // AppendElement does an odd cast of bool to nsresult, we just cast back - // here. - if (!(bool)newArray->AppendElement(item)) { + uint32_t count = 0; + Count(&count); + for (uint32_t i = 0; i < count; i++) { + if (!newArray->InsertElementAt(mArray[i], i)) { return NS_ERROR_OUT_OF_MEMORY; } } diff --git a/xpcom/ds/nsSupportsArray.h b/xpcom/ds/nsSupportsArray.h index af625f8f64c3..e0200f2d23c4 100644 --- a/xpcom/ds/nsSupportsArray.h +++ b/xpcom/ds/nsSupportsArray.h @@ -7,10 +7,13 @@ #ifndef nsSupportsArray_h__ #define nsSupportsArray_h__ +//#define DEBUG_SUPPORTSARRAY 1 + #include "nsISupportsArray.h" -#include "nsTArray.h" #include "mozilla/Attributes.h" +static const uint32_t kAutoArraySize = 8; + class nsSupportsArray final : public nsISupportsArray { ~nsSupportsArray(void); // nonvirtual since we're not subclassed @@ -28,16 +31,18 @@ public: // nsICollection methods: NS_IMETHOD Count(uint32_t* aResult) override { - *aResult = mArray.Length(); + *aResult = mCount; return NS_OK; } NS_IMETHOD GetElementAt(uint32_t aIndex, nsISupports** aResult) override; MOZ_MUST_USE NS_IMETHOD QueryElementAt(uint32_t aIndex, const nsIID& aIID, void** aResult) override { - nsISupports* element = mArray.SafeElementAt(aIndex, nullptr); - if (element) { - return element->QueryInterface(aIID, aResult); + if (aIndex < mCount) { + nsISupports* element = mArray[aIndex]; + if (element) { + return element->QueryInterface(aIID, aResult); + } } return NS_ERROR_FAILURE; } @@ -49,15 +54,20 @@ public: MOZ_MUST_USE NS_IMETHOD AppendElement(nsISupports* aElement) override { // XXX Invalid cast of bool to nsresult (bug 778110) - return (nsresult)InsertElementAt(aElement, mArray.Length())/* ? NS_OK : NS_ERROR_FAILURE*/; + return (nsresult)InsertElementAt(aElement, mCount)/* ? NS_OK : NS_ERROR_FAILURE*/; } // XXX this is badly named - should be RemoveFirstElement MOZ_MUST_USE NS_IMETHOD RemoveElement(nsISupports* aElement) override; + MOZ_MUST_USE NS_IMETHOD_(bool) MoveElement(int32_t aFrom, int32_t aTo) override; NS_IMETHOD Enumerate(nsIEnumerator** aResult) override; NS_IMETHOD Clear(void) override; // nsISupportsArray methods: + NS_IMETHOD_(bool) Equals(const nsISupportsArray* aOther) override; + NS_IMETHOD_(int32_t) IndexOf(const nsISupports* aPossibleElement) override; + NS_IMETHOD_(int32_t) IndexOfStartingAt(const nsISupports* aPossibleElement, + uint32_t aStartIndex = 0) override; NS_IMETHOD_(int32_t) LastIndexOf(const nsISupports* aPossibleElement) override; NS_IMETHOD GetIndexOf(nsISupports* aPossibleElement, int32_t* aResult) override @@ -66,6 +76,13 @@ public: return NS_OK; } + NS_IMETHOD GetIndexOfStartingAt(nsISupports* aPossibleElement, + uint32_t aStartIndex, int32_t* aResult) override + { + *aResult = IndexOfStartingAt(aPossibleElement, aStartIndex); + return NS_OK; + } + NS_IMETHOD GetLastIndexOf(nsISupports* aPossibleElement, int32_t* aResult) override { *aResult = LastIndexOf(aPossibleElement); @@ -79,7 +96,17 @@ public: ReplaceElementAt(nsISupports* aElement, uint32_t aIndex) override; MOZ_MUST_USE NS_IMETHOD_(bool) - RemoveElementAt(uint32_t aIndex) override; + RemoveElementAt(uint32_t aIndex) override + { + return RemoveElementsAt(aIndex, 1); + } + MOZ_MUST_USE NS_IMETHOD_(bool) + RemoveLastElement(const nsISupports* aElement) override; + + MOZ_MUST_USE NS_IMETHOD DeleteLastElement(nsISupports* aElement) override + { + return (RemoveLastElement(aElement) ? NS_OK : NS_ERROR_FAILURE); + } MOZ_MUST_USE NS_IMETHOD DeleteElementAt(uint32_t aIndex) override { @@ -90,12 +117,28 @@ public: MOZ_MUST_USE NS_IMETHOD Clone(nsISupportsArray** aResult) override; + MOZ_MUST_USE NS_IMETHOD_(bool) + RemoveElementsAt(uint32_t aIndex, uint32_t aCount) override; + + MOZ_MUST_USE NS_IMETHOD_(bool) + SizeTo(int32_t aSize) override; +protected: + void DeleteArray(void); + + bool GrowArrayBy(uint32_t aGrowBy); + + nsISupports** mArray; + uint32_t mArraySize; + uint32_t mCount; + nsISupports* mAutoArray[kAutoArraySize]; +#if DEBUG_SUPPORTSARRAY + uint32_t mMaxCount; + uint32_t mMaxSize; +#endif + private: // Copy constructors are not allowed explicit nsSupportsArray(const nsISupportsArray& aOther); - - typedef AutoTArray ISupportsArray; - ISupportsArray mArray; }; #endif // nsSupportsArray_h__ diff --git a/xpcom/glue/tests/gtest/TestArray.cpp b/xpcom/glue/tests/gtest/TestArray.cpp index 5a07bbfc05d2..f55da2c7d3e6 100644 --- a/xpcom/glue/tests/gtest/TestArray.cpp +++ b/xpcom/glue/tests/gtest/TestArray.cpp @@ -119,19 +119,26 @@ TEST(Array, main) // test IndexOf && LastIndexOf - int32_t expectedIndex = 0; + int32_t expectedIndex[5] = {0, 4, 6, 12, -1}; + int32_t count = 0; int32_t index = array->IndexOf(foo); - EXPECT_EQ(index, expectedIndex); - expectedIndex = 12; + EXPECT_EQ(index, expectedIndex[count]); + while (-1 != index) { + count++; + index = array->IndexOfStartingAt(foo, index + 1); + if (-1 != index) + EXPECT_EQ(index, expectedIndex[count]); + } index = array->LastIndexOf(foo); - EXPECT_EQ(index, expectedIndex); + count--; + EXPECT_EQ(index, expectedIndex[count]); // test ReplaceElementAt array->ReplaceElementAt(foo, 8); int32_t replaceResult[13] = {3, 0, 1, 2, 3, 4, 3, 5, 3, 7, 8, 9, 3}; CheckArray(array, 13, replaceResult, 9); - // test RemoveElementAt, RemoveElement + // test RemoveElementAt, RemoveElement RemoveLastElement array->RemoveElementAt(0); int32_t removeResult[12] = {0, 1, 2, 3, 4, 3, 5, 3, 7, 8, 9, 3}; CheckArray(array, 12, removeResult, 9); @@ -141,6 +148,9 @@ TEST(Array, main) array->RemoveElement(foo); int32_t removeResult3[10] = {0, 1, 2, 4, 3, 5, 7, 8, 9, 3}; CheckArray(array, 10, removeResult3, 9); + array->RemoveLastElement(foo); + int32_t removeResult4[9] = {0, 1, 2, 4, 3, 5, 7, 8, 9}; + CheckArray(array, 9, removeResult4, 9); foo = nullptr;