diff options
Diffstat (limited to '')
-rw-r--r-- | xpcom/ds/nsArrayEnumerator.cpp | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/xpcom/ds/nsArrayEnumerator.cpp b/xpcom/ds/nsArrayEnumerator.cpp new file mode 100644 index 0000000000..ec475f9e5b --- /dev/null +++ b/xpcom/ds/nsArrayEnumerator.cpp @@ -0,0 +1,213 @@ +/* -*- 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/Attributes.h" + +#include "nsArrayEnumerator.h" + +#include "nsIArray.h" +#include "nsSimpleEnumerator.h" + +#include "nsCOMArray.h" +#include "nsCOMPtr.h" +#include "mozilla/OperatorNewExtensions.h" +#include "mozilla/RefPtr.h" + +class nsSimpleArrayEnumerator final : public nsSimpleEnumerator { + public: + // nsISimpleEnumerator interface + NS_DECL_NSISIMPLEENUMERATOR + + // nsSimpleArrayEnumerator methods + explicit nsSimpleArrayEnumerator(nsIArray* aValueArray, const nsID& aEntryIID) + : mValueArray(aValueArray), mEntryIID(aEntryIID), mIndex(0) {} + + const nsID& DefaultInterface() override { return mEntryIID; } + + private: + ~nsSimpleArrayEnumerator() override = default; + + protected: + nsCOMPtr<nsIArray> mValueArray; + const nsID mEntryIID; + uint32_t mIndex; +}; + +NS_IMETHODIMP +nsSimpleArrayEnumerator::HasMoreElements(bool* aResult) { + MOZ_ASSERT(aResult != 0, "null ptr"); + if (!aResult) { + return NS_ERROR_NULL_POINTER; + } + + if (!mValueArray) { + *aResult = false; + return NS_OK; + } + + uint32_t cnt; + nsresult rv = mValueArray->GetLength(&cnt); + if (NS_FAILED(rv)) { + return rv; + } + *aResult = (mIndex < cnt); + return NS_OK; +} + +NS_IMETHODIMP +nsSimpleArrayEnumerator::GetNext(nsISupports** aResult) { + MOZ_ASSERT(aResult != 0, "null ptr"); + if (!aResult) { + return NS_ERROR_NULL_POINTER; + } + + if (!mValueArray) { + *aResult = nullptr; + return NS_OK; + } + + uint32_t cnt; + nsresult rv = mValueArray->GetLength(&cnt); + if (NS_FAILED(rv)) { + return rv; + } + if (mIndex >= cnt) { + return NS_ERROR_UNEXPECTED; + } + + return mValueArray->QueryElementAt(mIndex++, NS_GET_IID(nsISupports), + (void**)aResult); +} + +nsresult NS_NewArrayEnumerator(nsISimpleEnumerator** aResult, nsIArray* aArray, + const nsID& aEntryIID) { + RefPtr<nsSimpleArrayEnumerator> enumer = + new nsSimpleArrayEnumerator(aArray, aEntryIID); + enumer.forget(aResult); + return NS_OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +// enumerator implementation for nsCOMArray +// creates a snapshot of the array in question +// you MUST use NS_NewArrayEnumerator to create this, so that +// allocation is done correctly +class nsCOMArrayEnumerator final : public nsSimpleEnumerator { + public: + // nsISimpleEnumerator interface + NS_DECL_NSISIMPLEENUMERATOR + + // Use this instead of `new`. + static nsCOMArrayEnumerator* Allocate(const nsCOMArray_base& aArray, + const nsID& aEntryIID); + + // specialized operator to make sure we make room for mValues + void operator delete(void* aPtr) { free(aPtr); } + + const nsID& DefaultInterface() override { return mEntryIID; } + + private: + // nsSimpleArrayEnumerator methods + explicit nsCOMArrayEnumerator(const nsID& aEntryIID) + : mIndex(0), mArraySize(0), mEntryIID(aEntryIID) { + mValueArray[0] = nullptr; + } + + ~nsCOMArrayEnumerator(void) override; + + protected: + uint32_t mIndex; // current position + uint32_t mArraySize; // size of the array + + const nsID& mEntryIID; + + // this is actually bigger + nsISupports* mValueArray[1]; +}; + +nsCOMArrayEnumerator::~nsCOMArrayEnumerator() { + // only release the entries that we haven't visited yet + for (; mIndex < mArraySize; ++mIndex) { + NS_IF_RELEASE(mValueArray[mIndex]); + } +} + +NS_IMETHODIMP +nsCOMArrayEnumerator::HasMoreElements(bool* aResult) { + MOZ_ASSERT(aResult != 0, "null ptr"); + if (!aResult) { + return NS_ERROR_NULL_POINTER; + } + + *aResult = (mIndex < mArraySize); + return NS_OK; +} + +NS_IMETHODIMP +nsCOMArrayEnumerator::GetNext(nsISupports** aResult) { + MOZ_ASSERT(aResult != 0, "null ptr"); + if (!aResult) { + return NS_ERROR_NULL_POINTER; + } + + if (mIndex >= mArraySize) { + return NS_ERROR_UNEXPECTED; + } + + // pass the ownership of the reference to the caller. Since + // we AddRef'ed during creation of |this|, there is no need + // to AddRef here + *aResult = mValueArray[mIndex++]; + + // this really isn't necessary. just pretend this happens, since + // we'll never visit this value again! + // mValueArray[(mIndex-1)] = nullptr; + + return NS_OK; +} + +nsCOMArrayEnumerator* nsCOMArrayEnumerator::Allocate( + const nsCOMArray_base& aArray, const nsID& aEntryIID) { + // create enough space such that mValueArray points to a large + // enough value. Note that the initial value of aSize gives us + // space for mValueArray[0], so we must subtract + size_t size = sizeof(nsCOMArrayEnumerator); + uint32_t count; + if (aArray.Count() > 0) { + count = static_cast<uint32_t>(aArray.Count()); + size += (count - 1) * sizeof(aArray[0]); + } else { + count = 0; + } + + // Allocate a buffer large enough to contain our object and its array. + void* mem = moz_xmalloc(size); + auto result = + new (mozilla::KnownNotNull, mem) nsCOMArrayEnumerator(aEntryIID); + + result->mArraySize = count; + + // now need to copy over the values, and addref each one + // now this might seem like a lot of work, but we're actually just + // doing all our AddRef's ahead of time since GetNext() doesn't + // need to AddRef() on the way out + for (uint32_t i = 0; i < count; ++i) { + result->mValueArray[i] = aArray[i]; + NS_IF_ADDREF(result->mValueArray[i]); + } + + return result; +} + +nsresult NS_NewArrayEnumerator(nsISimpleEnumerator** aResult, + const nsCOMArray_base& aArray, + const nsID& aEntryIID) { + RefPtr<nsCOMArrayEnumerator> enumerator = + nsCOMArrayEnumerator::Allocate(aArray, aEntryIID); + enumerator.forget(aResult); + return NS_OK; +} |