fdo#70465: speed up AccessibleEventNotifier::generateId()
Iterating over all entries of a std::map is rather slow, so add a interval map to manage the free entries. A first attempt to use boost::icl::interval_set for this was abandoned; while the releaseId() function would be just 1 line, GCC 4.8.2 at least is unhappy about boost icl headers for non-obvious reasons: UnpackedTarball/boost/boost/icl/discrete_interval.hpp:45:225: error: default argument for template parameter for class enclosing ‘void boost::icl::boost_concept_check_dummy45(boost_concept_check45*)’ Change-Id: I7b767aefee57df7743dc13a694b6a61abdd536c7
This commit is contained in:
@@ -24,6 +24,7 @@
|
|||||||
#include <comphelper/guarding.hxx>
|
#include <comphelper/guarding.hxx>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
using namespace ::com::sun::star::uno;
|
using namespace ::com::sun::star::uno;
|
||||||
using namespace ::com::sun::star::lang;
|
using namespace ::com::sun::star::lang;
|
||||||
@@ -43,46 +44,71 @@ namespace
|
|||||||
::cppu::OInterfaceContainerHelper*,
|
::cppu::OInterfaceContainerHelper*,
|
||||||
::std::less< AccessibleEventNotifier::TClientId > > ClientMap;
|
::std::less< AccessibleEventNotifier::TClientId > > ClientMap;
|
||||||
|
|
||||||
|
/// key is the end of the interval, value is the start of the interval
|
||||||
|
typedef ::std::map<AccessibleEventNotifier::TClientId,
|
||||||
|
AccessibleEventNotifier::TClientId> IntervalMap;
|
||||||
|
|
||||||
struct lclMutex
|
struct lclMutex
|
||||||
: public rtl::Static< ::osl::Mutex, lclMutex > {};
|
: public rtl::Static< ::osl::Mutex, lclMutex > {};
|
||||||
struct Clients
|
struct Clients
|
||||||
: public rtl::Static< ClientMap, Clients > {};
|
: public rtl::Static< ClientMap, Clients > {};
|
||||||
|
struct FreeIntervals
|
||||||
|
: public rtl::StaticWithInit<IntervalMap, FreeIntervals> {
|
||||||
|
IntervalMap operator() () {
|
||||||
|
IntervalMap map;
|
||||||
|
map.insert(::std::make_pair(
|
||||||
|
::std::numeric_limits<AccessibleEventNotifier::TClientId>::max(), 1));
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static void releaseId(AccessibleEventNotifier::TClientId const nId)
|
||||||
|
{
|
||||||
|
IntervalMap & rFreeIntervals(FreeIntervals::get());
|
||||||
|
IntervalMap::iterator const upper(rFreeIntervals.upper_bound(nId));
|
||||||
|
assert(upper != rFreeIntervals.end());
|
||||||
|
assert(nId < upper->second); // second is start of the interval!
|
||||||
|
if (nId + 1 == upper->second)
|
||||||
|
{
|
||||||
|
--upper->second; // add nId to existing interval
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IntervalMap::iterator const lower(rFreeIntervals.lower_bound(nId));
|
||||||
|
if (lower != rFreeIntervals.end() && lower->first == nId - 1)
|
||||||
|
{
|
||||||
|
// add nId by replacing lower with new merged entry
|
||||||
|
rFreeIntervals.insert(::std::make_pair(nId, lower->second));
|
||||||
|
rFreeIntervals.erase(lower);
|
||||||
|
}
|
||||||
|
else // otherwise just add new 1-element interval
|
||||||
|
{
|
||||||
|
rFreeIntervals.insert(::std::make_pair(nId, nId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// currently it's not checked whether intervals can be merged now
|
||||||
|
// hopefully that won't be a problem in practice
|
||||||
|
}
|
||||||
|
|
||||||
/// generates a new client id
|
/// generates a new client id
|
||||||
static AccessibleEventNotifier::TClientId generateId()
|
static AccessibleEventNotifier::TClientId generateId()
|
||||||
{
|
{
|
||||||
AccessibleEventNotifier::TClientId nBiggestUsedId = 0;
|
IntervalMap & rFreeIntervals(FreeIntervals::get());
|
||||||
AccessibleEventNotifier::TClientId nFreeId = 0;
|
assert(!rFreeIntervals.empty());
|
||||||
|
IntervalMap::iterator const iter(rFreeIntervals.begin());
|
||||||
// look through all registered clients until we find a "gap" in the ids
|
AccessibleEventNotifier::TClientId const nFirst = iter->first;
|
||||||
|
AccessibleEventNotifier::TClientId const nFreeId = iter->second;
|
||||||
// Note that the following relies on the fact the elements in the map
|
assert(nFreeId <= nFirst);
|
||||||
// are traveled with ascending keys (aka client ids)
|
if (nFreeId != nFirst)
|
||||||
ClientMap &rClients = Clients::get();
|
|
||||||
for ( ClientMap::const_iterator aLookup = rClients.begin();
|
|
||||||
aLookup != rClients.end();
|
|
||||||
++aLookup
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
AccessibleEventNotifier::TClientId nCurrent = aLookup->first;
|
++iter->second; // remove nFreeId from interval
|
||||||
OSL_ENSURE( nCurrent > nBiggestUsedId,
|
}
|
||||||
"AccessibleEventNotifier::generateId: "
|
else
|
||||||
"map is expected to be sorted ascending!" );
|
{
|
||||||
|
rFreeIntervals.erase(iter); // remove 1-element interval
|
||||||
if ( nCurrent - nBiggestUsedId > 1 )
|
|
||||||
{ // found a "gap"
|
|
||||||
nFreeId = nBiggestUsedId + 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
nBiggestUsedId = nCurrent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !nFreeId )
|
assert(Clients::get().end() == Clients::get().find(nFreeId));
|
||||||
nFreeId = nBiggestUsedId + 1;
|
|
||||||
|
|
||||||
OSL_ENSURE( rClients.end() == rClients.find( nFreeId ),
|
|
||||||
"AccessibleEventNotifier::generateId: algorithm broken!" );
|
|
||||||
|
|
||||||
return nFreeId;
|
return nFreeId;
|
||||||
}
|
}
|
||||||
@@ -158,6 +184,7 @@ namespace comphelper
|
|||||||
// remove it from the clients map
|
// remove it from the clients map
|
||||||
delete aClientPos->second;
|
delete aClientPos->second;
|
||||||
Clients::get().erase( aClientPos );
|
Clients::get().erase( aClientPos );
|
||||||
|
releaseId(_nClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------
|
//---------------------------------------------------------------------
|
||||||
@@ -183,6 +210,7 @@ namespace comphelper
|
|||||||
// implementations have re-entrance problems and call into
|
// implementations have re-entrance problems and call into
|
||||||
// revokeClient while we are notifying from here)
|
// revokeClient while we are notifying from here)
|
||||||
Clients::get().erase(aClientPos);
|
Clients::get().erase(aClientPos);
|
||||||
|
releaseId(_nClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
// notify the "disposing" event for this client
|
// notify the "disposing" event for this client
|
||||||
|
Reference in New Issue
Block a user