diff options
Diffstat (limited to 'xpcom/ds/nsTObserverArray.h')
-rw-r--r-- | xpcom/ds/nsTObserverArray.h | 583 |
1 files changed, 583 insertions, 0 deletions
diff --git a/xpcom/ds/nsTObserverArray.h b/xpcom/ds/nsTObserverArray.h new file mode 100644 index 0000000000..4d3e4087e0 --- /dev/null +++ b/xpcom/ds/nsTObserverArray.h @@ -0,0 +1,583 @@ +/* -*- 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/. */ + +#ifndef nsTObserverArray_h___ +#define nsTObserverArray_h___ + +#include "mozilla/MemoryReporting.h" +#include "mozilla/ReverseIterator.h" +#include "nsTArray.h" +#include "nsCycleCollectionNoteChild.h" + +/** + * An array of observers. Like a normal array, but supports iterators that are + * stable even if the array is modified during iteration. + * The template parameter T is the observer type the array will hold; + * N is the number of built-in storage slots that come with the array. + * NOTE: You probably want to use nsTObserverArray, unless you specifically + * want built-in storage. See below. + * @see nsTObserverArray, nsTArray + */ + +class nsTObserverArray_base { + public: + typedef size_t index_type; + typedef size_t size_type; + typedef ptrdiff_t diff_type; + + protected: + class Iterator_base { + public: + Iterator_base(const Iterator_base&) = delete; + + protected: + friend class nsTObserverArray_base; + + Iterator_base(index_type aPosition, Iterator_base* aNext) + : mPosition(aPosition), mNext(aNext) {} + + // The current position of the iterator. Its exact meaning differs + // depending on iterator. See nsTObserverArray<T>::ForwardIterator. + index_type mPosition; + + // The next iterator currently iterating the same array + Iterator_base* mNext; + }; + + nsTObserverArray_base() : mIterators(nullptr) {} + + ~nsTObserverArray_base() { + NS_ASSERTION(mIterators == nullptr, "iterators outlasting array"); + } + + /** + * Adjusts iterators after an element has been inserted or removed + * from the array. + * @param aModPos Position where elements were added or removed. + * @param aAdjustment -1 if an element was removed, 1 if an element was + * added. + */ + void AdjustIterators(index_type aModPos, diff_type aAdjustment); + + /** + * Clears iterators when the array is destroyed. + */ + void ClearIterators(); + + mutable Iterator_base* mIterators; +}; + +template <class T, size_t N> +class nsAutoTObserverArray : protected nsTObserverArray_base { + public: + typedef T value_type; + typedef nsTArray<T> array_type; + + nsAutoTObserverArray() = default; + + // + // Accessor methods + // + + // @return The number of elements in the array. + size_type Length() const { return mArray.Length(); } + + // @return True if the array is empty or false otherwise. + bool IsEmpty() const { return mArray.IsEmpty(); } + + // This method provides direct, readonly access to the array elements. + // @return A pointer to the first element of the array. If the array is + // empty, then this pointer must not be dereferenced. + const value_type* Elements() const { return mArray.Elements(); } + value_type* Elements() { return mArray.Elements(); } + + // This method provides direct access to an element of the array. The given + // index must be within the array bounds. If the underlying array may change + // during iteration, use an iterator instead of this function. + // @param aIndex The index of an element in the array. + // @return A reference to the i'th element of the array. + value_type& ElementAt(index_type aIndex) { return mArray.ElementAt(aIndex); } + + // Same as above, but readonly. + const value_type& ElementAt(index_type aIndex) const { + return mArray.ElementAt(aIndex); + } + + // This method provides direct access to an element of the array in a bounds + // safe manner. If the requested index is out of bounds the provided default + // value is returned. + // @param aIndex The index of an element in the array. + // @param aDef The value to return if the index is out of bounds. + value_type& SafeElementAt(index_type aIndex, value_type& aDef) { + return mArray.SafeElementAt(aIndex, aDef); + } + + // Same as above, but readonly. + const value_type& SafeElementAt(index_type aIndex, + const value_type& aDef) const { + return mArray.SafeElementAt(aIndex, aDef); + } + + // No operator[] is provided because the point of this class is to support + // allow modifying the array during iteration, and ElementAt() is not safe + // in those conditions. + + // + // Search methods + // + + // This method searches, starting from the beginning of the array, + // for the first element in this array that is equal to the given element. + // 'operator==' must be defined for value_type. + // @param aItem The item to search for. + // @return true if the element was found. + template <class Item> + bool Contains(const Item& aItem) const { + return IndexOf(aItem) != array_type::NoIndex; + } + + // This method searches for the offset of the first element in this + // array that is equal to the given element. + // 'operator==' must be defined for value_type. + // @param aItem The item to search for. + // @param aStart The index to start from. + // @return The index of the found element or NoIndex if not found. + template <class Item> + index_type IndexOf(const Item& aItem, index_type aStart = 0) const { + return mArray.IndexOf(aItem, aStart); + } + + // + // Mutation methods + // + + // Insert a given element at the given index. + // @param aIndex The index at which to insert item. + // @param aItem The item to insert, + template <class Item> + void InsertElementAt(index_type aIndex, const Item& aItem) { + mArray.InsertElementAt(aIndex, aItem); + AdjustIterators(aIndex, 1); + } + + // Same as above but without copy constructing. + // This is useful to avoid temporaries. + value_type* InsertElementAt(index_type aIndex) { + value_type* item = mArray.InsertElementAt(aIndex); + AdjustIterators(aIndex, 1); + return item; + } + + // Prepend an element to the array unless it already exists in the array. + // 'operator==' must be defined for value_type. + // @param aItem The item to prepend. + template <class Item> + void PrependElementUnlessExists(const Item& aItem) { + if (!Contains(aItem)) { + mArray.InsertElementAt(0, aItem); + AdjustIterators(0, 1); + } + } + + // Append an element to the array. + // @param aItem The item to append. + template <class Item> + void AppendElement(Item&& aItem) { + mArray.AppendElement(std::forward<Item>(aItem)); + } + + // Same as above, but without copy-constructing. This is useful to avoid + // temporaries. + value_type* AppendElement() { return mArray.AppendElement(); } + + // Append an element to the array unless it already exists in the array. + // 'operator==' must be defined for value_type. + // @param aItem The item to append. + template <class Item> + void AppendElementUnlessExists(const Item& aItem) { + if (!Contains(aItem)) { + mArray.AppendElement(aItem); + } + } + + // Remove an element from the array. + // @param aIndex The index of the item to remove. + void RemoveElementAt(index_type aIndex) { + NS_ASSERTION(aIndex < mArray.Length(), "invalid index"); + mArray.RemoveElementAt(aIndex); + AdjustIterators(aIndex, -1); + } + + // This helper function combines IndexOf with RemoveElementAt to "search + // and destroy" the first element that is equal to the given element. + // 'operator==' must be defined for value_type. + // @param aItem The item to search for. + // @return true if the element was found and removed. + template <class Item> + bool RemoveElement(const Item& aItem) { + index_type index = mArray.IndexOf(aItem, 0); + if (index == array_type::NoIndex) { + return false; + } + + mArray.RemoveElementAt(index); + AdjustIterators(index, -1); + return true; + } + + // See nsTArray::RemoveElementsBy. Neither the predicate nor the removal of + // elements from the array must have any side effects that modify the array. + template <typename Predicate> + void NonObservingRemoveElementsBy(Predicate aPredicate) { + index_type i = 0; + mArray.RemoveElementsBy([&](const value_type& aItem) { + if (aPredicate(aItem)) { + // This element is going to be removed. + AdjustIterators(i, -1); + return true; + } + ++i; + return false; + }); + } + + // Removes all observers and collapses all iterators to the beginning of + // the array. The result is that forward iterators will see all elements + // in the array. + void Clear() { + mArray.Clear(); + ClearIterators(); + } + + // Compact the array to minimize the memory it uses + void Compact() { mArray.Compact(); } + + // Returns the number of bytes on the heap taken up by this object, not + // including sizeof(*this). If you want to measure anything hanging off the + // array, you must iterate over the elements and measure them individually; + // hence the "Shallow" prefix. + size_t ShallowSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { + return mArray.ShallowSizeOfExcludingThis(aMallocSizeOf); + } + + // + // Iterators + // + + // Base class for iterators. Do not use this directly. + class Iterator : public Iterator_base { + protected: + friend class nsAutoTObserverArray; + typedef nsAutoTObserverArray<T, N> array_type; + + Iterator(const Iterator& aOther) + : Iterator(aOther.mPosition, aOther.mArray) {} + + Iterator(index_type aPosition, const array_type& aArray) + : Iterator_base(aPosition, aArray.mIterators), + mArray(const_cast<array_type&>(aArray)) { + aArray.mIterators = this; + } + + ~Iterator() { + NS_ASSERTION(mArray.mIterators == this, + "Iterators must currently be destroyed in opposite order " + "from the construction order. It is suggested that you " + "simply put them on the stack"); + mArray.mIterators = mNext; + } + + // The array we're iterating + array_type& mArray; + }; + + // Iterates the array forward from beginning to end. mPosition points + // to the element that will be returned on next call to GetNext. + // Elements: + // - prepended to the array during iteration *will not* be traversed + // - appended during iteration *will* be traversed + // - removed during iteration *will not* be traversed. + // @see EndLimitedIterator + class ForwardIterator : protected Iterator { + public: + typedef nsAutoTObserverArray<T, N> array_type; + typedef Iterator base_type; + + explicit ForwardIterator(const array_type& aArray) : Iterator(0, aArray) {} + + ForwardIterator(const array_type& aArray, index_type aPos) + : Iterator(aPos, aArray) {} + + bool operator<(const ForwardIterator& aOther) const { + NS_ASSERTION(&this->mArray == &aOther.mArray, + "not iterating the same array"); + return base_type::mPosition < aOther.mPosition; + } + + // Returns true if there are more elements to iterate. + // This must precede a call to GetNext(). If false is + // returned, GetNext() must not be called. + bool HasMore() const { + return base_type::mPosition < base_type::mArray.Length(); + } + + // Returns the next element and steps one step. This must + // be preceded by a call to HasMore(). + // @return The next observer. + value_type& GetNext() { + NS_ASSERTION(HasMore(), "iterating beyond end of array"); + return base_type::mArray.Elements()[base_type::mPosition++]; + } + + // Removes the element at the current iterator position. + // (the last element returned from |GetNext()|) + // This will not affect the next call to |GetNext()| + void Remove() { + return base_type::mArray.RemoveElementAt(base_type::mPosition - 1); + } + }; + + // EndLimitedIterator works like ForwardIterator, but will not iterate new + // observers appended to the array after the iterator was created. + class EndLimitedIterator : protected ForwardIterator { + public: + typedef nsAutoTObserverArray<T, N> array_type; + typedef Iterator base_type; + + explicit EndLimitedIterator(const array_type& aArray) + : ForwardIterator(aArray), mEnd(aArray, aArray.Length()) {} + + // Returns true if there are more elements to iterate. + // This must precede a call to GetNext(). If false is + // returned, GetNext() must not be called. + bool HasMore() const { return *this < mEnd; } + + // Returns the next element and steps one step. This must + // be preceded by a call to HasMore(). + // @return The next observer. + value_type& GetNext() { + NS_ASSERTION(HasMore(), "iterating beyond end of array"); + return base_type::mArray.Elements()[base_type::mPosition++]; + } + + // Removes the element at the current iterator position. + // (the last element returned from |GetNext()|) + // This will not affect the next call to |GetNext()| + void Remove() { + return base_type::mArray.RemoveElementAt(base_type::mPosition - 1); + } + + private: + ForwardIterator mEnd; + }; + + // Iterates the array backward from end to start. mPosition points + // to the element that was returned last. + // Elements: + // - prepended to the array during iteration *will* be traversed, + // unless the iteration already arrived at the first element + // - appended during iteration *will not* be traversed + // - removed during iteration *will not* be traversed. + class BackwardIterator : protected Iterator { + public: + typedef nsAutoTObserverArray<T, N> array_type; + typedef Iterator base_type; + + explicit BackwardIterator(const array_type& aArray) + : Iterator(aArray.Length(), aArray) {} + + // Returns true if there are more elements to iterate. + // This must precede a call to GetNext(). If false is + // returned, GetNext() must not be called. + bool HasMore() const { return base_type::mPosition > 0; } + + // Returns the next element and steps one step. This must + // be preceded by a call to HasMore(). + // @return The next observer. + value_type& GetNext() { + NS_ASSERTION(HasMore(), "iterating beyond start of array"); + return base_type::mArray.Elements()[--base_type::mPosition]; + } + + // Removes the element at the current iterator position. + // (the last element returned from |GetNext()|) + // This will not affect the next call to |GetNext()| + void Remove() { + return base_type::mArray.RemoveElementAt(base_type::mPosition); + } + }; + + struct EndSentinel {}; + + // Internal type, do not use directly, see + // ForwardRange()/EndLimitedRange()/BackwardRange(). + template <typename Iterator, typename U> + struct STLIterator { + using value_type = std::remove_reference_t<U>; + + explicit STLIterator(const nsAutoTObserverArray<T, N>& aArray) + : mIterator{aArray} { + operator++(); + } + + bool operator!=(const EndSentinel&) const { + // We are a non-end-sentinel and the other is an end-sentinel, so we are + // still valid if mCurrent is valid. + return mCurrent; + } + + STLIterator& operator++() { + mCurrent = mIterator.HasMore() ? &mIterator.GetNext() : nullptr; + return *this; + } + + value_type* operator->() { return mCurrent; } + U& operator*() { return *mCurrent; } + + private: + Iterator mIterator; + value_type* mCurrent; + }; + + // Internal type, do not use directly, see + // ForwardRange()/EndLimitedRange()/BackwardRange(). + template <typename Iterator, typename U> + class STLIteratorRange { + public: + using iterator = STLIterator<Iterator, U>; + + explicit STLIteratorRange(const nsAutoTObserverArray<T, N>& aArray) + : mArray{aArray} {} + + STLIteratorRange(const STLIteratorRange& aOther) = delete; + + iterator begin() const { return iterator{mArray}; } + EndSentinel end() const { return {}; } + + private: + const nsAutoTObserverArray<T, N>& mArray; + }; + + template <typename U> + using STLForwardIteratorRange = STLIteratorRange<ForwardIterator, U>; + + template <typename U> + using STLEndLimitedIteratorRange = STLIteratorRange<EndLimitedIterator, U>; + + template <typename U> + using STLBackwardIteratorRange = STLIteratorRange<BackwardIterator, U>; + + // Constructs a range (usable with range-based for) based on the + // ForwardIterator semantics. Note that this range does not provide + // full-feature STL-style iterators usable with STL-style algorithms. + auto ForwardRange() { return STLForwardIteratorRange<T>{*this}; } + + // Constructs a const range (usable with range-based for) based on the + // ForwardIterator semantics. Note that this range does not provide + // full-feature STL-style iterators usable with STL-style algorithms. + auto ForwardRange() const { return STLForwardIteratorRange<const T>{*this}; } + + // Constructs a range (usable with range-based for) based on the + // EndLimitedIterator semantics. Note that this range does not provide + // full-feature STL-style iterators usable with STL-style algorithms. + auto EndLimitedRange() { return STLEndLimitedIteratorRange<T>{*this}; } + + // Constructs a const range (usable with range-based for) based on the + // EndLimitedIterator semantics. Note that this range does not provide + // full-feature STL-style iterators usable with STL-style algorithms. + auto EndLimitedRange() const { + return STLEndLimitedIteratorRange<const T>{*this}; + } + + // Constructs a range (usable with range-based for) based on the + // BackwardIterator semantics. Note that this range does not provide + // full-feature STL-style iterators usable with STL-style algorithms. + auto BackwardRange() { return STLBackwardIteratorRange<T>{*this}; } + + // Constructs a const range (usable with range-based for) based on the + // BackwardIterator semantics. Note that this range does not provide + // full-feature STL-style iterators usable with STL-style algorithms. + auto BackwardRange() const { + return STLBackwardIteratorRange<const T>{*this}; + } + + // Constructs a const range (usable with range-based for and STL-style + // algorithms) based on a non-observing iterator. The array must not be + // modified during iteration. + auto NonObservingRange() const { + return mozilla::detail::IteratorRange< + typename AutoTArray<T, N>::const_iterator, + typename AutoTArray<T, N>::const_reverse_iterator>{mArray.cbegin(), + mArray.cend()}; + } + + protected: + AutoTArray<T, N> mArray; +}; + +template <class T> +class nsTObserverArray : public nsAutoTObserverArray<T, 0> { + public: + typedef nsAutoTObserverArray<T, 0> base_type; + typedef nsTObserverArray_base::size_type size_type; + + // + // Initialization methods + // + + nsTObserverArray() = default; + + // Initialize this array and pre-allocate some number of elements. + explicit nsTObserverArray(size_type aCapacity) { + base_type::mArray.SetCapacity(aCapacity); + } + + nsTObserverArray Clone() const { + auto result = nsTObserverArray{}; + result.mArray.Assign(this->mArray); + return result; + } +}; + +template <typename T, size_t N> +auto MakeBackInserter(nsAutoTObserverArray<T, N>& aArray) { + return mozilla::nsTArrayBackInserter<T, nsAutoTObserverArray<T, N>>{aArray}; +} + +template <typename T, size_t N> +inline void ImplCycleCollectionUnlink(nsAutoTObserverArray<T, N>& aField) { + aField.Clear(); +} + +template <typename T, size_t N> +inline void ImplCycleCollectionTraverse( + nsCycleCollectionTraversalCallback& aCallback, + nsAutoTObserverArray<T, N>& aField, const char* aName, + uint32_t aFlags = 0) { + aFlags |= CycleCollectionEdgeNameArrayFlag; + size_t length = aField.Length(); + for (size_t i = 0; i < length; ++i) { + ImplCycleCollectionTraverse(aCallback, aField.ElementAt(i), aName, aFlags); + } +} + +// Note that this macro only works if the array holds pointers to XPCOM objects. +#define NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(array_, func_, params_) \ + do { \ + for (RefPtr obs_ : (array_).ForwardRange()) { \ + obs_->func_ params_; \ + } \ + } while (0) + +// Note that this macro only works if the array holds pointers to XPCOM objects. +#define NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(array_, func_, params_) \ + do { \ + for (auto* obs_ : (array_).ForwardRange()) { \ + obs_->func_ params_; \ + } \ + } while (0) + +#endif // nsTObserverArray_h___ |