// <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
}
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() ) return0;
// 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;
/** *ThismethodtakestheItemsfromthe'rSet'andaddsto'*this'. *Whichrangesin'*this'thatarenon-existentin'rSet'willnot *bealtered.TheWhichrangeof'*this'isalsonotchanged. * *Itemssetin'rSet'arealsosetin'*this'. *Default(0pointer)andInvalid(-1pointer)Itemsareprocessed *accordingtotheirparameter'eDontCareAs'and'eDefaultAs': * *SfxItemState::SET:HardsettothedefaultofthePool *SfxItemState::DEFAULT:Deleted(0pointer) *SfxItemState::INVALID:Invalid(-1pointer) * *NB:Allothervaluesfor'eDontCareAs'and'eDefaultAs'areinvalid
*/ 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");
}
}
}
}
}
/** *Expandstherangesofsettableitemsby'nFrom'to'nTo'.Keepsstateof *itemswhicharenewrangestoo.
*/ 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);
}
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++;
}
}
/** *TheSfxItemSettakesoverexactlythoseSfxPoolItemsthatare *setinrSetandareintheirownWhichrange.Allothersareremoved. *TheSfxItemPoolisretained,suchthatSfxPoolItemsthathavebeen *takenover,aremovedfromtherSet'sSfxItemPooltotheSfxItemPool *of*this. * *SfxPoolItemsinrSet,forwhichholds'IsInvalidItem()==true'are *takenoverasinvaliditems. * *@returnbooltrue *SfxPoolItemshavebeentakenover * *false *NoSfxPoolItemshavebeentakenover,because *e.g.theWhichrangesofSfxItemSetsarenotintersecting *ortheintersectiondoesnotcontainSfxPoolItemsthatare *setinrSet
*/ bool SfxItemSet::Set
( const SfxItemSet& rSet, /* The SfxItemSet, whose SfxPoolItems are
to been taken over */
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);
}
}
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");
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.0.42Bemerkung:
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-27)
¤
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.