Bug 1929968 - Ensure the search unit tests use add_setup correctly. r=search-reviewer...
[gecko.git] / xpcom / ds / nsTObserverArray.h
blob4d3e4087e0337db972986309cc8f5fc425e20e73
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"
12 #include "nsTArray.h"
13 #include "nsCycleCollectionNoteChild.h"
15 /**
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 {
26 public:
27 typedef size_t index_type;
28 typedef size_t size_type;
29 typedef ptrdiff_t diff_type;
31 protected:
32 class Iterator_base {
33 public:
34 Iterator_base(const Iterator_base&) = delete;
36 protected:
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.
44 index_type mPosition;
46 // The next iterator currently iterating the same array
47 Iterator_base* mNext;
50 nsTObserverArray_base() : mIterators(nullptr) {}
52 ~nsTObserverArray_base() {
53 NS_ASSERTION(mIterators == nullptr, "iterators outlasting array");
56 /**
57 * Adjusts iterators after an element has been inserted or removed
58 * from the array.
59 * @param aModPos Position where elements were added or removed.
60 * @param aAdjustment -1 if an element was removed, 1 if an element was
61 * added.
63 void AdjustIterators(index_type aModPos, diff_type aAdjustment);
65 /**
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 {
75 public:
76 typedef T value_type;
77 typedef nsTArray<T> array_type;
79 nsAutoTObserverArray() = default;
82 // Accessor methods
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.
129 // Search methods
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);
154 // Mutation methods
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);
171 return item;
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
193 // temporaries.
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) {
223 return false;
226 mArray.RemoveElementAt(index);
227 AdjustIterators(index, -1);
228 return true;
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) {
235 index_type i = 0;
236 mArray.RemoveElementsBy([&](const value_type& aItem) {
237 if (aPredicate(aItem)) {
238 // This element is going to be removed.
239 AdjustIterators(i, -1);
240 return true;
242 ++i;
243 return false;
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
249 // in the array.
250 void Clear() {
251 mArray.Clear();
252 ClearIterators();
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);
267 // Iterators
270 // Base class for iterators. Do not use this directly.
271 class Iterator : public Iterator_base {
272 protected:
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;
285 ~Iterator() {
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
294 array_type& mArray;
297 // Iterates the array forward from beginning to end. mPosition points
298 // to the element that will be returned on next call to GetNext.
299 // Elements:
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 {
305 public:
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()|
338 void Remove() {
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 {
346 public:
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()|
369 void Remove() {
370 return base_type::mArray.RemoveElementAt(base_type::mPosition - 1);
373 private:
374 ForwardIterator mEnd;
377 // Iterates the array backward from end to start. mPosition points
378 // to the element that was returned last.
379 // Elements:
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 {
385 public:
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()|
408 void Remove() {
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>
418 struct STLIterator {
419 using value_type = std::remove_reference_t<U>;
421 explicit STLIterator(const nsAutoTObserverArray<T, N>& aArray)
422 : mIterator{aArray} {
423 operator++();
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.
429 return mCurrent;
432 STLIterator& operator++() {
433 mCurrent = mIterator.HasMore() ? &mIterator.GetNext() : nullptr;
434 return *this;
437 value_type* operator->() { return mCurrent; }
438 U& operator*() { return *mCurrent; }
440 private:
441 Iterator mIterator;
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 {
449 public:
450 using iterator = STLIterator<Iterator, U>;
452 explicit STLIteratorRange(const nsAutoTObserverArray<T, N>& aArray)
453 : mArray{aArray} {}
455 STLIteratorRange(const STLIteratorRange& aOther) = delete;
457 iterator begin() const { return iterator{mArray}; }
458 EndSentinel end() const { return {}; }
460 private:
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(),
514 mArray.cend()};
517 protected:
518 AutoTArray<T, N> mArray;
521 template <class T>
522 class nsTObserverArray : public nsAutoTObserverArray<T, 0> {
523 public:
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);
541 return result;
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) {
552 aField.Clear();
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_) \
569 do { \
570 for (RefPtr obs_ : (array_).ForwardRange()) { \
571 obs_->func_ params_; \
573 } while (0)
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_) \
577 do { \
578 for (auto* obs_ : (array_).ForwardRange()) { \
579 obs_->func_ params_; \
581 } while (0)
583 #endif // nsTObserverArray_h___