|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #ifndef nsTObserverArray_h___ |
|
7 #define nsTObserverArray_h___ |
|
8 |
|
9 #include "mozilla/MemoryReporting.h" |
|
10 #include "nsTArray.h" |
|
11 #include "nsCycleCollectionNoteChild.h" |
|
12 |
|
13 /** |
|
14 * An array of observers. Like a normal array, but supports iterators that are |
|
15 * stable even if the array is modified during iteration. |
|
16 * The template parameter T is the observer type the array will hold; |
|
17 * N is the number of built-in storage slots that come with the array. |
|
18 * NOTE: You probably want to use nsTObserverArray, unless you specifically |
|
19 * want built-in storage. See below. |
|
20 * @see nsTObserverArray, nsTArray |
|
21 */ |
|
22 |
|
23 class NS_COM_GLUE nsTObserverArray_base { |
|
24 public: |
|
25 typedef uint32_t index_type; |
|
26 typedef uint32_t size_type; |
|
27 typedef int32_t diff_type; |
|
28 |
|
29 protected: |
|
30 class Iterator_base { |
|
31 protected: |
|
32 friend class nsTObserverArray_base; |
|
33 |
|
34 Iterator_base(index_type aPosition, Iterator_base* aNext) |
|
35 : mPosition(aPosition), |
|
36 mNext(aNext) { |
|
37 } |
|
38 |
|
39 // The current position of the iterator. Its exact meaning differs |
|
40 // depending on iterator. See nsTObserverArray<T>::ForwardIterator. |
|
41 index_type mPosition; |
|
42 |
|
43 // The next iterator currently iterating the same array |
|
44 Iterator_base* mNext; |
|
45 }; |
|
46 |
|
47 nsTObserverArray_base() |
|
48 : mIterators(nullptr) { |
|
49 } |
|
50 |
|
51 ~nsTObserverArray_base() { |
|
52 NS_ASSERTION(mIterators == nullptr, "iterators outlasting array"); |
|
53 } |
|
54 |
|
55 /** |
|
56 * Adjusts iterators after an element has been inserted or removed |
|
57 * from the array. |
|
58 * @param modPos Position where elements were added or removed. |
|
59 * @param adjustment -1 if an element was removed, 1 if an element was |
|
60 * added. |
|
61 */ |
|
62 void AdjustIterators(index_type aModPos, diff_type aAdjustment); |
|
63 |
|
64 /** |
|
65 * Clears iterators when the array is destroyed. |
|
66 */ |
|
67 void ClearIterators(); |
|
68 |
|
69 mutable Iterator_base* mIterators; |
|
70 }; |
|
71 |
|
72 template<class T, uint32_t N> |
|
73 class nsAutoTObserverArray : protected nsTObserverArray_base { |
|
74 public: |
|
75 typedef T elem_type; |
|
76 typedef nsTArray<T> array_type; |
|
77 |
|
78 nsAutoTObserverArray() { |
|
79 } |
|
80 |
|
81 // |
|
82 // Accessor methods |
|
83 // |
|
84 |
|
85 // @return The number of elements in the array. |
|
86 size_type Length() const { |
|
87 return mArray.Length(); |
|
88 } |
|
89 |
|
90 // @return True if the array is empty or false otherwise. |
|
91 bool IsEmpty() const { |
|
92 return mArray.IsEmpty(); |
|
93 } |
|
94 |
|
95 // This method provides direct access to the i'th element of the array. |
|
96 // The given index must be within the array bounds. If the underlying array |
|
97 // may change during iteration, use an iterator instead of this function. |
|
98 // @param i The index of an element in the array. |
|
99 // @return A reference to the i'th element of the array. |
|
100 elem_type& ElementAt(index_type i) { |
|
101 return mArray.ElementAt(i); |
|
102 } |
|
103 |
|
104 // Same as above, but readonly. |
|
105 const elem_type& ElementAt(index_type i) const { |
|
106 return mArray.ElementAt(i); |
|
107 } |
|
108 |
|
109 // This method provides direct access to the i'th element of the array in |
|
110 // a bounds safe manner. If the requested index is out of bounds the |
|
111 // provided default value is returned. |
|
112 // @param i The index of an element in the array. |
|
113 // @param def The value to return if the index is out of bounds. |
|
114 elem_type& SafeElementAt(index_type i, elem_type& def) { |
|
115 return mArray.SafeElementAt(i, def); |
|
116 } |
|
117 |
|
118 // Same as above, but readonly. |
|
119 const elem_type& SafeElementAt(index_type i, const elem_type& def) const { |
|
120 return mArray.SafeElementAt(i, def); |
|
121 } |
|
122 |
|
123 // No operator[] is provided because the point of this class is to support |
|
124 // allow modifying the array during iteration, and ElementAt() is not safe |
|
125 // in those conditions. |
|
126 |
|
127 // |
|
128 // Search methods |
|
129 // |
|
130 |
|
131 // This method searches, starting from the beginning of the array, |
|
132 // for the first element in this array that is equal to the given element. |
|
133 // 'operator==' must be defined for elem_type. |
|
134 // @param item The item to search for. |
|
135 // @return true if the element was found. |
|
136 template<class Item> |
|
137 bool Contains(const Item& item) const { |
|
138 return IndexOf(item) != array_type::NoIndex; |
|
139 } |
|
140 |
|
141 // This method searches for the offset of the first element in this |
|
142 // array that is equal to the given element. |
|
143 // 'operator==' must be defined for elem_type. |
|
144 // @param item The item to search for. |
|
145 // @param start The index to start from. |
|
146 // @return The index of the found element or NoIndex if not found. |
|
147 template<class Item> |
|
148 index_type IndexOf(const Item& item, index_type start = 0) const { |
|
149 return mArray.IndexOf(item, start); |
|
150 } |
|
151 |
|
152 // |
|
153 // Mutation methods |
|
154 // |
|
155 |
|
156 // Insert a given element at the given index. |
|
157 // @param index The index at which to insert item. |
|
158 // @param item The item to insert, |
|
159 // @return A pointer to the newly inserted element, or a null on DOM |
|
160 template<class Item> |
|
161 elem_type *InsertElementAt(index_type aIndex, const Item& aItem) { |
|
162 elem_type* item = mArray.InsertElementAt(aIndex, aItem); |
|
163 AdjustIterators(aIndex, 1); |
|
164 return item; |
|
165 } |
|
166 |
|
167 // Same as above but without copy constructing. |
|
168 // This is useful to avoid temporaries. |
|
169 elem_type* InsertElementAt(index_type aIndex) { |
|
170 elem_type* item = mArray.InsertElementAt(aIndex); |
|
171 AdjustIterators(aIndex, 1); |
|
172 return item; |
|
173 } |
|
174 |
|
175 // Prepend an element to the array unless it already exists in the array. |
|
176 // 'operator==' must be defined for elem_type. |
|
177 // @param item The item to prepend. |
|
178 // @return true if the element was found, or inserted successfully. |
|
179 template<class Item> |
|
180 bool PrependElementUnlessExists(const Item& item) { |
|
181 if (Contains(item)) { |
|
182 return true; |
|
183 } |
|
184 |
|
185 bool inserted = mArray.InsertElementAt(0, item) != nullptr; |
|
186 AdjustIterators(0, 1); |
|
187 return inserted; |
|
188 } |
|
189 |
|
190 // Append an element to the array. |
|
191 // @param item The item to append. |
|
192 // @return A pointer to the newly appended element, or null on OOM. |
|
193 template<class Item> |
|
194 elem_type* AppendElement(const Item& item) { |
|
195 return mArray.AppendElement(item); |
|
196 } |
|
197 |
|
198 // Same as above, but without copy-constructing. This is useful to avoid |
|
199 // temporaries. |
|
200 elem_type* AppendElement() { |
|
201 return mArray.AppendElement(); |
|
202 } |
|
203 |
|
204 // Append an element to the array unless it already exists in the array. |
|
205 // 'operator==' must be defined for elem_type. |
|
206 // @param item The item to append. |
|
207 // @return true if the element was found, or inserted successfully. |
|
208 template<class Item> |
|
209 bool AppendElementUnlessExists(const Item& item) { |
|
210 return Contains(item) || AppendElement(item) != nullptr; |
|
211 } |
|
212 |
|
213 // Remove an element from the array. |
|
214 // @param index The index of the item to remove. |
|
215 void RemoveElementAt(index_type index) { |
|
216 NS_ASSERTION(index < mArray.Length(), "invalid index"); |
|
217 mArray.RemoveElementAt(index); |
|
218 AdjustIterators(index, -1); |
|
219 } |
|
220 |
|
221 // This helper function combines IndexOf with RemoveElementAt to "search |
|
222 // and destroy" the first element that is equal to the given element. |
|
223 // 'operator==' must be defined for elem_type. |
|
224 // @param item The item to search for. |
|
225 // @return true if the element was found and removed. |
|
226 template<class Item> |
|
227 bool RemoveElement(const Item& item) { |
|
228 index_type index = mArray.IndexOf(item, 0); |
|
229 if (index == array_type::NoIndex) |
|
230 return false; |
|
231 |
|
232 mArray.RemoveElementAt(index); |
|
233 AdjustIterators(index, -1); |
|
234 return true; |
|
235 } |
|
236 |
|
237 // Removes all observers and collapses all iterators to the beginning of |
|
238 // the array. The result is that forward iterators will see all elements |
|
239 // in the array. |
|
240 void Clear() { |
|
241 mArray.Clear(); |
|
242 ClearIterators(); |
|
243 } |
|
244 |
|
245 // Returns the number of bytes on the heap taken up by this object, not |
|
246 // including sizeof(*this). |
|
247 size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const { |
|
248 return mArray.SizeOfExcludingThis(mallocSizeOf); |
|
249 } |
|
250 |
|
251 // |
|
252 // Iterators |
|
253 // |
|
254 |
|
255 // Base class for iterators. Do not use this directly. |
|
256 class Iterator : public Iterator_base { |
|
257 protected: |
|
258 friend class nsAutoTObserverArray; |
|
259 typedef nsAutoTObserverArray<T, N> array_type; |
|
260 |
|
261 Iterator(index_type aPosition, const array_type& aArray) |
|
262 : Iterator_base(aPosition, aArray.mIterators), |
|
263 mArray(const_cast<array_type&>(aArray)) { |
|
264 aArray.mIterators = this; |
|
265 } |
|
266 |
|
267 ~Iterator() { |
|
268 NS_ASSERTION(mArray.mIterators == this, |
|
269 "Iterators must currently be destroyed in opposite order " |
|
270 "from the construction order. It is suggested that you " |
|
271 "simply put them on the stack"); |
|
272 mArray.mIterators = mNext; |
|
273 } |
|
274 |
|
275 // The array we're iterating |
|
276 array_type& mArray; |
|
277 }; |
|
278 |
|
279 // Iterates the array forward from beginning to end. mPosition points |
|
280 // to the element that will be returned on next call to GetNext. |
|
281 // Elements: |
|
282 // - prepended to the array during iteration *will not* be traversed |
|
283 // - appended during iteration *will* be traversed |
|
284 // - removed during iteration *will not* be traversed. |
|
285 // @see EndLimitedIterator |
|
286 class ForwardIterator : protected Iterator { |
|
287 public: |
|
288 typedef nsAutoTObserverArray<T, N> array_type; |
|
289 typedef Iterator base_type; |
|
290 |
|
291 ForwardIterator(const array_type& aArray) |
|
292 : Iterator(0, aArray) { |
|
293 } |
|
294 |
|
295 ForwardIterator(const array_type& aArray, index_type aPos) |
|
296 : Iterator(aPos, aArray) { |
|
297 } |
|
298 |
|
299 bool operator <(const ForwardIterator& aOther) const { |
|
300 NS_ASSERTION(&this->mArray == &aOther.mArray, |
|
301 "not iterating the same array"); |
|
302 return base_type::mPosition < aOther.mPosition; |
|
303 } |
|
304 |
|
305 // Returns true if there are more elements to iterate. |
|
306 // This must precede a call to GetNext(). If false is |
|
307 // returned, GetNext() must not be called. |
|
308 bool HasMore() const { |
|
309 return base_type::mPosition < base_type::mArray.Length(); |
|
310 } |
|
311 |
|
312 // Returns the next element and steps one step. This must |
|
313 // be preceded by a call to HasMore(). |
|
314 // @return The next observer. |
|
315 elem_type& GetNext() { |
|
316 NS_ASSERTION(HasMore(), "iterating beyond end of array"); |
|
317 return base_type::mArray.ElementAt(base_type::mPosition++); |
|
318 } |
|
319 }; |
|
320 |
|
321 // EndLimitedIterator works like ForwardIterator, but will not iterate new |
|
322 // observers appended to the array after the iterator was created. |
|
323 class EndLimitedIterator : protected ForwardIterator { |
|
324 public: |
|
325 typedef nsAutoTObserverArray<T, N> array_type; |
|
326 typedef Iterator base_type; |
|
327 |
|
328 EndLimitedIterator(const array_type& aArray) |
|
329 : ForwardIterator(aArray), |
|
330 mEnd(aArray, aArray.Length()) { |
|
331 } |
|
332 |
|
333 // Returns true if there are more elements to iterate. |
|
334 // This must precede a call to GetNext(). If false is |
|
335 // returned, GetNext() must not be called. |
|
336 bool HasMore() const { |
|
337 return *this < mEnd; |
|
338 } |
|
339 |
|
340 // Returns the next element and steps one step. This must |
|
341 // be preceded by a call to HasMore(). |
|
342 // @return The next observer. |
|
343 elem_type& GetNext() { |
|
344 NS_ASSERTION(HasMore(), "iterating beyond end of array"); |
|
345 return base_type::mArray.ElementAt(base_type::mPosition++); |
|
346 } |
|
347 |
|
348 private: |
|
349 ForwardIterator mEnd; |
|
350 }; |
|
351 |
|
352 protected: |
|
353 nsAutoTArray<T, N> mArray; |
|
354 }; |
|
355 |
|
356 template<class T> |
|
357 class nsTObserverArray : public nsAutoTObserverArray<T, 0> { |
|
358 public: |
|
359 typedef nsAutoTObserverArray<T, 0> base_type; |
|
360 typedef nsTObserverArray_base::size_type size_type; |
|
361 |
|
362 // |
|
363 // Initialization methods |
|
364 // |
|
365 |
|
366 nsTObserverArray() {} |
|
367 |
|
368 // Initialize this array and pre-allocate some number of elements. |
|
369 explicit nsTObserverArray(size_type capacity) { |
|
370 base_type::mArray.SetCapacity(capacity); |
|
371 } |
|
372 }; |
|
373 |
|
374 template <typename T, uint32_t N> |
|
375 inline void |
|
376 ImplCycleCollectionUnlink(nsAutoTObserverArray<T, N>& aField) |
|
377 { |
|
378 aField.Clear(); |
|
379 } |
|
380 |
|
381 template <typename T, uint32_t N> |
|
382 inline void |
|
383 ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, |
|
384 nsAutoTObserverArray<T, N>& aField, |
|
385 const char* aName, |
|
386 uint32_t aFlags = 0) |
|
387 { |
|
388 aFlags |= CycleCollectionEdgeNameArrayFlag; |
|
389 uint32_t length = aField.Length(); |
|
390 for (uint32_t i = 0; i < length; ++i) { |
|
391 ImplCycleCollectionTraverse(aCallback, aField.ElementAt(i), aName, aFlags); |
|
392 } |
|
393 } |
|
394 |
|
395 // XXXbz I wish I didn't have to pass in the observer type, but I |
|
396 // don't see a way to get it out of array_. |
|
397 // Note that this macro only works if the array holds pointers to XPCOM objects. |
|
398 #define NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(array_, obstype_, func_, params_) \ |
|
399 PR_BEGIN_MACRO \ |
|
400 nsTObserverArray<obstype_ *>::ForwardIterator iter_(array_); \ |
|
401 nsRefPtr<obstype_> obs_; \ |
|
402 while (iter_.HasMore()) { \ |
|
403 obs_ = iter_.GetNext(); \ |
|
404 obs_ -> func_ params_ ; \ |
|
405 } \ |
|
406 PR_END_MACRO |
|
407 |
|
408 // Note that this macro only works if the array holds pointers to XPCOM objects. |
|
409 #define NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(array_, obstype_, func_, params_) \ |
|
410 PR_BEGIN_MACRO \ |
|
411 nsTObserverArray<obstype_ *>::ForwardIterator iter_(array_); \ |
|
412 obstype_* obs_; \ |
|
413 while (iter_.HasMore()) { \ |
|
414 obs_ = iter_.GetNext(); \ |
|
415 obs_ -> func_ params_ ; \ |
|
416 } \ |
|
417 PR_END_MACRO |
|
418 #endif // nsTObserverArray_h___ |