xpcom/glue/nsTObserverArray.h

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/xpcom/glue/nsTObserverArray.h	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,418 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#ifndef nsTObserverArray_h___
    1.10 +#define nsTObserverArray_h___
    1.11 +
    1.12 +#include "mozilla/MemoryReporting.h"
    1.13 +#include "nsTArray.h"
    1.14 +#include "nsCycleCollectionNoteChild.h"
    1.15 +
    1.16 +/**
    1.17 + * An array of observers. Like a normal array, but supports iterators that are
    1.18 + * stable even if the array is modified during iteration.
    1.19 + * The template parameter T is the observer type the array will hold;
    1.20 + * N is the number of built-in storage slots that come with the array.
    1.21 + * NOTE: You probably want to use nsTObserverArray, unless you specifically
    1.22 + * want built-in storage. See below.
    1.23 + * @see nsTObserverArray, nsTArray
    1.24 + */
    1.25 +
    1.26 +class NS_COM_GLUE nsTObserverArray_base {
    1.27 +  public:
    1.28 +    typedef uint32_t index_type;
    1.29 +    typedef uint32_t size_type;
    1.30 +    typedef int32_t  diff_type;
    1.31 +
    1.32 +  protected:
    1.33 +    class Iterator_base {
    1.34 +      protected:
    1.35 +        friend class nsTObserverArray_base;
    1.36 +
    1.37 +        Iterator_base(index_type aPosition, Iterator_base* aNext)
    1.38 +          : mPosition(aPosition),
    1.39 +            mNext(aNext) {
    1.40 +        }
    1.41 +
    1.42 +        // The current position of the iterator. Its exact meaning differs
    1.43 +        // depending on iterator. See nsTObserverArray<T>::ForwardIterator.
    1.44 +        index_type mPosition;
    1.45 +
    1.46 +        // The next iterator currently iterating the same array
    1.47 +        Iterator_base* mNext;
    1.48 +    };
    1.49 +
    1.50 +    nsTObserverArray_base()
    1.51 +      : mIterators(nullptr) {
    1.52 +    }
    1.53 +
    1.54 +    ~nsTObserverArray_base() {
    1.55 +      NS_ASSERTION(mIterators == nullptr, "iterators outlasting array");
    1.56 +    }
    1.57 +
    1.58 +    /**
    1.59 +     * Adjusts iterators after an element has been inserted or removed
    1.60 +     * from the array.
    1.61 +     * @param modPos     Position where elements were added or removed.
    1.62 +     * @param adjustment -1 if an element was removed, 1 if an element was
    1.63 +     *                   added.
    1.64 +     */
    1.65 +    void AdjustIterators(index_type aModPos, diff_type aAdjustment);
    1.66 +
    1.67 +    /**
    1.68 +     * Clears iterators when the array is destroyed.
    1.69 +     */
    1.70 +    void ClearIterators();
    1.71 +
    1.72 +    mutable Iterator_base* mIterators;
    1.73 +};
    1.74 +
    1.75 +template<class T, uint32_t N>
    1.76 +class nsAutoTObserverArray : protected nsTObserverArray_base {
    1.77 +  public:
    1.78 +    typedef T           elem_type;
    1.79 +    typedef nsTArray<T> array_type;
    1.80 +
    1.81 +    nsAutoTObserverArray() {
    1.82 +    }
    1.83 +
    1.84 +    //
    1.85 +    // Accessor methods
    1.86 +    //
    1.87 +
    1.88 +    // @return The number of elements in the array.
    1.89 +    size_type Length() const {
    1.90 +      return mArray.Length();
    1.91 +    }
    1.92 +
    1.93 +    // @return True if the array is empty or false otherwise.
    1.94 +    bool IsEmpty() const {
    1.95 +      return mArray.IsEmpty();
    1.96 +    }
    1.97 +
    1.98 +    // This method provides direct access to the i'th element of the array.
    1.99 +    // The given index must be within the array bounds. If the underlying array
   1.100 +    // may change during iteration, use an iterator instead of this function.
   1.101 +    // @param i  The index of an element in the array.
   1.102 +    // @return   A reference to the i'th element of the array.
   1.103 +    elem_type& ElementAt(index_type i) {
   1.104 +      return mArray.ElementAt(i);
   1.105 +    }
   1.106 +
   1.107 +    // Same as above, but readonly.
   1.108 +    const elem_type& ElementAt(index_type i) const {
   1.109 +      return mArray.ElementAt(i);
   1.110 +    }
   1.111 +
   1.112 +    // This method provides direct access to the i'th element of the array in
   1.113 +    // a bounds safe manner. If the requested index is out of bounds the
   1.114 +    // provided default value is returned.
   1.115 +    // @param i  The index of an element in the array.
   1.116 +    // @param def The value to return if the index is out of bounds.
   1.117 +    elem_type& SafeElementAt(index_type i, elem_type& def) {
   1.118 +      return mArray.SafeElementAt(i, def);
   1.119 +    }
   1.120 +
   1.121 +    // Same as above, but readonly.
   1.122 +    const elem_type& SafeElementAt(index_type i, const elem_type& def) const {
   1.123 +      return mArray.SafeElementAt(i, def);
   1.124 +    }
   1.125 +
   1.126 +    // No operator[] is provided because the point of this class is to support
   1.127 +    // allow modifying the array during iteration, and ElementAt() is not safe
   1.128 +    // in those conditions.
   1.129 +
   1.130 +    //
   1.131 +    // Search methods
   1.132 +    //
   1.133 +
   1.134 +    // This method searches, starting from the beginning of the array,
   1.135 +    // for the first element in this array that is equal to the given element.
   1.136 +    // 'operator==' must be defined for elem_type.
   1.137 +    // @param item   The item to search for.
   1.138 +    // @return       true if the element was found.
   1.139 +    template<class Item>
   1.140 +    bool Contains(const Item& item) const {
   1.141 +      return IndexOf(item) != array_type::NoIndex;
   1.142 +    }
   1.143 +
   1.144 +    // This method searches for the offset of the first element in this
   1.145 +    // array that is equal to the given element.
   1.146 +    // 'operator==' must be defined for elem_type.
   1.147 +    // @param item   The item to search for.
   1.148 +    // @param start  The index to start from.
   1.149 +    // @return       The index of the found element or NoIndex if not found.
   1.150 +    template<class Item>
   1.151 +    index_type IndexOf(const Item& item, index_type start = 0) const {
   1.152 +      return mArray.IndexOf(item, start);
   1.153 +    }
   1.154 +
   1.155 +    //
   1.156 +    // Mutation methods
   1.157 +    //
   1.158 +  
   1.159 +    // Insert a given element at the given index.
   1.160 +    // @param index  The index at which to insert item.
   1.161 +    // @param item   The item to insert,
   1.162 +    // @return       A pointer to the newly inserted element, or a null on DOM
   1.163 +    template<class Item>
   1.164 +    elem_type *InsertElementAt(index_type aIndex, const Item& aItem) {
   1.165 +      elem_type* item = mArray.InsertElementAt(aIndex, aItem);
   1.166 +      AdjustIterators(aIndex, 1);
   1.167 +      return item;
   1.168 +    }
   1.169 +
   1.170 +    // Same as above but without copy constructing.
   1.171 +    // This is useful to avoid temporaries.
   1.172 +    elem_type* InsertElementAt(index_type aIndex) {
   1.173 +      elem_type* item = mArray.InsertElementAt(aIndex);
   1.174 +      AdjustIterators(aIndex, 1);
   1.175 +      return item;
   1.176 +    }
   1.177 +
   1.178 +    // Prepend an element to the array unless it already exists in the array.
   1.179 +    // 'operator==' must be defined for elem_type.
   1.180 +    // @param item   The item to prepend.
   1.181 +    // @return       true if the element was found, or inserted successfully.
   1.182 +    template<class Item>
   1.183 +    bool PrependElementUnlessExists(const Item& item) {
   1.184 +      if (Contains(item)) {
   1.185 +        return true;
   1.186 +      }
   1.187 +      
   1.188 +      bool inserted = mArray.InsertElementAt(0, item) != nullptr;
   1.189 +      AdjustIterators(0, 1);
   1.190 +      return inserted;
   1.191 +    }
   1.192 +
   1.193 +    // Append an element to the array.
   1.194 +    // @param item   The item to append.
   1.195 +    // @return A pointer to the newly appended element, or null on OOM.
   1.196 +    template<class Item>
   1.197 +    elem_type* AppendElement(const Item& item) {
   1.198 +      return mArray.AppendElement(item);
   1.199 +    }
   1.200 +
   1.201 +    // Same as above, but without copy-constructing. This is useful to avoid
   1.202 +    // temporaries.
   1.203 +    elem_type* AppendElement() {
   1.204 +      return mArray.AppendElement();
   1.205 +    }
   1.206 +
   1.207 +    // Append an element to the array unless it already exists in the array.
   1.208 +    // 'operator==' must be defined for elem_type.
   1.209 +    // @param item   The item to append.
   1.210 +    // @return       true if the element was found, or inserted successfully.
   1.211 +    template<class Item>
   1.212 +    bool AppendElementUnlessExists(const Item& item) {
   1.213 +      return Contains(item) || AppendElement(item) != nullptr;
   1.214 +    }
   1.215 +
   1.216 +    // Remove an element from the array.
   1.217 +    // @param index  The index of the item to remove.
   1.218 +    void RemoveElementAt(index_type index) {
   1.219 +      NS_ASSERTION(index < mArray.Length(), "invalid index");
   1.220 +      mArray.RemoveElementAt(index);
   1.221 +      AdjustIterators(index, -1);
   1.222 +    }
   1.223 +
   1.224 +    // This helper function combines IndexOf with RemoveElementAt to "search
   1.225 +    // and destroy" the first element that is equal to the given element.
   1.226 +    // 'operator==' must be defined for elem_type.
   1.227 +    // @param item  The item to search for.
   1.228 +    // @return true if the element was found and removed.
   1.229 +    template<class Item>
   1.230 +    bool RemoveElement(const Item& item) {
   1.231 +      index_type index = mArray.IndexOf(item, 0);
   1.232 +      if (index == array_type::NoIndex)
   1.233 +        return false;
   1.234 +
   1.235 +      mArray.RemoveElementAt(index);
   1.236 +      AdjustIterators(index, -1);
   1.237 +      return true;
   1.238 +    }
   1.239 +
   1.240 +    // Removes all observers and collapses all iterators to the beginning of
   1.241 +    // the array. The result is that forward iterators will see all elements
   1.242 +    // in the array.
   1.243 +    void Clear() {
   1.244 +      mArray.Clear();
   1.245 +      ClearIterators();
   1.246 +    }
   1.247 +
   1.248 +    // Returns the number of bytes on the heap taken up by this object, not
   1.249 +    // including sizeof(*this).
   1.250 +    size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
   1.251 +      return mArray.SizeOfExcludingThis(mallocSizeOf);
   1.252 +    }
   1.253 +
   1.254 +    //
   1.255 +    // Iterators
   1.256 +    //
   1.257 +
   1.258 +    // Base class for iterators. Do not use this directly.
   1.259 +    class Iterator : public Iterator_base {
   1.260 +      protected:
   1.261 +        friend class nsAutoTObserverArray;
   1.262 +        typedef nsAutoTObserverArray<T, N> array_type;
   1.263 +
   1.264 +        Iterator(index_type aPosition, const array_type& aArray)
   1.265 +          : Iterator_base(aPosition, aArray.mIterators),
   1.266 +            mArray(const_cast<array_type&>(aArray)) {
   1.267 +          aArray.mIterators = this;
   1.268 +        }
   1.269 +
   1.270 +        ~Iterator() {
   1.271 +          NS_ASSERTION(mArray.mIterators == this,
   1.272 +                       "Iterators must currently be destroyed in opposite order "
   1.273 +                       "from the construction order. It is suggested that you "
   1.274 +                       "simply put them on the stack");
   1.275 +          mArray.mIterators = mNext;
   1.276 +        }
   1.277 +
   1.278 +        // The array we're iterating
   1.279 +        array_type& mArray;
   1.280 +    };
   1.281 +
   1.282 +    // Iterates the array forward from beginning to end. mPosition points
   1.283 +    // to the element that will be returned on next call to GetNext.
   1.284 +    // Elements:
   1.285 +    // - prepended to the array during iteration *will not* be traversed
   1.286 +    // - appended during iteration *will* be traversed
   1.287 +    // - removed during iteration *will not* be traversed.
   1.288 +    // @see EndLimitedIterator
   1.289 +    class ForwardIterator : protected Iterator {
   1.290 +      public:
   1.291 +        typedef nsAutoTObserverArray<T, N> array_type;
   1.292 +        typedef Iterator                   base_type;
   1.293 +
   1.294 +        ForwardIterator(const array_type& aArray)
   1.295 +          : Iterator(0, aArray) {
   1.296 +        }
   1.297 +
   1.298 +        ForwardIterator(const array_type& aArray, index_type aPos)
   1.299 +          : Iterator(aPos, aArray) {
   1.300 +        }
   1.301 +
   1.302 +        bool operator <(const ForwardIterator& aOther) const {
   1.303 +          NS_ASSERTION(&this->mArray == &aOther.mArray,
   1.304 +                       "not iterating the same array");
   1.305 +          return base_type::mPosition < aOther.mPosition;
   1.306 +        }
   1.307 +
   1.308 +        // Returns true if there are more elements to iterate.
   1.309 +        // This must precede a call to GetNext(). If false is
   1.310 +        // returned, GetNext() must not be called.
   1.311 +        bool HasMore() const {
   1.312 +          return base_type::mPosition < base_type::mArray.Length();
   1.313 +        }
   1.314 +
   1.315 +        // Returns the next element and steps one step. This must
   1.316 +        // be preceded by a call to HasMore().
   1.317 +        // @return The next observer.
   1.318 +        elem_type& GetNext() {
   1.319 +          NS_ASSERTION(HasMore(), "iterating beyond end of array");
   1.320 +          return base_type::mArray.ElementAt(base_type::mPosition++);
   1.321 +        }
   1.322 +    };
   1.323 +
   1.324 +    // EndLimitedIterator works like ForwardIterator, but will not iterate new
   1.325 +    // observers appended to the array after the iterator was created.
   1.326 +    class EndLimitedIterator : protected ForwardIterator {
   1.327 +      public:
   1.328 +        typedef nsAutoTObserverArray<T, N> array_type;
   1.329 +        typedef Iterator                   base_type;
   1.330 +
   1.331 +        EndLimitedIterator(const array_type& aArray)
   1.332 +          : ForwardIterator(aArray),
   1.333 +            mEnd(aArray, aArray.Length()) {
   1.334 +        }
   1.335 +
   1.336 +        // Returns true if there are more elements to iterate.
   1.337 +        // This must precede a call to GetNext(). If false is
   1.338 +        // returned, GetNext() must not be called.
   1.339 +        bool HasMore() const {
   1.340 +          return *this < mEnd;
   1.341 +        }
   1.342 +
   1.343 +        // Returns the next element and steps one step. This must
   1.344 +        // be preceded by a call to HasMore().
   1.345 +        // @return The next observer.
   1.346 +        elem_type& GetNext() {
   1.347 +          NS_ASSERTION(HasMore(), "iterating beyond end of array");
   1.348 +          return base_type::mArray.ElementAt(base_type::mPosition++);
   1.349 +        }
   1.350 +
   1.351 +      private:
   1.352 +        ForwardIterator mEnd;
   1.353 +    };
   1.354 +
   1.355 +  protected:
   1.356 +    nsAutoTArray<T, N> mArray;
   1.357 +};
   1.358 +
   1.359 +template<class T>
   1.360 +class nsTObserverArray : public nsAutoTObserverArray<T, 0> {
   1.361 +  public:
   1.362 +    typedef nsAutoTObserverArray<T, 0>       base_type;
   1.363 +    typedef nsTObserverArray_base::size_type size_type;
   1.364 +
   1.365 +    //
   1.366 +    // Initialization methods
   1.367 +    //
   1.368 +
   1.369 +    nsTObserverArray() {}
   1.370 +
   1.371 +    // Initialize this array and pre-allocate some number of elements.
   1.372 +    explicit nsTObserverArray(size_type capacity) {
   1.373 +      base_type::mArray.SetCapacity(capacity);
   1.374 +    }
   1.375 +};
   1.376 +
   1.377 +template <typename T, uint32_t N>
   1.378 +inline void
   1.379 +ImplCycleCollectionUnlink(nsAutoTObserverArray<T, N>& aField)
   1.380 +{
   1.381 +  aField.Clear();
   1.382 +}
   1.383 +
   1.384 +template <typename T, uint32_t N>
   1.385 +inline void
   1.386 +ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
   1.387 +                            nsAutoTObserverArray<T, N>& aField,
   1.388 +                            const char* aName,
   1.389 +                            uint32_t aFlags = 0)
   1.390 +{
   1.391 +  aFlags |= CycleCollectionEdgeNameArrayFlag;
   1.392 +  uint32_t length = aField.Length();
   1.393 +  for (uint32_t i = 0; i < length; ++i) {
   1.394 +    ImplCycleCollectionTraverse(aCallback, aField.ElementAt(i), aName, aFlags);
   1.395 +  }
   1.396 +}
   1.397 +
   1.398 +// XXXbz I wish I didn't have to pass in the observer type, but I
   1.399 +// don't see a way to get it out of array_.
   1.400 +// Note that this macro only works if the array holds pointers to XPCOM objects.
   1.401 +#define NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(array_, obstype_, func_, params_) \
   1.402 +  PR_BEGIN_MACRO                                                             \
   1.403 +    nsTObserverArray<obstype_ *>::ForwardIterator iter_(array_);             \
   1.404 +    nsRefPtr<obstype_> obs_;                                                 \
   1.405 +    while (iter_.HasMore()) {                                                 \
   1.406 +      obs_ = iter_.GetNext();                                                \
   1.407 +      obs_ -> func_ params_ ;                                                \
   1.408 +    }                                                                        \
   1.409 +  PR_END_MACRO
   1.410 +
   1.411 +// Note that this macro only works if the array holds pointers to XPCOM objects.
   1.412 +#define NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(array_, obstype_, func_, params_) \
   1.413 +  PR_BEGIN_MACRO                                                             \
   1.414 +    nsTObserverArray<obstype_ *>::ForwardIterator iter_(array_);             \
   1.415 +    obstype_* obs_;                                                          \
   1.416 +    while (iter_.HasMore()) {                                                \
   1.417 +      obs_ = iter_.GetNext();                                                \
   1.418 +      obs_ -> func_ params_ ;                                                \
   1.419 +    }                                                                        \
   1.420 +  PR_END_MACRO
   1.421 +#endif // nsTObserverArray_h___

mercurial