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