staticbool isDeletedPtr(SvtListener* p)
{ /** mark deleted entries by toggling the last bit,which is effectively unused, since the struct we point *toisatleast16-bitaligned.Thisallowsthebinarysearchtocontinueworkingevenwhenwehave
* deleted entries */ return (reinterpret_cast<uintptr_t>(p) & 0x01) == 0x01;
}
staticvoid sortListeners(std::vector<SvtListener*>& listeners, size_t firstUnsorted)
{ // Add() only appends new values, so often the container will be sorted expect for one // or few last items. For larger containers it is much more efficient to just handle // the unsorted part. auto sortedEnd = firstUnsorted == 0
? std::is_sorted_until(listeners.begin(),listeners.end())
: listeners.begin() + firstUnsorted; if( listeners.end() - sortedEnd == 1 )
{ // Just one item, insert it in the right place.
SvtListener* item = listeners.back();
listeners.pop_back();
listeners.insert( std::upper_bound( listeners.begin(), listeners.end(), item ), item );
} elseif( o3tl::make_unsigned( sortedEnd - listeners.begin()) > listeners.size() * 3 / 4 )
{ // Sort the unsorted part and then merge.
std::sort( sortedEnd, listeners.end());
std::inplace_merge( listeners.begin(), sortedEnd, listeners.end());
} else
{
std::sort(listeners.begin(), listeners.end());
}
}
void SvtBroadcaster::Normalize() const
{ // clear empty slots first, because then we often have to do very little sorting if (mnEmptySlots)
{
std::erase_if(maListeners, [] (SvtListener* p) { return isDeletedPtr(p); });
mnEmptySlots = 0;
}
if (!mbDestNormalized)
{
sortListeners(maDestructedListeners, 0);
mbDestNormalized = true;
}
}
void SvtBroadcaster::Add( SvtListener* p )
{
assert(!mbDisposing && "called inside my own destructor?");
assert(!mbAboutToDie && "called after PrepareForDestruction()?"); if (mbDisposing || mbAboutToDie) return; // Avoid normalizing if the item appended keeps the container sorted. auto nRealSize = static_cast<sal_Int32>(maListeners.size() - mnEmptySlots); auto bSorted = mnListenersFirstUnsorted == nRealSize; if (maListeners.empty() || (bSorted && maListeners.back() <= p))
{
++mnListenersFirstUnsorted;
maListeners.push_back(p); return;
} // see if we can stuff it into an empty slot, which succeeds surprisingly often in // some calc workloads where it removes and then re-adds the same listener if (mnEmptySlots && bSorted)
{ auto it = std::lower_bound(maListeners.begin(), maListeners.end(), p); if (it != maListeners.end() && isDeletedPtr(*it))
{
*it = p;
++mnListenersFirstUnsorted;
--mnEmptySlots; return;
}
}
maListeners.push_back(p);
}
void SvtBroadcaster::Remove( SvtListener* p )
{ if (mbDisposing) return;
if (mbAboutToDie)
{ // only reset mbDestNormalized if we are going to become unsorted if (!maDestructedListeners.empty() && maDestructedListeners.back() > p)
mbDestNormalized = false;
maDestructedListeners.push_back(p); return;
}
// We only need to fully normalize if one or more Add()s have been performed that make the array unsorted. auto nRealSize = static_cast<sal_Int32>(maListeners.size() - mnEmptySlots); if (mnListenersFirstUnsorted != nRealSize || (maListeners.size() > 1024 && maListeners.size() / nRealSize > 16))
Normalize();
auto it = std::lower_bound(maListeners.begin(), maListeners.end(), p);
assert (it != maListeners.end() && *it == p); if (it != maListeners.end() && *it == p)
{
markDeletedPtr(*it);
++mnEmptySlots;
--mnListenersFirstUnsorted;
}
if (!HasListeners())
ListenersGone();
}
SvtBroadcaster::SvtBroadcaster( const SvtBroadcaster &rBC ) :
mnEmptySlots(0), mnListenersFirstUnsorted(0),
mbAboutToDie(false), mbDisposing(false),
mbDestNormalized(true)
{
assert(!rBC.mbAboutToDie && "copying an object marked with PrepareForDestruction()?");
assert(!rBC.mbDisposing && "copying an object that is in its destructor?");
rBC.Normalize(); // so that insert into ourself is in-order, and therefore we do not need to Normalize()
maListeners.reserve(rBC.maListeners.size()); for (SvtListener* p : rBC.maListeners)
p->StartListening(*this); // this will call back into this->Add()
}
// now when both lists are sorted, we can linearly unregister all // listeners, with the exception of those that already asked to be removed // during their own destruction
ListenersType::const_iterator dest(maDestructedListeners.begin()); for (auto& rpListener : maListeners)
{ // skip the destructed ones while (dest != maDestructedListeners.end() && (*dest < rpListener))
++dest;
ListenersType::const_iterator dest(maDestructedListeners.begin());
ListenersType aListeners(maListeners); // this copy is important to avoid erasing entries while iterating for (auto& rpListener : aListeners)
{ // skip the destructed ones while (dest != maDestructedListeners.end() && (*dest < rpListener))
++dest;
void SvtBroadcaster::PrepareForDestruction()
{
mbAboutToDie = true; // the reserve() serves two purpose (1) performance (2) makes sure our iterators do not become invalid
maDestructedListeners.reserve(maListeners.size());
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.1Bemerkung:
(vorverarbeitet am 2026-09-29)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.