gecko/xpcom/glue/nsTObserverArray.h

491 lines
15 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsTObserverArray_h___
#define nsTObserverArray_h___
#include "mozilla/MemoryReporting.h"
#include "nsTArray.h"
#include "nsCycleCollectionNoteChild.h"
/**
* An array of observers. Like a normal array, but supports iterators that are
* stable even if the array is modified during iteration.
* The template parameter T is the observer type the array will hold;
* N is the number of built-in storage slots that come with the array.
* NOTE: You probably want to use nsTObserverArray, unless you specifically
* want built-in storage. See below.
* @see nsTObserverArray, nsTArray
*/
class NS_COM_GLUE nsTObserverArray_base
{
public:
typedef size_t index_type;
typedef size_t size_type;
typedef ptrdiff_t diff_type;
protected:
class Iterator_base
{
protected:
friend class nsTObserverArray_base;
Iterator_base(index_type aPosition, Iterator_base* aNext)
: mPosition(aPosition)
, mNext(aNext)
{
}
// The current position of the iterator. Its exact meaning differs
// depending on iterator. See nsTObserverArray<T>::ForwardIterator.
index_type mPosition;
// The next iterator currently iterating the same array
Iterator_base* mNext;
};
nsTObserverArray_base() : mIterators(nullptr) {}
~nsTObserverArray_base()
{
NS_ASSERTION(mIterators == nullptr, "iterators outlasting array");
}
/**
* Adjusts iterators after an element has been inserted or removed
* from the array.
* @param aModPos Position where elements were added or removed.
* @param aAdjustment -1 if an element was removed, 1 if an element was
* added.
*/
void AdjustIterators(index_type aModPos, diff_type aAdjustment);
/**
* Clears iterators when the array is destroyed.
*/
void ClearIterators();
mutable Iterator_base* mIterators;
};
template<class T, size_t N>
class nsAutoTObserverArray : protected nsTObserverArray_base
{
public:
typedef T elem_type;
typedef nsTArray<T> array_type;
nsAutoTObserverArray() {}
//
// Accessor methods
//
// @return The number of elements in the array.
size_type Length() const { return mArray.Length(); }
// @return True if the array is empty or false otherwise.
bool IsEmpty() const { return mArray.IsEmpty(); }
// This method provides direct access to an element of the array. The given
// index must be within the array bounds. If the underlying array may change
// during iteration, use an iterator instead of this function.
// @param aIndex The index of an element in the array.
// @return A reference to the i'th element of the array.
elem_type& ElementAt(index_type aIndex)
{
return mArray.ElementAt(aIndex);
}
// Same as above, but readonly.
const elem_type& ElementAt(index_type aIndex) const
{
return mArray.ElementAt(aIndex);
}
// This method provides direct access to an element of the array in a bounds
// safe manner. If the requested index is out of bounds the provided default
// value is returned.
// @param aIndex The index of an element in the array.
// @param aDef The value to return if the index is out of bounds.
elem_type& SafeElementAt(index_type aIndex, elem_type& aDef)
{
return mArray.SafeElementAt(aIndex, aDef);
}
// Same as above, but readonly.
const elem_type& SafeElementAt(index_type aIndex, const elem_type& aDef) const
{
return mArray.SafeElementAt(aIndex, aDef);
}
// No operator[] is provided because the point of this class is to support
// allow modifying the array during iteration, and ElementAt() is not safe
// in those conditions.
//
// Search methods
//
// This method searches, starting from the beginning of the array,
// for the first element in this array that is equal to the given element.
// 'operator==' must be defined for elem_type.
// @param aItem The item to search for.
// @return true if the element was found.
template<class Item>
bool Contains(const Item& aItem) const
{
return IndexOf(aItem) != array_type::NoIndex;
}
// This method searches for the offset of the first element in this
// array that is equal to the given element.
// 'operator==' must be defined for elem_type.
// @param aItem The item to search for.
// @param aStart The index to start from.
// @return The index of the found element or NoIndex if not found.
template<class Item>
index_type IndexOf(const Item& aItem, index_type aStart = 0) const
{
return mArray.IndexOf(aItem, aStart);
}
//
// Mutation methods
//
// Insert a given element at the given index.
// @param aIndex The index at which to insert item.
// @param aItem The item to insert,
// @return A pointer to the newly inserted element, or a null on DOM
template<class Item>
elem_type* InsertElementAt(index_type aIndex, const Item& aItem)
{
elem_type* item = mArray.InsertElementAt(aIndex, aItem);
AdjustIterators(aIndex, 1);
return item;
}
// Same as above but without copy constructing.
// This is useful to avoid temporaries.
elem_type* InsertElementAt(index_type aIndex)
{
elem_type* item = mArray.InsertElementAt(aIndex);
AdjustIterators(aIndex, 1);
return item;
}
// Prepend an element to the array unless it already exists in the array.
// 'operator==' must be defined for elem_type.
// @param aItem The item to prepend.
// @return true if the element was found, or inserted successfully.
template<class Item>
bool PrependElementUnlessExists(const Item& aItem)
{
if (Contains(aItem)) {
return true;
}
bool inserted = mArray.InsertElementAt(0, aItem) != nullptr;
AdjustIterators(0, 1);
return inserted;
}
// Append an element to the array.
// @param aItem The item to append.
// @return A pointer to the newly appended element, or null on OOM.
template<class Item>
elem_type* AppendElement(const Item& aItem)
{
return mArray.AppendElement(aItem);
}
// Same as above, but without copy-constructing. This is useful to avoid
// temporaries.
elem_type* AppendElement()
{
return mArray.AppendElement();
}
// Append an element to the array unless it already exists in the array.
// 'operator==' must be defined for elem_type.
// @param aItem The item to append.
// @return true if the element was found, or inserted successfully.
template<class Item>
bool AppendElementUnlessExists(const Item& aItem)
{
return Contains(aItem) || AppendElement(aItem) != nullptr;
}
// Remove an element from the array.
// @param aIndex The index of the item to remove.
void RemoveElementAt(index_type aIndex)
{
NS_ASSERTION(aIndex < mArray.Length(), "invalid index");
mArray.RemoveElementAt(aIndex);
AdjustIterators(aIndex, -1);
}
// This helper function combines IndexOf with RemoveElementAt to "search
// and destroy" the first element that is equal to the given element.
// 'operator==' must be defined for elem_type.
// @param aItem The item to search for.
// @return true if the element was found and removed.
template<class Item>
bool RemoveElement(const Item& aItem)
{
index_type index = mArray.IndexOf(aItem, 0);
if (index == array_type::NoIndex) {
return false;
}
mArray.RemoveElementAt(index);
AdjustIterators(index, -1);
return true;
}
// Removes all observers and collapses all iterators to the beginning of
// the array. The result is that forward iterators will see all elements
// in the array.
void Clear()
{
mArray.Clear();
ClearIterators();
}
// Compact the array to minimize the memory it uses
void Compact() { mArray.Compact(); }
// Returns the number of bytes on the heap taken up by this object, not
// including sizeof(*this).
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
{
return mArray.SizeOfExcludingThis(aMallocSizeOf);
}
//
// Iterators
//
// Base class for iterators. Do not use this directly.
class Iterator : public Iterator_base
{
protected:
friend class nsAutoTObserverArray;
typedef nsAutoTObserverArray<T, N> array_type;
Iterator(index_type aPosition, const array_type& aArray)
: Iterator_base(aPosition, aArray.mIterators)
, mArray(const_cast<array_type&>(aArray))
{
aArray.mIterators = this;
}
~Iterator()
{
NS_ASSERTION(mArray.mIterators == this,
"Iterators must currently be destroyed in opposite order "
"from the construction order. It is suggested that you "
"simply put them on the stack");
mArray.mIterators = mNext;
}
// The array we're iterating
array_type& mArray;
};
// Iterates the array forward from beginning to end. mPosition points
// to the element that will be returned on next call to GetNext.
// Elements:
// - prepended to the array during iteration *will not* be traversed
// - appended during iteration *will* be traversed
// - removed during iteration *will not* be traversed.
// @see EndLimitedIterator
class ForwardIterator : protected Iterator
{
public:
typedef nsAutoTObserverArray<T, N> array_type;
typedef Iterator base_type;
ForwardIterator(const array_type& aArray)
: Iterator(0, aArray)
{
}
ForwardIterator(const array_type& aArray, index_type aPos)
: Iterator(aPos, aArray)
{
}
bool operator<(const ForwardIterator& aOther) const
{
NS_ASSERTION(&this->mArray == &aOther.mArray,
"not iterating the same array");
return base_type::mPosition < aOther.mPosition;
}
// Returns true if there are more elements to iterate.
// This must precede a call to GetNext(). If false is
// returned, GetNext() must not be called.
bool HasMore() const
{
return base_type::mPosition < base_type::mArray.Length();
}
// Returns the next element and steps one step. This must
// be preceded by a call to HasMore().
// @return The next observer.
elem_type& GetNext()
{
NS_ASSERTION(HasMore(), "iterating beyond end of array");
return base_type::mArray.ElementAt(base_type::mPosition++);
}
};
// EndLimitedIterator works like ForwardIterator, but will not iterate new
// observers appended to the array after the iterator was created.
class EndLimitedIterator : protected ForwardIterator
{
public:
typedef nsAutoTObserverArray<T, N> array_type;
typedef Iterator base_type;
EndLimitedIterator(const array_type& aArray)
: ForwardIterator(aArray)
, mEnd(aArray, aArray.Length())
{
}
// Returns true if there are more elements to iterate.
// This must precede a call to GetNext(). If false is
// returned, GetNext() must not be called.
bool HasMore() const { return *this < mEnd; }
// Returns the next element and steps one step. This must
// be preceded by a call to HasMore().
// @return The next observer.
elem_type& GetNext()
{
NS_ASSERTION(HasMore(), "iterating beyond end of array");
return base_type::mArray.ElementAt(base_type::mPosition++);
}
private:
ForwardIterator mEnd;
};
// Iterates the array backward from end to start. mPosition points
// to the element that was returned last.
// Elements:
// - prepended to the array during iteration *will* be traversed,
// unless the iteration already arrived at the first element
// - appended during iteration *will not* be traversed
// - removed during iteration *will not* be traversed.
class BackwardIterator : protected Iterator
{
public:
typedef nsAutoTObserverArray<T, N> array_type;
typedef Iterator base_type;
BackwardIterator(const array_type& aArray)
: Iterator(aArray.Length(), aArray)
{
}
// Returns true if there are more elements to iterate.
// This must precede a call to GetNext(). If false is
// returned, GetNext() must not be called.
bool HasMore() const { return base_type::mPosition > 0; }
// Returns the next element and steps one step. This must
// be preceded by a call to HasMore().
// @return The next observer.
elem_type& GetNext()
{
NS_ASSERTION(HasMore(), "iterating beyond start of array");
return base_type::mArray.ElementAt(--base_type::mPosition);
}
// Removes the element at the current iterator position.
// (the last element returned from |GetNext()|)
// This will not affect the next call to |GetNext()|
void Remove()
{
return base_type::mArray.RemoveElementAt(base_type::mPosition);
}
};
protected:
nsAutoTArray<T, N> mArray;
};
template<class T>
class nsTObserverArray : public nsAutoTObserverArray<T, 0>
{
public:
typedef nsAutoTObserverArray<T, 0> base_type;
typedef nsTObserverArray_base::size_type size_type;
//
// Initialization methods
//
nsTObserverArray() {}
// Initialize this array and pre-allocate some number of elements.
explicit nsTObserverArray(size_type aCapacity)
{
base_type::mArray.SetCapacity(aCapacity);
}
};
template<typename T, size_t N>
inline void
ImplCycleCollectionUnlink(nsAutoTObserverArray<T, N>& aField)
{
aField.Clear();
}
template<typename T, size_t N>
inline void
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
nsAutoTObserverArray<T, N>& aField,
const char* aName,
uint32_t aFlags = 0)
{
aFlags |= CycleCollectionEdgeNameArrayFlag;
size_t length = aField.Length();
for (size_t i = 0; i < length; ++i) {
ImplCycleCollectionTraverse(aCallback, aField.ElementAt(i), aName, aFlags);
}
}
// XXXbz I wish I didn't have to pass in the observer type, but I
// don't see a way to get it out of array_.
// Note that this macro only works if the array holds pointers to XPCOM objects.
#define NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(array_, obstype_, func_, params_) \
PR_BEGIN_MACRO \
nsTObserverArray<obstype_ *>::ForwardIterator iter_(array_); \
nsRefPtr<obstype_> obs_; \
while (iter_.HasMore()) { \
obs_ = iter_.GetNext(); \
obs_ -> func_ params_ ; \
} \
PR_END_MACRO
// Note that this macro only works if the array holds pointers to XPCOM objects.
#define NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(array_, obstype_, func_, params_) \
PR_BEGIN_MACRO \
nsTObserverArray<obstype_ *>::ForwardIterator iter_(array_); \
obstype_* obs_; \
while (iter_.HasMore()) { \
obs_ = iter_.GetNext(); \
obs_ -> func_ params_ ; \
} \
PR_END_MACRO
#endif // nsTObserverArray_h___