1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef nsTObserverArray_h___
8 #define nsTObserverArray_h___
10 #include "mozilla/MemoryReporting.h"
11 #include "mozilla/ReverseIterator.h"
13 #include "nsCycleCollectionNoteChild.h"
16 * An array of observers. Like a normal array, but supports iterators that are
17 * stable even if the array is modified during iteration.
18 * The template parameter T is the observer type the array will hold;
19 * N is the number of built-in storage slots that come with the array.
20 * NOTE: You probably want to use nsTObserverArray, unless you specifically
21 * want built-in storage. See below.
22 * @see nsTObserverArray, nsTArray
25 class nsTObserverArray_base
{
27 typedef size_t index_type
;
28 typedef size_t size_type
;
29 typedef ptrdiff_t diff_type
;
34 Iterator_base(const Iterator_base
&) = delete;
37 friend class nsTObserverArray_base
;
39 Iterator_base(index_type aPosition
, Iterator_base
* aNext
)
40 : mPosition(aPosition
), mNext(aNext
) {}
42 // The current position of the iterator. Its exact meaning differs
43 // depending on iterator. See nsTObserverArray<T>::ForwardIterator.
46 // The next iterator currently iterating the same array
50 nsTObserverArray_base() : mIterators(nullptr) {}
52 ~nsTObserverArray_base() {
53 NS_ASSERTION(mIterators
== nullptr, "iterators outlasting array");
57 * Adjusts iterators after an element has been inserted or removed
59 * @param aModPos Position where elements were added or removed.
60 * @param aAdjustment -1 if an element was removed, 1 if an element was
63 void AdjustIterators(index_type aModPos
, diff_type aAdjustment
);
66 * Clears iterators when the array is destroyed.
68 void ClearIterators();
70 mutable Iterator_base
* mIterators
;
73 template <class T
, size_t N
>
74 class nsAutoTObserverArray
: protected nsTObserverArray_base
{
77 typedef nsTArray
<T
> array_type
;
79 nsAutoTObserverArray() = default;
85 // @return The number of elements in the array.
86 size_type
Length() const { return mArray
.Length(); }
88 // @return True if the array is empty or false otherwise.
89 bool IsEmpty() const { return mArray
.IsEmpty(); }
91 // This method provides direct, readonly access to the array elements.
92 // @return A pointer to the first element of the array. If the array is
93 // empty, then this pointer must not be dereferenced.
94 const value_type
* Elements() const { return mArray
.Elements(); }
95 value_type
* Elements() { return mArray
.Elements(); }
97 // This method provides direct access to an element of the array. The given
98 // index must be within the array bounds. If the underlying array may change
99 // during iteration, use an iterator instead of this function.
100 // @param aIndex The index of an element in the array.
101 // @return A reference to the i'th element of the array.
102 value_type
& ElementAt(index_type aIndex
) { return mArray
.ElementAt(aIndex
); }
104 // Same as above, but readonly.
105 const value_type
& ElementAt(index_type aIndex
) const {
106 return mArray
.ElementAt(aIndex
);
109 // This method provides direct access to an element of the array in a bounds
110 // safe manner. If the requested index is out of bounds the provided default
111 // value is returned.
112 // @param aIndex The index of an element in the array.
113 // @param aDef The value to return if the index is out of bounds.
114 value_type
& SafeElementAt(index_type aIndex
, value_type
& aDef
) {
115 return mArray
.SafeElementAt(aIndex
, aDef
);
118 // Same as above, but readonly.
119 const value_type
& SafeElementAt(index_type aIndex
,
120 const value_type
& aDef
) const {
121 return mArray
.SafeElementAt(aIndex
, aDef
);
124 // No operator[] is provided because the point of this class is to support
125 // allow modifying the array during iteration, and ElementAt() is not safe
126 // in those conditions.
132 // This method searches, starting from the beginning of the array,
133 // for the first element in this array that is equal to the given element.
134 // 'operator==' must be defined for value_type.
135 // @param aItem The item to search for.
136 // @return true if the element was found.
137 template <class Item
>
138 bool Contains(const Item
& aItem
) const {
139 return IndexOf(aItem
) != array_type::NoIndex
;
142 // This method searches for the offset of the first element in this
143 // array that is equal to the given element.
144 // 'operator==' must be defined for value_type.
145 // @param aItem The item to search for.
146 // @param aStart The index to start from.
147 // @return The index of the found element or NoIndex if not found.
148 template <class Item
>
149 index_type
IndexOf(const Item
& aItem
, index_type aStart
= 0) const {
150 return mArray
.IndexOf(aItem
, aStart
);
157 // Insert a given element at the given index.
158 // @param aIndex The index at which to insert item.
159 // @param aItem The item to insert,
160 template <class Item
>
161 void InsertElementAt(index_type aIndex
, const Item
& aItem
) {
162 mArray
.InsertElementAt(aIndex
, aItem
);
163 AdjustIterators(aIndex
, 1);
166 // Same as above but without copy constructing.
167 // This is useful to avoid temporaries.
168 value_type
* InsertElementAt(index_type aIndex
) {
169 value_type
* item
= mArray
.InsertElementAt(aIndex
);
170 AdjustIterators(aIndex
, 1);
174 // Prepend an element to the array unless it already exists in the array.
175 // 'operator==' must be defined for value_type.
176 // @param aItem The item to prepend.
177 template <class Item
>
178 void PrependElementUnlessExists(const Item
& aItem
) {
179 if (!Contains(aItem
)) {
180 mArray
.InsertElementAt(0, aItem
);
181 AdjustIterators(0, 1);
185 // Append an element to the array.
186 // @param aItem The item to append.
187 template <class Item
>
188 void AppendElement(Item
&& aItem
) {
189 mArray
.AppendElement(std::forward
<Item
>(aItem
));
192 // Same as above, but without copy-constructing. This is useful to avoid
194 value_type
* AppendElement() { return mArray
.AppendElement(); }
196 // Append an element to the array unless it already exists in the array.
197 // 'operator==' must be defined for value_type.
198 // @param aItem The item to append.
199 template <class Item
>
200 void AppendElementUnlessExists(const Item
& aItem
) {
201 if (!Contains(aItem
)) {
202 mArray
.AppendElement(aItem
);
206 // Remove an element from the array.
207 // @param aIndex The index of the item to remove.
208 void RemoveElementAt(index_type aIndex
) {
209 NS_ASSERTION(aIndex
< mArray
.Length(), "invalid index");
210 mArray
.RemoveElementAt(aIndex
);
211 AdjustIterators(aIndex
, -1);
214 // This helper function combines IndexOf with RemoveElementAt to "search
215 // and destroy" the first element that is equal to the given element.
216 // 'operator==' must be defined for value_type.
217 // @param aItem The item to search for.
218 // @return true if the element was found and removed.
219 template <class Item
>
220 bool RemoveElement(const Item
& aItem
) {
221 index_type index
= mArray
.IndexOf(aItem
, 0);
222 if (index
== array_type::NoIndex
) {
226 mArray
.RemoveElementAt(index
);
227 AdjustIterators(index
, -1);
231 // See nsTArray::RemoveElementsBy. Neither the predicate nor the removal of
232 // elements from the array must have any side effects that modify the array.
233 template <typename Predicate
>
234 void NonObservingRemoveElementsBy(Predicate aPredicate
) {
236 mArray
.RemoveElementsBy([&](const value_type
& aItem
) {
237 if (aPredicate(aItem
)) {
238 // This element is going to be removed.
239 AdjustIterators(i
, -1);
247 // Removes all observers and collapses all iterators to the beginning of
248 // the array. The result is that forward iterators will see all elements
255 // Compact the array to minimize the memory it uses
256 void Compact() { mArray
.Compact(); }
258 // Returns the number of bytes on the heap taken up by this object, not
259 // including sizeof(*this). If you want to measure anything hanging off the
260 // array, you must iterate over the elements and measure them individually;
261 // hence the "Shallow" prefix.
262 size_t ShallowSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf
) const {
263 return mArray
.ShallowSizeOfExcludingThis(aMallocSizeOf
);
270 // Base class for iterators. Do not use this directly.
271 class Iterator
: public Iterator_base
{
273 friend class nsAutoTObserverArray
;
274 typedef nsAutoTObserverArray
<T
, N
> array_type
;
276 Iterator(const Iterator
& aOther
)
277 : Iterator(aOther
.mPosition
, aOther
.mArray
) {}
279 Iterator(index_type aPosition
, const array_type
& aArray
)
280 : Iterator_base(aPosition
, aArray
.mIterators
),
281 mArray(const_cast<array_type
&>(aArray
)) {
282 aArray
.mIterators
= this;
286 NS_ASSERTION(mArray
.mIterators
== this,
287 "Iterators must currently be destroyed in opposite order "
288 "from the construction order. It is suggested that you "
289 "simply put them on the stack");
290 mArray
.mIterators
= mNext
;
293 // The array we're iterating
297 // Iterates the array forward from beginning to end. mPosition points
298 // to the element that will be returned on next call to GetNext.
300 // - prepended to the array during iteration *will not* be traversed
301 // - appended during iteration *will* be traversed
302 // - removed during iteration *will not* be traversed.
303 // @see EndLimitedIterator
304 class ForwardIterator
: protected Iterator
{
306 typedef nsAutoTObserverArray
<T
, N
> array_type
;
307 typedef Iterator base_type
;
309 explicit ForwardIterator(const array_type
& aArray
) : Iterator(0, aArray
) {}
311 ForwardIterator(const array_type
& aArray
, index_type aPos
)
312 : Iterator(aPos
, aArray
) {}
314 bool operator<(const ForwardIterator
& aOther
) const {
315 NS_ASSERTION(&this->mArray
== &aOther
.mArray
,
316 "not iterating the same array");
317 return base_type::mPosition
< aOther
.mPosition
;
320 // Returns true if there are more elements to iterate.
321 // This must precede a call to GetNext(). If false is
322 // returned, GetNext() must not be called.
323 bool HasMore() const {
324 return base_type::mPosition
< base_type::mArray
.Length();
327 // Returns the next element and steps one step. This must
328 // be preceded by a call to HasMore().
329 // @return The next observer.
330 value_type
& GetNext() {
331 NS_ASSERTION(HasMore(), "iterating beyond end of array");
332 return base_type::mArray
.Elements()[base_type::mPosition
++];
335 // Removes the element at the current iterator position.
336 // (the last element returned from |GetNext()|)
337 // This will not affect the next call to |GetNext()|
339 return base_type::mArray
.RemoveElementAt(base_type::mPosition
- 1);
343 // EndLimitedIterator works like ForwardIterator, but will not iterate new
344 // observers appended to the array after the iterator was created.
345 class EndLimitedIterator
: protected ForwardIterator
{
347 typedef nsAutoTObserverArray
<T
, N
> array_type
;
348 typedef Iterator base_type
;
350 explicit EndLimitedIterator(const array_type
& aArray
)
351 : ForwardIterator(aArray
), mEnd(aArray
, aArray
.Length()) {}
353 // Returns true if there are more elements to iterate.
354 // This must precede a call to GetNext(). If false is
355 // returned, GetNext() must not be called.
356 bool HasMore() const { return *this < mEnd
; }
358 // Returns the next element and steps one step. This must
359 // be preceded by a call to HasMore().
360 // @return The next observer.
361 value_type
& GetNext() {
362 NS_ASSERTION(HasMore(), "iterating beyond end of array");
363 return base_type::mArray
.Elements()[base_type::mPosition
++];
366 // Removes the element at the current iterator position.
367 // (the last element returned from |GetNext()|)
368 // This will not affect the next call to |GetNext()|
370 return base_type::mArray
.RemoveElementAt(base_type::mPosition
- 1);
374 ForwardIterator mEnd
;
377 // Iterates the array backward from end to start. mPosition points
378 // to the element that was returned last.
380 // - prepended to the array during iteration *will* be traversed,
381 // unless the iteration already arrived at the first element
382 // - appended during iteration *will not* be traversed
383 // - removed during iteration *will not* be traversed.
384 class BackwardIterator
: protected Iterator
{
386 typedef nsAutoTObserverArray
<T
, N
> array_type
;
387 typedef Iterator base_type
;
389 explicit BackwardIterator(const array_type
& aArray
)
390 : Iterator(aArray
.Length(), aArray
) {}
392 // Returns true if there are more elements to iterate.
393 // This must precede a call to GetNext(). If false is
394 // returned, GetNext() must not be called.
395 bool HasMore() const { return base_type::mPosition
> 0; }
397 // Returns the next element and steps one step. This must
398 // be preceded by a call to HasMore().
399 // @return The next observer.
400 value_type
& GetNext() {
401 NS_ASSERTION(HasMore(), "iterating beyond start of array");
402 return base_type::mArray
.Elements()[--base_type::mPosition
];
405 // Removes the element at the current iterator position.
406 // (the last element returned from |GetNext()|)
407 // This will not affect the next call to |GetNext()|
409 return base_type::mArray
.RemoveElementAt(base_type::mPosition
);
413 struct EndSentinel
{};
415 // Internal type, do not use directly, see
416 // ForwardRange()/EndLimitedRange()/BackwardRange().
417 template <typename Iterator
, typename U
>
419 using value_type
= std::remove_reference_t
<U
>;
421 explicit STLIterator(const nsAutoTObserverArray
<T
, N
>& aArray
)
422 : mIterator
{aArray
} {
426 bool operator!=(const EndSentinel
&) const {
427 // We are a non-end-sentinel and the other is an end-sentinel, so we are
428 // still valid if mCurrent is valid.
432 STLIterator
& operator++() {
433 mCurrent
= mIterator
.HasMore() ? &mIterator
.GetNext() : nullptr;
437 value_type
* operator->() { return mCurrent
; }
438 U
& operator*() { return *mCurrent
; }
442 value_type
* mCurrent
;
445 // Internal type, do not use directly, see
446 // ForwardRange()/EndLimitedRange()/BackwardRange().
447 template <typename Iterator
, typename U
>
448 class STLIteratorRange
{
450 using iterator
= STLIterator
<Iterator
, U
>;
452 explicit STLIteratorRange(const nsAutoTObserverArray
<T
, N
>& aArray
)
455 STLIteratorRange(const STLIteratorRange
& aOther
) = delete;
457 iterator
begin() const { return iterator
{mArray
}; }
458 EndSentinel
end() const { return {}; }
461 const nsAutoTObserverArray
<T
, N
>& mArray
;
464 template <typename U
>
465 using STLForwardIteratorRange
= STLIteratorRange
<ForwardIterator
, U
>;
467 template <typename U
>
468 using STLEndLimitedIteratorRange
= STLIteratorRange
<EndLimitedIterator
, U
>;
470 template <typename U
>
471 using STLBackwardIteratorRange
= STLIteratorRange
<BackwardIterator
, U
>;
473 // Constructs a range (usable with range-based for) based on the
474 // ForwardIterator semantics. Note that this range does not provide
475 // full-feature STL-style iterators usable with STL-style algorithms.
476 auto ForwardRange() { return STLForwardIteratorRange
<T
>{*this}; }
478 // Constructs a const range (usable with range-based for) based on the
479 // ForwardIterator semantics. Note that this range does not provide
480 // full-feature STL-style iterators usable with STL-style algorithms.
481 auto ForwardRange() const { return STLForwardIteratorRange
<const T
>{*this}; }
483 // Constructs a range (usable with range-based for) based on the
484 // EndLimitedIterator semantics. Note that this range does not provide
485 // full-feature STL-style iterators usable with STL-style algorithms.
486 auto EndLimitedRange() { return STLEndLimitedIteratorRange
<T
>{*this}; }
488 // Constructs a const range (usable with range-based for) based on the
489 // EndLimitedIterator semantics. Note that this range does not provide
490 // full-feature STL-style iterators usable with STL-style algorithms.
491 auto EndLimitedRange() const {
492 return STLEndLimitedIteratorRange
<const T
>{*this};
495 // Constructs a range (usable with range-based for) based on the
496 // BackwardIterator semantics. Note that this range does not provide
497 // full-feature STL-style iterators usable with STL-style algorithms.
498 auto BackwardRange() { return STLBackwardIteratorRange
<T
>{*this}; }
500 // Constructs a const range (usable with range-based for) based on the
501 // BackwardIterator semantics. Note that this range does not provide
502 // full-feature STL-style iterators usable with STL-style algorithms.
503 auto BackwardRange() const {
504 return STLBackwardIteratorRange
<const T
>{*this};
507 // Constructs a const range (usable with range-based for and STL-style
508 // algorithms) based on a non-observing iterator. The array must not be
509 // modified during iteration.
510 auto NonObservingRange() const {
511 return mozilla::detail::IteratorRange
<
512 typename AutoTArray
<T
, N
>::const_iterator
,
513 typename AutoTArray
<T
, N
>::const_reverse_iterator
>{mArray
.cbegin(),
518 AutoTArray
<T
, N
> mArray
;
522 class nsTObserverArray
: public nsAutoTObserverArray
<T
, 0> {
524 typedef nsAutoTObserverArray
<T
, 0> base_type
;
525 typedef nsTObserverArray_base::size_type size_type
;
528 // Initialization methods
531 nsTObserverArray() = default;
533 // Initialize this array and pre-allocate some number of elements.
534 explicit nsTObserverArray(size_type aCapacity
) {
535 base_type::mArray
.SetCapacity(aCapacity
);
538 nsTObserverArray
Clone() const {
539 auto result
= nsTObserverArray
{};
540 result
.mArray
.Assign(this->mArray
);
545 template <typename T
, size_t N
>
546 auto MakeBackInserter(nsAutoTObserverArray
<T
, N
>& aArray
) {
547 return mozilla::nsTArrayBackInserter
<T
, nsAutoTObserverArray
<T
, N
>>{aArray
};
550 template <typename T
, size_t N
>
551 inline void ImplCycleCollectionUnlink(nsAutoTObserverArray
<T
, N
>& aField
) {
555 template <typename T
, size_t N
>
556 inline void ImplCycleCollectionTraverse(
557 nsCycleCollectionTraversalCallback
& aCallback
,
558 nsAutoTObserverArray
<T
, N
>& aField
, const char* aName
,
559 uint32_t aFlags
= 0) {
560 aFlags
|= CycleCollectionEdgeNameArrayFlag
;
561 size_t length
= aField
.Length();
562 for (size_t i
= 0; i
< length
; ++i
) {
563 ImplCycleCollectionTraverse(aCallback
, aField
.ElementAt(i
), aName
, aFlags
);
567 // Note that this macro only works if the array holds pointers to XPCOM objects.
568 #define NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(array_, func_, params_) \
570 for (RefPtr obs_ : (array_).ForwardRange()) { \
571 obs_->func_ params_; \
575 // Note that this macro only works if the array holds pointers to XPCOM objects.
576 #define NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(array_, func_, params_) \
578 for (auto* obs_ : (array_).ForwardRange()) { \
579 obs_->func_ params_; \
583 #endif // nsTObserverArray_h___