/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * 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/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
// <TotalCount, <number of entries, sum of used count>> typedef std::unordered_map<sal_uInt16, std::pair<sal_uInt32, sal_uInt32>> ItemArrayUsage; static ItemArrayUsage aItemArrayUsage;
SVL_DLLPUBLIC void listSfxItemSetUsage()
{ struct sorted {
sal_uInt16 nTotalCount;
sal_uInt32 nAppearances;
sal_uInt32 nAllUsedCount;
sorted(sal_uInt16 _nTotalCount, sal_uInt32 _nAppearances, sal_uInt32 _nAllUsedCount)
: nTotalCount(_nTotalCount), nAppearances(_nAppearances), nAllUsedCount(_nAllUsedCount) {} booloperator<(const sorted& rDesc) const { return nTotalCount > rDesc.nTotalCount; }
};
std::vector<sorted> aSorted;
aSorted.reserve(aItemArrayUsage.size()); for (constauto& rEntry : aItemArrayUsage)
aSorted.emplace_back(rEntry.first, rEntry.second.first, rEntry.second.second);
std::sort(aSorted.begin(), aSorted.end());
SAL_INFO("svl.items", "ITEM: List of " << aItemArrayUsage.size() << " SfxItemPool TotalCounts with usages:"); double fAllFillRatePercent(0.0);
sal_uInt32 nUsed(0);
sal_uInt32 nAllocated(0); for (constauto& rEntry : aSorted)
{ const sal_uInt32 nAllCount(rEntry.nAppearances * rEntry.nTotalCount); constdouble fFillRatePercent(0 == nAllCount ? 0.0 : (static_cast<double>(rEntry.nAllUsedCount) / static_cast<double>(nAllCount)) * 100.0);
SAL_INFO("svl.items", " TotalCount: " << rEntry.nTotalCount
<< " Appearances: " << rEntry.nAppearances
<< " FillRate(%): " << fFillRatePercent);
fAllFillRatePercent += fFillRatePercent;
nUsed += rEntry.nAllUsedCount;
nAllocated += rEntry.nTotalCount * rEntry.nAppearances;
}
SAL_INFO("svl.items", " Average FillRate(%): " << fAllFillRatePercent / aItemArrayUsage.size());
SAL_INFO("svl.items", " Used: " << nUsed << " Allocated: " << nAllocated);
SAL_INFO("svl.items", " Average Used/Allocated(%): " << (static_cast<double>(nUsed) / static_cast<double>(nAllocated)) * 100.0);
} #endif // NOTE: Only needed for one Item in SC (see notes below for // ScPatternAttr). Still keep it so that when errors // come up to this change be able to quickly check using the // fallback flag 'ITEM_CLASSIC_MODE'
// I thought about this constructor a while, but when there is no // Item we need no cleanup at destruction (what we would need the // Pool for), so it is OK and makes default construction easier // when no Pool is needed. The other constructors guarantee that // there *cannot* be a state with Item set and Pool not set. IF // you change this class, ALWAYS ensure that this can not happen (!)
SfxPoolItemHolder::SfxPoolItemHolder()
: m_pPool(nullptr)
, m_pItem(nullptr) #ifndef NDEBUG
, m_bDeleted(false) #endif
{ #ifdef DBG_UTIL
nAllocatedSfxPoolItemHolderCount++;
nUsedSfxPoolItemHolderCount++; #endif
}
/** * Ctor for a SfxItemSet with exactly the Which Ranges, which are known to * the supplied SfxItemPool. * * For Sfx programmers: an SfxItemSet constructed in this way cannot * contain any Items with SlotIds as Which values.
*/
SfxItemSet::SfxItemSet(SfxItemPool& rPool)
: m_pPool(&rPool)
, m_pParent(nullptr)
, m_nRegister(0) #ifdef DBG_UTIL
, m_nRegisteredSfxItemIter(0) #endif
, m_aWhichRanges(rPool.GetMergedIdRanges())
, m_aPoolItemMap()
{ #ifdef DBG_UTIL
nAllocatedSfxItemSetCount++;
nUsedSfxItemSetCount++; #endif
assert(m_aWhichRanges.validRanges2());
}
SfxItemSet::~SfxItemSet()
{ #ifdef DBG_UTIL
nAllocatedSfxItemSetCount--;
addArrayUsage(Count(), TotalCount()); #endif // cleanup items. No std::fill needed, we are done with this ItemSet. // the callback is not set in destructor, so no worries about that
ClearAllItemsImpl();
// for invariant-testing
m_aWhichRanges.reset();
}
// Delete single Items or all Items (nWhich == 0)
sal_uInt16 SfxItemSet::ClearItem( sal_uInt16 nWhich )
{ if( !Count() ) return 0;
// remember count before resetting it, that is the retval const sal_uInt16 nRetval(Count());
m_aPoolItemMap.clear();
if (0 != m_nRegister)
{
GetPool()->unregisterItemSet(*this);
m_nRegister = 0;
}
return nRetval;
}
void SfxItemSet::ClearInvalidItems()
{ if (0 == Count()) // no items set, done return;
// loop, here using const_iterator due to need to set ptr in m_ppItems array for (PoolItemMap::iterator aCandidate(m_aPoolItemMap.begin()); aCandidate != m_aPoolItemMap.end();)
{ if (IsInvalidItem(aCandidate->second))
{ #ifdef DBG_UTIL
assert(0 == m_nRegisteredSfxItemIter && "ITEM: SfxItemSet ClearInvalidItems with active SfxItemIters (!)"); #endif
aCandidate = m_aPoolItemMap.erase(aCandidate);
} else
aCandidate++;
}
}
SfxItemState SfxItemSet::GetItemState_ForIter(PoolItemMap::const_iterator aHit, const SfxPoolItem **ppItem)
{ if (IsInvalidItem(aHit->second)) // Different ones are present return SfxItemState::INVALID;
if (IsDisabledItem(aHit->second)) // Item is Disabled return SfxItemState::DISABLED;
// if we have the Item, add it to output an hand back if (nullptr != ppItem)
*ppItem = aHit->second;
// we need to reset ppItem when it was *not* set by GetItemState_ForWhichID // since many usages of that return parameter re-use it, so it might still // be set to 'something' if (!bRet && nullptr != ppItem)
{
*ppItem = nullptr;
}
const SfxPoolItem* SfxItemSet::PutImplAsTargetWhich(const SfxPoolItem& rItem, sal_uInt16 nTargetWhich, bool bPassingOwnership)
{ if (0 == nTargetWhich || nTargetWhich == rItem.Which()) // nTargetWhich not different or not given, use default return PutImpl(rItem, bPassingOwnership);
if (bPassingOwnership && 0 == rItem.GetRefCount())
{ // we *can* use rItem when it's not pooled AKA has no RefCount const_cast<SfxPoolItem&>(rItem).SetWhich(nTargetWhich); return PutImpl(rItem, true);
}
// else we have to create a clone, set WhichID at it and // delete rItem when bPassingOwnership was intended
SfxPoolItem* pClone(rItem.Clone(GetPool()));
pClone->SetWhich(nTargetWhich); if (bPassingOwnership) delete &rItem; return PutImpl(*pClone, true);
}
const SfxPoolItem* SfxItemSet::PutImpl(const SfxPoolItem& rItem, bool bPassingOwnership)
{ if (IsDisabledItem(&rItem))
{ // no action needed: IsDisabledItem if (bPassingOwnership) delete &rItem; return nullptr;
}
const sal_uInt16 nWhich(rItem.Which());
if (!GetRanges().doesContainWhich(nWhich))
{ // no action needed: not in WhichRange if (bPassingOwnership) delete &rItem; return nullptr;
}
if (SfxPoolItem::areSame(*pEntry, rItem))
{ // no action needed: identical item already in place if (bPassingOwnership) delete &rItem; return nullptr;
}
}
// prepare new entry const SfxPoolItem* pNew(implCreateItemEntry(*GetPool(), &rItem, bPassingOwnership));
// Notification-Callback
Changed(pEntry, pNew);
// check register for add/remove. add first so that unregister/register // is avoided when an Item is replaced (increase, decrease, do not reach 0)
checkAddPoolRegistration(pNew);
checkRemovePoolRegistration(pEntry);
// cleanup old entry & set entry at m_ppItems array
implCleanupItemEntry(pEntry);
if (pEntry)
aHit->second = pNew; else
{ #ifdef DBG_UTIL
assert(0 == m_nRegisteredSfxItemIter && "ITEM: SfxItemSet PutImpl with active SfxItemIters (!)"); #endif
m_aPoolItemMap[nWhich] = pNew;
}
return pNew;
}
bool SfxItemSet::Put(const SfxItemSet& rSource, bool bInvalidAsDefault)
{ if (0 == rSource.Count()) // no items in source, done returnfalse;
/** * This method takes the Items from the 'rSet' and adds to '*this'. * Which ranges in '*this' that are non-existent in 'rSet' will not * be altered. The Which range of '*this' is also not changed. * * Items set in 'rSet' are also set in '*this'. * Default (0 pointer) and Invalid (-1 pointer) Items are processed * according to their parameter 'eDontCareAs' and 'eDefaultAs': * * SfxItemState::SET: Hard set to the default of the Pool * SfxItemState::DEFAULT: Deleted (0 pointer) * SfxItemState::INVALID: Invalid (-1 pointer) * * NB: All other values for 'eDontCareAs' and 'eDefaultAs' are invalid
*/ void SfxItemSet::PutExtended
( const SfxItemSet& rSource, // Source of the Items to be put
SfxItemState eDontCareAs, // What will happen to the DontCare Items
SfxItemState eDefaultAs // What will happen to the Default Items
)
{ // don't "optimize" with "if( rSource.Count()" because of dontcare + defaults for (const WhichPair& rPair : rSource.GetRanges())
{ for (sal_uInt16 nWhich = rPair.first; nWhich <= rPair.second; nWhich++)
{
PoolItemMap::const_iterator aHit(rSource.m_aPoolItemMap.find(nWhich));
if (aHit != rSource.m_aPoolItemMap.end())
{ if (IsInvalidItem(aHit->second))
{ // Item is DontCare: switch (eDontCareAs)
{ case SfxItemState::SET:
PutImpl(rSource.GetPool()->GetUserOrPoolDefaultItem(nWhich), false); break;
case SfxItemState::DEFAULT:
ClearSingleItem_ForWhichID(nWhich); break;
case SfxItemState::INVALID:
DisableOrInvalidateItem_ForWhichID(false, nWhich); break;
default:
assert(!"invalid Argument for eDontCareAs");
}
} else
{ // Item is set:
PutImpl(*aHit->second, false);
}
} else
{ // Item is default: switch (eDefaultAs)
{ case SfxItemState::SET:
PutImpl(rSource.GetPool()->GetUserOrPoolDefaultItem(nWhich), false); break;
case SfxItemState::DEFAULT:
ClearSingleItem_ForWhichID(nWhich); break;
case SfxItemState::INVALID:
DisableOrInvalidateItem_ForWhichID(false, nWhich); break;
default:
assert(!"invalid Argument for eDefaultAs");
}
}
}
}
}
/** * Expands the ranges of settable items by 'nFrom' to 'nTo'. Keeps state of * items which are new ranges too.
*/ void SfxItemSet::MergeRange( sal_uInt16 nFrom, sal_uInt16 nTo )
{ // check if all from new range are already included. This will // use the cache in WhichRangesContainer since we check linearly. // Start with assuming all are included, but only if not empty. // If empty all included is wrong (and GetRanges().MergeRange // will do the right thing/shortcut) bool bAllIncluded(!GetRanges().empty());
for (sal_uInt16 a(nFrom); bAllIncluded && a <= nTo; a++) if (!GetRanges().doesContainWhich(a))
bAllIncluded = false;
// if yes, we are done if (bAllIncluded) return;
// need to create new WhichRanges auto aNewRanges = m_aWhichRanges.MergeRange(nFrom, nTo);
RecreateRanges_Impl(aNewRanges);
m_aWhichRanges = std::move(aNewRanges);
}
/** * Modifies the ranges of settable items. Keeps state of items which * are new ranges too.
*/ void SfxItemSet::SetRanges( const WhichRangesContainer& aNewRanges )
{ // Identical Ranges? if (GetRanges() == aNewRanges) return;
void SfxItemSet::RecreateRanges_Impl(const WhichRangesContainer& rNewRanges)
{ if (0 == Count()) // no existing items, done return;
// check if existing items are in the new ItemRanges. // if they are not, remove the item for (PoolItemMap::iterator aCandidate(m_aPoolItemMap.begin()); aCandidate != m_aPoolItemMap.end();)
{ if (!rNewRanges.doesContainWhich(aCandidate->first))
{ #ifdef DBG_UTIL
assert(0 == m_nRegisteredSfxItemIter && "ITEM: SfxItemSet RecreateRanges with active SfxItemIters (!)"); #endif
ClearSingleItem_PrepareRemove(aCandidate->second);
aCandidate = m_aPoolItemMap.erase(aCandidate);
} else
aCandidate++;
}
}
/** * The SfxItemSet takes over exactly those SfxPoolItems that are * set in rSet and are in their own Which range. All others are removed. * The SfxItemPool is retained, such that SfxPoolItems that have been * taken over, are moved from the rSet's SfxItemPool to the SfxItemPool * of *this. * * SfxPoolItems in rSet, for which holds 'IsInvalidItem() == true' are * taken over as invalid items. * * @return bool true * SfxPoolItems have been taken over * * false * No SfxPoolItems have been taken over, because * e.g. the Which ranges of SfxItemSets are not intersecting * or the intersection does not contain SfxPoolItems that are * set in rSet
*/ bool SfxItemSet::Set
( const SfxItemSet& rSet, /* The SfxItemSet, whose SfxPoolItems are
to been taken over */
bool bDeep /* true (default)
The SfxPoolItems from the parents that may be present in rSet, are also taken over into this SfxPoolItemSet
false The SfxPoolItems from the parents of
rSet are not taken into account */
)
{ if (Count())
ClearItem();
const SfxPoolItem* SfxItemSet::GetItem(sal_uInt16 nId, bool bSearchInParent) const
{ // evtl. Convert from SlotID to WhichId const sal_uInt16 nWhich(GetPool()->GetWhichIDFromSlotID(nId));
// Is the Item set or 'bDeep == true' available? const SfxPoolItem *pItem(nullptr); const SfxItemState eState(GetItemState_ForWhichID(SfxItemState::UNKNOWN, nWhich, bSearchInParent, &pItem));
void SfxItemSet::Intersect( const SfxItemSet& rSet )
{ // Delete all Items *not* contained in rSet
assert(m_pPool && "Not implemented without Pool");
if (!Count() || this == &rSet) // none set -> none to delete // same ItemSet? -> no Items not contained return;
if (!rSet.Count())
{ // no Items contained in rSet -> Delete everything
ClearAllItemsImpl(); return;
}
// locally delete all items *not* contained in rSet, independent of their // values, just dependent of existence. Iterate over all existing local items for (PoolItemMap::iterator aCandidate(m_aPoolItemMap.begin()); aCandidate != m_aPoolItemMap.end();)
{ // check if an item with that WhichID exists in rSet const PoolItemMap::const_iterator aHit(rSet.m_aPoolItemMap.find(aCandidate->first));
if (aHit == rSet.m_aPoolItemMap.end())
{ // no item with that WhichID exists in rset, so we have to delete // aCandidate. // tdf#164712: NOTE: This includes all set items (SfxItemState::SET) // but *also* SfxItemState::DISABLED and SfxItemState::INVALID. #ifdef DBG_UTIL
assert(0 == m_nRegisteredSfxItemIter && "ITEM: SfxItemSet Intersect with active SfxItemIters (!)"); #endif
ClearSingleItem_PrepareRemove(aCandidate->second);
aCandidate = m_aPoolItemMap.erase(aCandidate);
} else
aCandidate++;
}
}
// Delete all Items contained in rSet if (!Count() || !rSet.Count()) // None set? return;
if (this == &rSet)
{ // same ItemSet, all Items are contained -> Delete everything
ClearAllItemsImpl(); return;
}
// locally delete all items contained in rSet, independent of their // values, just dependent of their existence in rSet. // tdf#164712: NOTE: This includes all set items (SfxItemState::SET) // but also SfxItemState::DISABLED and SfxItemState::INVALID. // These are all items that exist in the std::unordered_map (PoolItemMap) // of rSet, so we can just iterate over those and use the WhichID to // delete the eventually Items in the local set for (PoolItemMap::const_iterator aCandidate(rSet.m_aPoolItemMap.begin()); aCandidate != rSet.m_aPoolItemMap.end(); aCandidate++)
{
ClearSingleItem_ForWhichID(aCandidate->first);
}
}
/** * Decision table for MergeValue(s) * * Principles: * 1. If the Which value in the 1st set is "unknown", there's never any action * 2. If the Which value in the 2nd set is "unknown", it's made the "default" * 3. For comparisons the values of the "default" Items are take into account * * 1st Item 2nd Item Values bIgnoreDefs Remove Assign Add * * set set == sal_False - - - * default set == sal_False - - - * dontcare set == sal_False - - - * unknown set == sal_False - - - * set default == sal_False - - - * default default == sal_False - - - * dontcare default == sal_False - - - * unknown default == sal_False - - - * set dontcare == sal_False 1st Item -1 - * default dontcare == sal_False - -1 - * dontcare dontcare == sal_False - - - * unknown dontcare == sal_False - - - * set unknown == sal_False 1st Item -1 - * default unknown == sal_False - - - * dontcare unknown == sal_False - - - * unknown unknown == sal_False - - - * * set set != sal_False 1st Item -1 - * default set != sal_False - -1 - * dontcare set != sal_False - - - * unknown set != sal_False - - - * set default != sal_False 1st Item -1 - * default default != sal_False - - - * dontcare default != sal_False - - - * unknown default != sal_False - - - * set dontcare != sal_False 1st Item -1 - * default dontcare != sal_False - -1 - * dontcare dontcare != sal_False - - - * unknown dontcare != sal_False - - - * set unknown != sal_False 1st Item -1 - * default unknown != sal_False - - - * dontcare unknown != sal_False - - - * unknown unknown != sal_False - - - * * set set == sal_True - - - * default set == sal_True - 2nd Item 2nd Item * dontcare set == sal_True - - - * unknown set == sal_True - - - * set default == sal_True - - - * default default == sal_True - - - * dontcare default == sal_True - - - * unknown default == sal_True - - - * set dontcare == sal_True - - - * default dontcare == sal_True - -1 - * dontcare dontcare == sal_True - - - * unknown dontcare == sal_True - - - * set unknown == sal_True - - - * default unknown == sal_True - - - * dontcare unknown == sal_True - - - * unknown unknown == sal_True - - - * * set set != sal_True 1st Item -1 - * default set != sal_True - 2nd Item 2nd Item * dontcare set != sal_True - - - * unknown set != sal_True - - - * set default != sal_True - - - * default default != sal_True - - - * dontcare default != sal_True - - - * unknown default != sal_True - - - * set dontcare != sal_True 1st Item -1 - * default dontcare != sal_True - -1 - * dontcare dontcare != sal_True - - - * unknown dontcare != sal_True - - - * set unknown != sal_True - - - * default unknown != sal_True - - - * dontcare unknown != sal_True - - - * unknown unknown != sal_True - - -
*/
void SfxItemSet::MergeItem_Impl(sal_uInt16 nWhich, const SfxPoolItem *pFnd2, bool bIgnoreDefaults)
{ // callers need to ensure that nWhich is in local range
assert(GetRanges().doesContainWhich(nWhich) && "ITEM: call to MergeItem_Impl with WhichID outside local range (!)"); const PoolItemMap::iterator aHit(m_aPoolItemMap.find(nWhich));
if (aHit == m_aPoolItemMap.end())
{ // 1st Item nWhich is not set (Default) const SfxPoolItem* pNew(nullptr);
if (pNew)
{ #ifdef DBG_UTIL
assert(0 == m_nRegisteredSfxItemIter && "ITEM: SfxItemSet MergeItem with active SfxItemIters (!)"); #endif
m_aPoolItemMap[nWhich] = pNew;
checkAddPoolRegistration(pNew);
}
return;
}
const SfxPoolItem* pFnd1(aHit->second);
if (IsInvalidItem(pFnd1))
{ return;
}
// 1st Item is set, check for change bool bDoChange(false);
if (nullptr == pFnd2)
{ // 2nd Item is not set (Default) if (!bIgnoreDefaults && *pFnd1 != GetPool()->GetUserOrPoolDefaultItem(nWhich))
{ // Decision table: set, default, !=, sal_False
bDoChange = true;
}
} elseif (IsInvalidItem(pFnd2))
{ // 2nd Item is invalid (dontcare) if (!bIgnoreDefaults || *pFnd1 != GetPool()->GetUserOrPoolDefaultItem(nWhich))
{ // Decision table: set, dontcare, doesn't matter, sal_False // or: set, dontcare, !=, sal_True
bDoChange = true;
}
} elseif (*pFnd1 != *pFnd2)
{ // 2nd Item is set // Decision table: set, set, !=, doesn't matter
bDoChange = true;
}
if (bDoChange)
{
ClearSingleItem_PrepareRemove(pFnd1);
aHit->second = INVALID_POOL_ITEM;
}
}
void SfxItemSet::MergeValues( const SfxItemSet& rSet )
{ // WARNING! When making changes/fixing bugs, always update the table above!!
assert( GetPool() == rSet.GetPool() && "MergeValues with different Pools" );
// CAUTION: Old version did *different* things when the WhichRanges // were the same (true) or different (false) (which is an error/ // false optimization): // true: MergeItem_Impl was directly fed with SfxItem*'s // for entry @this & @rSet // false: Looped over rSet WhichID's, fetched defaults from pool, // fed all that to SfxItemSet::MergeValue which then // evtl. could not find that WhichID in local WhichRanges // Better to loop over local WhichRanges (these get changed) and look // for Item with same WhichID in rSet, this is done now. for (autoconst & rRange : GetRanges())
{ for (sal_uInt16 nWhich(rRange.first); nWhich <= rRange.second; nWhich++)
{
PoolItemMap::const_iterator aHit(rSet.m_aPoolItemMap.find(nWhich)); const SfxPoolItem* src(aHit == rSet.m_aPoolItemMap.end() ? nullptr : aHit->second);
MergeItem_Impl(nWhich, src, false/*bIgnoreDefaults*/);
}
}
}
void SfxItemSet::MergeValue(const SfxPoolItem& rAttr)
{ if (IsDisabledItem(&rAttr)) // DisabledItem, nothing to do return;
if (GetRanges().doesContainWhich(rAttr.Which()))
{
MergeItem_Impl(rAttr.Which(), &rAttr, /*bIgnoreDefaults*/true);
}
}
SfxItemSet SfxItemSet::CloneAsValue(bool bItems, SfxItemPool *pToPool ) const
{ // if you are trying to clone, then the thing you are cloning is polymorphic, which means // it cannot be cloned as a value
assert((typeid(*this) == typeid(SfxItemSet)) && "cannot call this on a subclass of SfxItemSet");
/** * Explicitly define this ctor to avoid auto-generation by the compiler. * The compiler does not take the ctor with the 'const SfxItemSet&'!
*/
SfxAllItemSet::SfxAllItemSet(const SfxAllItemSet &rCopy)
: SfxItemSet(rCopy)
{
}
/** * Putting with automatic extension of the WhichId with the ID of the Item.
*/ const SfxPoolItem* SfxAllItemSet::PutImpl( const SfxPoolItem& rItem, bool bPassingOwnership )
{
MergeRange(rItem.Which(), rItem.Which()); return SfxItemSet::PutImpl(rItem, bPassingOwnership);
}
std::unique_ptr<SfxItemSet> SfxAllItemSet::Clone(bool bItems, SfxItemPool *pToPool ) const
{ if (pToPool && pToPool != GetPool())
{
std::unique_ptr<SfxAllItemSet> pNewSet(new SfxAllItemSet( *pToPool )); if ( bItems )
pNewSet->Set( *this ); return pNewSet;
} else return std::unique_ptr<SfxItemSet>(bItems ? new SfxAllItemSet(*this) : new SfxAllItemSet(*GetPool()));
}
bool WhichRangesContainer::doesContainWhich(sal_uInt16 nWhich) const
{ // special case for single entry - happens often e.g. UI stuff if (m_size == 1)
{ if( m_pairs->first <= nWhich && nWhich <= m_pairs->second ) returntrue;
// we have only one WhichPair entry and it's not contained -> failed returnfalse;
}
if (m_size == 0) returnfalse;
// check if nWhich is inside last successfully used WhichPair if (INVALID_WHICHPAIR_OFFSET != m_aLastWhichPairOffset
&& m_aLastWhichPairFirst <= nWhich
&& nWhich <= m_aLastWhichPairSecond)
{ #ifdef DBG_UTIL
isHit(); #endif // we can re-use the last found WhichPair returntrue;
}
#ifdef DBG_UTIL
isMiss(); #endif
// we have to find the correct WhichPair, iterate linear. This // also directly updates the buffered m_aLastWhichPair* values
m_aLastWhichPairOffset = 0;
for (const WhichPair& rPair : *this)
{ // Within this range? if( rPair.first <= nWhich && nWhich <= rPair.second )
{ // found, remember parameters for buffered hits
m_aLastWhichPairFirst = rPair.first;
m_aLastWhichPairSecond = rPair.second;
// *need* to reset: if 1st WhichPair only one entry it could be 1 // what could wrongly trigger re-use above for next search
m_aLastWhichPairOffset = INVALID_WHICHPAIR_OFFSET;
returnfalse;
}
// Adds a range to which ranges, keeping the ranges in valid state (sorted, non-overlapping)
WhichRangesContainer WhichRangesContainer::MergeRange(sal_uInt16 nFrom,
sal_uInt16 nTo) const
{
assert(svl::detail::validRange(nFrom, nTo));
if (empty()) return WhichRangesContainer(nFrom, nTo);
// create vector of ranges (sal_uInt16 pairs of lower and upper bound) const size_t nOldCount = size(); // Allocate one item more than we already have. // In the worst case scenario we waste a little bit // of memory, but we avoid another allocation, which is more important.
std::unique_ptr<WhichPair[]> aRangesTable(new WhichPair[nOldCount+1]); int aRangesTableSize = 0; bool bAdded = false; for (constauto& rPair : *this)
{ if (!bAdded && rPair.first >= nFrom)
{ // insert new range, keep ranges sorted
aRangesTable[aRangesTableSize++] = { nFrom, nTo };
bAdded = true;
} // insert current range
aRangesTable[aRangesTableSize++] = rPair;
} if (!bAdded)
aRangesTable[aRangesTableSize++] = { nFrom, nTo };
// true if ranges overlap or adjoin, false if ranges are separate auto needMerge = [](WhichPair lhs, WhichPair rhs) { return (lhs.first - 1) <= rhs.second && (rhs.first - 1) <= lhs.second;
};
auto it = aRangesTable.get(); auto endIt = aRangesTable.get() + aRangesTableSize; // we have at least one range at this point for (;;)
{ auto itNext = std::next(it); if (itNext == endIt) break; // check if neighbouring ranges overlap or adjoin if (needMerge(*it, *itNext))
{ // lower bounds are sorted, implies: it->first = min(it[0].first, it[1].first)
it->second = std::max(it->second, itNext->second); // remove next element
std::move(std::next(itNext), endIt, itNext);
--aRangesTableSize;
endIt = aRangesTable.get() + aRangesTableSize;
} else
++it;
}
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.