Coverage Report

Created: 2026-10-14 09:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svl/source/items/itemset.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
21
#include <string.h>
22
23
#include <algorithm>
24
#include <cassert>
25
#include <cstddef>
26
#include <unordered_map>
27
28
#include <libxml/xmlwriter.h>
29
#include <tools/XmlWriter.hxx>
30
#include <o3tl/hash_combine.hxx>
31
32
#include <sal/log.hxx>
33
#include <svl/itemset.hxx>
34
#include <svl/itempool.hxx>
35
#include <svl/itemiter.hxx>
36
#include <svl/setitem.hxx>
37
#include <svl/whiter.hxx>
38
#include <svl/voiditem.hxx>
39
40
#ifdef DBG_UTIL
41
static size_t nAllocatedSfxItemSetCount(0);
42
static size_t nUsedSfxItemSetCount(0);
43
static size_t nAllocatedSfxPoolItemHolderCount(0);
44
static size_t nUsedSfxPoolItemHolderCount(0);
45
size_t getAllocatedSfxItemSetCount() { return nAllocatedSfxItemSetCount; }
46
size_t getUsedSfxItemSetCount() { return nUsedSfxItemSetCount; }
47
size_t getAllocatedSfxPoolItemHolderCount() { return nAllocatedSfxPoolItemHolderCount; }
48
size_t getUsedSfxPoolItemHolderCount() { return nUsedSfxPoolItemHolderCount; }
49
50
// <TotalCount, <number of entries, sum of used count>>
51
typedef std::unordered_map<sal_uInt16, std::pair<sal_uInt32, sal_uInt32>> ItemArrayUsage;
52
static ItemArrayUsage aItemArrayUsage;
53
54
static void addArrayUsage(sal_uInt16 nCount, sal_uInt16 nTotalCount)
55
{
56
    ItemArrayUsage::iterator aHit(aItemArrayUsage.find(nTotalCount));
57
    if (aHit == aItemArrayUsage.end())
58
    {
59
        aItemArrayUsage.insert({nTotalCount, {1, nCount}});
60
        return;
61
    }
62
    aHit->second.first++;
63
    aHit->second.second += nCount;
64
}
65
66
SVL_DLLPUBLIC void listSfxItemSetUsage()
67
{
68
    struct sorted {
69
        sal_uInt16 nTotalCount;
70
        sal_uInt32 nAppearances;
71
        sal_uInt32 nAllUsedCount;
72
        sorted(sal_uInt16 _nTotalCount, sal_uInt32 _nAppearances, sal_uInt32 _nAllUsedCount)
73
            : nTotalCount(_nTotalCount), nAppearances(_nAppearances), nAllUsedCount(_nAllUsedCount) {}
74
        bool operator<(const sorted& rDesc) const { return nTotalCount > rDesc.nTotalCount; }
75
    };
76
    std::vector<sorted> aSorted;
77
    aSorted.reserve(aItemArrayUsage.size());
78
    for (const auto& rEntry : aItemArrayUsage)
79
        aSorted.emplace_back(rEntry.first, rEntry.second.first, rEntry.second.second);
80
    std::sort(aSorted.begin(), aSorted.end());
81
    SAL_INFO("svl.items", "ITEM: List of " << aItemArrayUsage.size() << " SfxItemPool TotalCounts with usages:");
82
    double fAllFillRatePercent(0.0);
83
    sal_uInt32 nUsed(0);
84
    sal_uInt32 nAllocated(0);
85
    for (const auto& rEntry : aSorted)
86
    {
87
        const sal_uInt32 nAllCount(rEntry.nAppearances * rEntry.nTotalCount);
88
        const double fFillRatePercent(0 == nAllCount ? 0.0 : (static_cast<double>(rEntry.nAllUsedCount) / static_cast<double>(nAllCount)) * 100.0);
89
        SAL_INFO("svl.items",
90
            " TotalCount: " << rEntry.nTotalCount
91
            << " Appearances: " << rEntry.nAppearances
92
            << " FillRate(%): " << fFillRatePercent);
93
        fAllFillRatePercent += fFillRatePercent;
94
        nUsed += rEntry.nAllUsedCount;
95
        nAllocated += rEntry.nTotalCount * rEntry.nAppearances;
96
    }
97
    SAL_INFO("svl.items", " Average FillRate(%): " << fAllFillRatePercent / aItemArrayUsage.size());
98
    SAL_INFO("svl.items", " Used: " << nUsed << " Allocated: " << nAllocated);
99
    SAL_INFO("svl.items", " Average Used/Allocated(%): " << (static_cast<double>(nUsed) / static_cast<double>(nAllocated)) * 100.0);
100
}
101
#endif
102
// NOTE: Only needed for one Item in SC (see notes below for
103
// ScPatternAttr). Still keep it so that when errors
104
// come up to this change be able to quickly check using the
105
// fallback flag 'ITEM_CLASSIC_MODE'
106
107
// I thought about this constructor a while, but when there is no
108
// Item we need no cleanup at destruction (what we would need the
109
// Pool for), so it is OK and makes default construction easier
110
// when no Pool is needed. The other constructors guarantee that
111
// there *cannot* be a state with Item set and Pool not set. IF
112
// you change this class, ALWAYS ensure that this can not happen (!)
113
SfxPoolItemHolder::SfxPoolItemHolder()
114
7.11M
: m_pPool(nullptr)
115
7.11M
, m_pItem(nullptr)
116
#ifndef NDEBUG
117
, m_bDeleted(false)
118
#endif
119
7.11M
{
120
#ifdef DBG_UTIL
121
    nAllocatedSfxPoolItemHolderCount++;
122
    nUsedSfxPoolItemHolderCount++;
123
#endif
124
7.11M
}
125
126
SfxPoolItemHolder::SfxPoolItemHolder(SfxItemPool& rPool, const SfxPoolItem* pItem, bool bPassingOwnership)
127
31.7M
: m_pPool(&rPool)
128
31.7M
, m_pItem(pItem)
129
#ifndef NDEBUG
130
, m_bDeleted(false)
131
#endif
132
31.7M
{
133
#ifdef DBG_UTIL
134
    nAllocatedSfxPoolItemHolderCount++;
135
    nUsedSfxPoolItemHolderCount++;
136
#endif
137
31.7M
    if (nullptr != m_pItem)
138
31.7M
        m_pItem = implCreateItemEntry(getPool(), m_pItem, bPassingOwnership);
139
31.7M
    if (nullptr != m_pItem && getPool().NeedsSurrogateSupport(m_pItem->Which()))
140
5.28M
        getPool().registerPoolItemHolder(*this);
141
31.7M
}
142
143
SfxPoolItemHolder::SfxPoolItemHolder(const SfxPoolItemHolder& rHolder)
144
14.9M
: m_pPool(rHolder.m_pPool)
145
14.9M
, m_pItem(rHolder.m_pItem)
146
#ifndef NDEBUG
147
, m_bDeleted(false)
148
#endif
149
14.9M
{
150
#ifdef DBG_UTIL
151
    assert(!rHolder.isDeleted() && "Destructed instance used (!)");
152
    nAllocatedSfxPoolItemHolderCount++;
153
    nUsedSfxPoolItemHolderCount++;
154
#endif
155
14.9M
    if (nullptr != m_pItem)
156
14.9M
        m_pItem = implCreateItemEntry(getPool(), m_pItem, false);
157
14.9M
    if (nullptr != m_pItem && getPool().NeedsSurrogateSupport(m_pItem->Which()))
158
2.30M
        getPool().registerPoolItemHolder(*this);
159
14.9M
}
160
161
SfxPoolItemHolder::~SfxPoolItemHolder()
162
53.8M
{
163
#ifdef DBG_UTIL
164
    assert(!isDeleted() && "Destructed instance used (!)");
165
    nAllocatedSfxPoolItemHolderCount--;
166
#endif
167
53.8M
    if (nullptr != m_pItem && getPool().NeedsSurrogateSupport(m_pItem->Which()))
168
7.59M
        getPool().unregisterPoolItemHolder(*this);
169
53.8M
    if (nullptr != m_pItem)
170
46.7M
        implCleanupItemEntry(m_pItem);
171
#ifndef NDEBUG
172
    m_bDeleted = true;
173
#endif
174
53.8M
}
175
176
const SfxPoolItemHolder& SfxPoolItemHolder::operator=(const SfxPoolItemHolder& rHolder)
177
8.87k
{
178
8.87k
    assert(!isDeleted() && "Destructed instance used (!)");
179
8.87k
    assert(!rHolder.isDeleted() && "Destructed instance used (!)");
180
8.87k
    if (this == &rHolder || *this == rHolder)
181
0
        return *this;
182
183
8.87k
    if (nullptr != m_pItem && getPool().NeedsSurrogateSupport(m_pItem->Which()))
184
0
        getPool().unregisterPoolItemHolder(*this);
185
186
8.87k
    if (nullptr != m_pItem)
187
0
        implCleanupItemEntry(m_pItem);
188
189
8.87k
    m_pPool = rHolder.m_pPool;
190
8.87k
    m_pItem = rHolder.m_pItem;
191
192
8.87k
    if (nullptr != m_pItem)
193
8.87k
        m_pItem = implCreateItemEntry(getPool(), m_pItem, false);
194
195
8.87k
    if (nullptr != m_pItem && getPool().NeedsSurrogateSupport(m_pItem->Which()))
196
0
        getPool().registerPoolItemHolder(*this);
197
198
8.87k
    return *this;
199
8.87k
}
200
201
bool SfxPoolItemHolder::operator==(const SfxPoolItemHolder &rHolder) const
202
8.87k
{
203
8.87k
    assert(!isDeleted() && "Destructed instance used (!)");
204
8.87k
    assert(!rHolder.isDeleted() && "Destructed instance used (!)");
205
8.87k
    return m_pPool == rHolder.m_pPool && areSfxPoolItemPtrsEqual(m_pItem, rHolder.m_pItem);
206
8.87k
}
207
208
/**
209
 * Ctor for a SfxItemSet with exactly the Which Ranges, which are known to
210
 * the supplied SfxItemPool.
211
 *
212
 * For Sfx programmers: an SfxItemSet constructed in this way cannot
213
 * contain any Items with SlotIds as Which values.
214
 */
215
SfxItemSet::SfxItemSet(SfxItemPool& rPool)
216
3.46M
: m_pPool(&rPool)
217
3.46M
, m_pParent(nullptr)
218
3.46M
, m_nRegister(0)
219
#ifdef DBG_UTIL
220
, m_nRegisteredSfxItemIter(0)
221
, m_bDeleted(false)
222
#endif
223
3.46M
, m_aWhichRanges(rPool.GetMergedIdRanges())
224
3.46M
, m_aPoolItemMap()
225
3.46M
{
226
#ifdef DBG_UTIL
227
    nAllocatedSfxItemSetCount++;
228
    nUsedSfxItemSetCount++;
229
#endif
230
3.46M
    assert(m_aWhichRanges.validRanges2());
231
3.46M
}
232
233
SfxItemSet::SfxItemSet(SfxItemPool& pool, WhichRangesContainer wids)
234
395M
: m_pPool(&pool)
235
395M
, m_pParent(nullptr)
236
395M
, m_nRegister(0)
237
#ifdef DBG_UTIL
238
, m_nRegisteredSfxItemIter(0)
239
, m_bDeleted(false)
240
#endif
241
395M
, m_aWhichRanges(std::move(wids))
242
395M
, m_aPoolItemMap()
243
395M
{
244
#ifdef DBG_UTIL
245
    nAllocatedSfxItemSetCount++;
246
    nUsedSfxItemSetCount++;
247
#endif
248
395M
    assert(m_aWhichRanges.TotalCount() != 0);
249
395M
    assert(m_aWhichRanges.validRanges2());
250
395M
}
251
252
SfxItemSet::SfxItemSet( const SfxItemSet& rASet )
253
150M
: m_pPool( rASet.m_pPool )
254
150M
, m_pParent( rASet.m_pParent )
255
150M
, m_nRegister( 0 )
256
#ifdef DBG_UTIL
257
, m_nRegisteredSfxItemIter(0)
258
, m_bDeleted(false)
259
#endif
260
150M
, m_aWhichRanges( rASet.m_aWhichRanges )
261
150M
, m_aPoolItemMap()
262
150M
{
263
#ifdef DBG_UTIL
264
    nAllocatedSfxItemSetCount++;
265
    nUsedSfxItemSetCount++;
266
#endif
267
150M
    if (rASet.GetRanges().empty())
268
0
        return;
269
270
150M
    for (const auto& rSource : rASet.m_aPoolItemMap)
271
359M
    {
272
359M
        const SfxPoolItem* pNew(implCreateItemEntry(*GetPool(), rSource.second, false));
273
359M
        m_aPoolItemMap[rSource.first] = pNew;
274
359M
        if (m_nRegister != rASet.m_nRegister)
275
56.7M
            checkAddPoolRegistration(pNew);
276
359M
    }
277
278
150M
    assert(m_aWhichRanges.validRanges2());
279
150M
}
280
281
SfxItemSet::SfxItemSet(SfxItemSet&& rASet) noexcept
282
4.54M
: m_pPool( rASet.m_pPool )
283
4.54M
, m_pParent( rASet.m_pParent )
284
4.54M
, m_nRegister( rASet.m_nRegister )
285
#ifdef DBG_UTIL
286
, m_nRegisteredSfxItemIter(0)
287
, m_bDeleted(false)
288
#endif
289
4.54M
, m_aWhichRanges( std::move(rASet.m_aWhichRanges) )
290
4.54M
, m_aPoolItemMap( std::move(rASet.m_aPoolItemMap) )
291
4.54M
{
292
#ifdef DBG_UTIL
293
    nAllocatedSfxItemSetCount++;
294
    nUsedSfxItemSetCount++;
295
    assert(0 == rASet.m_nRegisteredSfxItemIter && "ITEM: SfxItemSet MOVE constructor with active SfxItemIters (!)");
296
#endif
297
    // deregister if rASet is registered before ptrs vanish
298
4.54M
    if (0 != rASet.m_nRegister)
299
508k
        rASet.GetPool()->unregisterItemSet(rASet);
300
301
    // register if new set needs that
302
4.54M
    if (0 != m_nRegister)
303
508k
        GetPool()->registerItemSet(*this);
304
305
    // taking over ownership
306
4.54M
    rASet.m_pPool = nullptr;
307
4.54M
    rASet.m_pParent = nullptr;
308
4.54M
    rASet.m_nRegister = 0;
309
4.54M
    rASet.m_aWhichRanges.reset();
310
311
4.54M
    assert(m_aWhichRanges.validRanges2());
312
4.54M
}
313
314
SfxItemSet::~SfxItemSet()
315
554M
{
316
#ifdef DBG_UTIL
317
    nAllocatedSfxItemSetCount--;
318
    addArrayUsage(Count(), TotalCount());
319
#endif
320
    // cleanup items. No std::fill needed, we are done with this ItemSet.
321
    // the callback is not set in destructor, so no worries about that
322
554M
    ClearAllItemsImpl();
323
324
    // for invariant-testing
325
554M
    m_aWhichRanges.reset();
326
#ifdef DBG_UTIL
327
    m_bDeleted = true;
328
#endif
329
554M
}
330
331
332
// Delete single Items or all Items (nWhich == 0)
333
sal_uInt16 SfxItemSet::ClearItem( sal_uInt16 nWhich )
334
165M
{
335
165M
    if( !Count() )
336
47.3M
        return 0;
337
338
117M
    if( nWhich )
339
111M
        return ClearSingleItem_ForWhichID(nWhich);
340
341
    // clear all & reset to nullptr
342
6.28M
    return ClearAllItemsImpl();
343
117M
}
344
345
sal_uInt16 SfxItemSet::ClearSingleItem_ForWhichID( sal_uInt16 nWhich )
346
148M
{
347
148M
    PoolItemMap::iterator aHit(m_aPoolItemMap.find(nWhich));
348
349
148M
    if (aHit == m_aPoolItemMap.end())
350
133M
        return 0;
351
352
#ifdef DBG_UTIL
353
    assert(0 == m_nRegisteredSfxItemIter && "ITEM: SfxItemSet ClearItem with active SfxItemIters (!)");
354
#endif
355
356
15.2M
    ClearSingleItem_PrepareRemove(aHit->second);
357
15.2M
    m_aPoolItemMap.erase(aHit);
358
359
15.2M
    return 1;
360
148M
}
361
362
void SfxItemSet::ClearSingleItem_PrepareRemove(const SfxPoolItem* pItem)
363
888M
{
364
888M
    if (nullptr == pItem)
365
0
        return;
366
367
    // Notification-Callback
368
888M
    Changed(pItem, nullptr);
369
370
    // check register for remove
371
888M
    checkRemovePoolRegistration(pItem);
372
373
    // cleanup item & reset ptr
374
888M
    implCleanupItemEntry(pItem);
375
888M
}
376
377
void SfxItemSet::checkRemovePoolRegistration(const SfxPoolItem* pItem)
378
1.43G
{
379
1.43G
    if (nullptr == pItem)
380
        // no Item, done
381
528M
        return;
382
383
902M
    if (IsInvalidItem(pItem) || IsDisabledItem(pItem))
384
        // checks IsInvalidItem/IsDisabledItem
385
174k
        return;
386
387
902M
    if (SfxItemPool::IsSlot(pItem->Which()))
388
        // no slots, these do not support NeedsSurrogateSupport
389
16.1M
        return;
390
391
886M
    if(!GetPool()->NeedsSurrogateSupport(pItem->Which()))
392
        // not needed for this item, done
393
833M
        return;
394
395
    // there must be a registered one
396
886M
    assert(0 != m_nRegister);
397
398
    // decrement counter
399
52.6M
    m_nRegister--;
400
401
    // deregister when no more Items that NeedsSurrogateSupport exist
402
52.6M
    if (0 == m_nRegister)
403
24.7M
        GetPool()->unregisterItemSet(*this);
404
405
52.6M
    if (pItem->isNameOrIndex())
406
4.05M
        GetPool()->unregisterNameOrIndex(*pItem);
407
52.6M
}
408
409
void SfxItemSet::checkAddPoolRegistration(const SfxPoolItem* pItem)
410
599M
{
411
599M
    if (nullptr == pItem)
412
        // no Item, done
413
0
        return;
414
415
599M
    if (IsInvalidItem(pItem) || IsDisabledItem(pItem))
416
        // checks IsInvalidItem/IsDisabledItem
417
5.64k
        return;
418
419
599M
    if (SfxItemPool::IsSlot(pItem->Which()))
420
        // no slots, these do not support NeedsSurrogateSupport
421
6.83M
        return;
422
423
592M
    if(!GetPool()->NeedsSurrogateSupport(pItem->Which()))
424
        // not needed for this item, done
425
539M
        return;
426
427
    // register when first Item that NeedsSurrogateSupport exist
428
52.6M
    if (0 == m_nRegister)
429
24.7M
        GetPool()->registerItemSet(*this);
430
431
52.6M
    if (pItem->isNameOrIndex())
432
4.05M
        GetPool()->registerNameOrIndex(*pItem);
433
434
    // increment counter
435
52.6M
    m_nRegister++;
436
52.6M
}
437
438
sal_uInt16 SfxItemSet::ClearAllItemsImpl()
439
560M
{
440
560M
    if (0 == Count())
441
        // no items set, done
442
319M
        return 0;
443
444
#ifdef DBG_UTIL
445
    assert(0 == m_nRegisteredSfxItemIter && "ITEM: SfxItemSet ClearAllItems with active SfxItemIters (!)");
446
#endif
447
448
    // loop & cleanup items
449
241M
    for (const auto& rCandidate : m_aPoolItemMap)
450
872M
        ClearSingleItem_PrepareRemove(rCandidate.second);
451
452
    // remember count before resetting it, that is the retval
453
241M
    const sal_uInt16 nRetval(Count());
454
241M
    m_aPoolItemMap.clear();
455
456
241M
    if (0 != m_nRegister)
457
0
    {
458
0
        GetPool()->unregisterItemSet(*this);
459
0
        m_nRegister = 0;
460
0
    }
461
462
241M
    return nRetval;
463
560M
}
464
465
void SfxItemSet::ClearInvalidItems()
466
654k
{
467
654k
    if (0 == Count())
468
        // no items set, done
469
1.13k
        return;
470
471
    // loop, here using const_iterator due to need to set ptr in m_ppItems array
472
8.82M
    for (PoolItemMap::iterator aCandidate(m_aPoolItemMap.begin()); aCandidate != m_aPoolItemMap.end();)
473
8.17M
    {
474
8.17M
        if (IsInvalidItem(aCandidate->second))
475
49
        {
476
#ifdef DBG_UTIL
477
            assert(0 == m_nRegisteredSfxItemIter && "ITEM: SfxItemSet ClearInvalidItems with active SfxItemIters (!)");
478
#endif
479
49
            aCandidate = m_aPoolItemMap.erase(aCandidate);
480
49
        }
481
8.17M
        else
482
8.17M
            aCandidate++;
483
8.17M
    }
484
653k
}
485
486
SfxItemState SfxItemSet::GetItemState_ForIter(PoolItemMap::const_iterator aHit, const SfxPoolItem **ppItem)
487
867M
{
488
867M
    if (IsInvalidItem(aHit->second))
489
        // Different ones are present
490
554k
        return SfxItemState::INVALID;
491
492
866M
    if (IsDisabledItem(aHit->second))
493
        // Item is Disabled
494
350
        return SfxItemState::DISABLED;
495
496
    // if we have the Item, add it to output an hand back
497
866M
    if (nullptr != ppItem)
498
574M
        *ppItem = aHit->second;
499
500
    // Item is set
501
866M
    return SfxItemState::SET;
502
866M
}
503
504
SfxItemState SfxItemSet::GetItemState_ForWhichID( SfxItemState eState, sal_uInt16 nWhich, bool bSrchInParent, const SfxPoolItem **ppItem) const
505
6.26G
{
506
6.26G
    if (m_pAccessTracker && !m_bAccessTrackerIgnore) {
507
0
        if (std::find(m_pAccessTracker->begin(), m_pAccessTracker->end(), nWhich)
508
0
                == m_pAccessTracker->end())
509
0
            m_pAccessTracker->push_back(nWhich);
510
0
    }
511
512
6.26G
    PoolItemMap::const_iterator aHit(m_aPoolItemMap.find(nWhich));
513
514
6.26G
    if (aHit != m_aPoolItemMap.end())
515
725M
        return GetItemState_ForIter(aHit, ppItem);
516
517
5.53G
    if (GetRanges().doesContainWhich(nWhich))
518
5.17G
    {
519
        // set to Default
520
5.17G
        eState = SfxItemState::DEFAULT;
521
5.17G
    }
522
523
    // search in parent?
524
5.53G
    if (bSrchInParent && nullptr != GetParent() && (SfxItemState::UNKNOWN == eState || SfxItemState::DEFAULT == eState))
525
1.33G
    {
526
        // Use the *highest* SfxItemState as result
527
1.33G
        return GetParent()->GetItemState_ForWhichID( eState, nWhich, true, ppItem);
528
1.33G
    }
529
530
4.20G
    return eState;
531
5.53G
}
532
533
bool SfxItemSet::HasItem(sal_uInt16 nWhich, const SfxPoolItem** ppItem) const
534
3.72M
{
535
3.72M
    if (m_pAccessTracker && !m_bAccessTrackerIgnore) {
536
0
        if (std::find(m_pAccessTracker->begin(), m_pAccessTracker->end(), nWhich)
537
0
                == m_pAccessTracker->end())
538
0
            m_pAccessTracker->push_back(nWhich);
539
0
    }
540
541
3.72M
    const bool bRet(SfxItemState::SET == GetItemState_ForWhichID(SfxItemState::UNKNOWN, nWhich, true, ppItem));
542
543
    // we need to reset ppItem when it was *not* set by GetItemState_ForWhichID
544
    // since many usages of that return parameter re-use it, so it might still
545
    // be set to 'something'
546
3.72M
    if (!bRet && nullptr != ppItem)
547
0
    {
548
0
        *ppItem = nullptr;
549
0
    }
550
551
3.72M
    return bRet;
552
3.72M
}
553
554
void SfxItemSet::CollectHasItems(std::vector<sal_uInt16>& rItemWhichs) const
555
1.59M
{
556
1.59M
    for(auto const & rPair : m_aWhichRanges)
557
4.76M
    {
558
4.76M
        const sal_uInt16 nBeg = rPair.first;
559
4.76M
        const sal_uInt16 nEnd = rPair.second;
560
87.4M
        for( sal_uInt16 nWhich = nBeg; nWhich <= nEnd; ++nWhich )
561
82.6M
        {
562
82.6M
            bool bHasItem = false;
563
82.6M
            auto aHit(m_aPoolItemMap.find(nWhich));
564
82.6M
            if (aHit != m_aPoolItemMap.end())
565
2.88M
            {
566
2.88M
                bHasItem = !IsInvalidItem(aHit->second) && !IsDisabledItem(aHit->second);
567
2.88M
            }
568
79.8M
            else
569
79.8M
            {
570
79.8M
                if (m_pParent)
571
0
                    bHasItem = SfxItemState::SET == m_pParent->GetItemState_ForWhichID( SfxItemState::DEFAULT, nWhich, true, nullptr);
572
79.8M
            }
573
82.6M
            if (bHasItem)
574
2.88M
                rItemWhichs.push_back( nWhich );
575
82.6M
        }
576
4.76M
    }
577
1.59M
}
578
579
const SfxPoolItem* SfxItemSet::PutImplAsTargetWhich(const SfxPoolItem& rItem, sal_uInt16 nTargetWhich, bool bPassingOwnership)
580
8.23M
{
581
8.23M
    if (0 == nTargetWhich || nTargetWhich == rItem.Which())
582
        // nTargetWhich not different or not given, use default
583
0
        return PutImpl(rItem, bPassingOwnership);
584
585
8.23M
    if (bPassingOwnership && 0 == rItem.GetRefCount())
586
0
    {
587
        // we *can* use rItem when it's not pooled AKA has no RefCount
588
0
        const_cast<SfxPoolItem&>(rItem).SetWhich(nTargetWhich);
589
0
        return PutImpl(rItem, true);
590
0
    }
591
592
    // else we have to create a clone, set WhichID at it and
593
    // delete rItem when bPassingOwnership was intended
594
8.23M
    SfxPoolItem* pClone(rItem.Clone(GetPool()));
595
8.23M
    pClone->SetWhich(nTargetWhich);
596
8.23M
    if (bPassingOwnership)
597
0
        delete &rItem;
598
8.23M
    return PutImpl(*pClone, true);
599
8.23M
}
600
601
const SfxPoolItem* SfxItemSet::PutImpl(const SfxPoolItem& rItem, bool bPassingOwnership)
602
927M
{
603
927M
    if (IsDisabledItem(&rItem))
604
0
    {
605
        // no action needed: IsDisabledItem
606
0
        if (bPassingOwnership)
607
0
            delete &rItem;
608
0
        return nullptr;
609
0
    }
610
611
927M
    const sal_uInt16 nWhich(rItem.Which());
612
613
927M
    if (!GetRanges().doesContainWhich(nWhich))
614
293M
    {
615
        // no action needed: not in WhichRange
616
293M
        if (bPassingOwnership)
617
11.1k
            delete &rItem;
618
293M
        return nullptr;
619
293M
    }
620
621
634M
    const SfxPoolItem* pEntry(nullptr);
622
634M
    PoolItemMap::iterator aHit(m_aPoolItemMap.find(nWhich));
623
624
634M
    if (aHit != m_aPoolItemMap.end())
625
105M
    {
626
        // compare items, evtl. containing content compare
627
105M
        pEntry = aHit->second;
628
629
105M
        if (SfxPoolItem::areSame(*pEntry, rItem))
630
91.4M
        {
631
            // no action needed: identical item already in place
632
91.4M
            if (bPassingOwnership)
633
11.6M
                delete &rItem;
634
91.4M
            return nullptr;
635
91.4M
        }
636
105M
    }
637
638
    // prepare new entry
639
542M
    const SfxPoolItem* pNew(implCreateItemEntry(*GetPool(), &rItem, bPassingOwnership));
640
641
    // Notification-Callback
642
542M
    Changed(pEntry, pNew);
643
644
    // check register for add/remove. add first so that unregister/register
645
    // is avoided when an Item is replaced (increase, decrease, do not reach 0)
646
542M
    checkAddPoolRegistration(pNew);
647
542M
    checkRemovePoolRegistration(pEntry);
648
649
    // cleanup old entry & set entry at m_ppItems array
650
542M
    implCleanupItemEntry(pEntry);
651
652
542M
    if (pEntry)
653
13.6M
        aHit->second = pNew;
654
528M
    else
655
528M
    {
656
#ifdef DBG_UTIL
657
        assert(0 == m_nRegisteredSfxItemIter && "ITEM: SfxItemSet PutImpl with active SfxItemIters (!)");
658
#endif
659
528M
        m_aPoolItemMap[nWhich] = pNew;
660
528M
    }
661
662
542M
    return pNew;
663
634M
}
664
665
bool SfxItemSet::Put(const SfxItemSet& rSource, bool bInvalidAsDefault)
666
62.6M
{
667
62.6M
    if (0 == rSource.Count())
668
        // no items in source, done
669
1.97M
        return false;
670
671
60.7M
    bool bRetval(false);
672
673
425M
    for (PoolItemMap::const_iterator aCandidate(rSource.m_aPoolItemMap.begin()); aCandidate != rSource.m_aPoolItemMap.end(); aCandidate++)
674
365M
    {
675
365M
        if (IsInvalidItem(aCandidate->second))
676
120
        {
677
120
            if (bInvalidAsDefault)
678
0
            {
679
0
                bRetval |= 0 != ClearSingleItem_ForWhichID(aCandidate->first);
680
0
            }
681
120
            else
682
120
            {
683
120
                DisableOrInvalidateItem_ForWhichID(false, aCandidate->first);
684
120
            }
685
120
        }
686
365M
        else
687
365M
        {
688
365M
            bRetval |= nullptr != PutImpl(*aCandidate->second, false);
689
365M
        }
690
365M
    }
691
692
60.7M
    return bRetval;
693
62.6M
}
694
695
/**
696
 * This method takes the Items from the 'rSet' and adds to '*this'.
697
 * Which ranges in '*this' that are non-existent in 'rSet' will not
698
 * be altered. The Which range of '*this' is also not changed.
699
 *
700
 * Items set in 'rSet' are also set in '*this'.
701
 * Default (0 pointer) and Invalid (-1 pointer) Items are processed
702
 * according to their parameter 'eDontCareAs' and 'eDefaultAs':
703
 *
704
 * SfxItemState::SET:       Hard set to the default of the Pool
705
 * SfxItemState::DEFAULT:   Deleted (0 pointer)
706
 * SfxItemState::INVALID:  Invalid (-1 pointer)
707
 *
708
 * NB: All other values for 'eDontCareAs' and 'eDefaultAs' are invalid
709
 */
710
void SfxItemSet::PutExtended
711
(
712
    const SfxItemSet&   rSource,        // Source of the Items to be put
713
    SfxItemState        eDontCareAs,    // What will happen to the DontCare Items
714
    SfxItemState        eDefaultAs      // What will happen to the Default Items
715
)
716
0
{
717
    // don't "optimize" with "if( rSource.Count()" because of dontcare + defaults
718
0
    for (const WhichPair& rPair : rSource.GetRanges())
719
0
    {
720
0
        for (sal_uInt16 nWhich = rPair.first; nWhich <= rPair.second; nWhich++)
721
0
        {
722
0
            PoolItemMap::const_iterator aHit(rSource.m_aPoolItemMap.find(nWhich));
723
724
0
            if (aHit != rSource.m_aPoolItemMap.end())
725
0
            {
726
0
                if (IsInvalidItem(aHit->second))
727
0
                {
728
                    // Item is DontCare:
729
0
                    switch (eDontCareAs)
730
0
                    {
731
0
                        case SfxItemState::SET:
732
0
                            PutImpl(rSource.GetPool()->GetUserOrPoolDefaultItem(nWhich), false);
733
0
                            break;
734
735
0
                        case SfxItemState::DEFAULT:
736
0
                            ClearSingleItem_ForWhichID(nWhich);
737
0
                            break;
738
739
0
                        case SfxItemState::INVALID:
740
0
                            DisableOrInvalidateItem_ForWhichID(false, nWhich);
741
0
                            break;
742
743
0
                        default:
744
0
                            assert(!"invalid Argument for eDontCareAs");
745
0
                    }
746
0
                }
747
0
                else
748
0
                {
749
                    // Item is set:
750
0
                    PutImpl(*aHit->second, false);
751
0
                }
752
0
            }
753
0
            else
754
0
            {
755
                // Item is default:
756
0
                switch (eDefaultAs)
757
0
                {
758
0
                    case SfxItemState::SET:
759
0
                        PutImpl(rSource.GetPool()->GetUserOrPoolDefaultItem(nWhich), false);
760
0
                        break;
761
762
0
                    case SfxItemState::DEFAULT:
763
0
                        ClearSingleItem_ForWhichID(nWhich);
764
0
                        break;
765
766
0
                    case SfxItemState::INVALID:
767
0
                        DisableOrInvalidateItem_ForWhichID(false, nWhich);
768
0
                        break;
769
770
0
                    default:
771
0
                        assert(!"invalid Argument for eDefaultAs");
772
0
                }
773
0
            }
774
0
        }
775
0
    }
776
0
}
777
778
/**
779
 * Expands the ranges of settable items by 'nFrom' to 'nTo'. Keeps state of
780
 * items which are new ranges too.
781
 */
782
void SfxItemSet::MergeRange( sal_uInt16 nFrom, sal_uInt16 nTo )
783
745k
{
784
    // check if all from new range are already included. This will
785
    // use the cache in WhichRangesContainer since we check linearly.
786
    // Start with assuming all are included, but only if not empty.
787
    // If empty all included is wrong (and GetRanges().MergeRange
788
    // will do the right thing/shortcut)
789
745k
    bool bAllIncluded(!GetRanges().empty());
790
791
1.49M
    for (sal_uInt16 a(nFrom); bAllIncluded && a <= nTo; a++)
792
745k
        if (!GetRanges().doesContainWhich(a))
793
392k
            bAllIncluded = false;
794
795
    // if yes, we are done
796
745k
    if (bAllIncluded)
797
352k
        return;
798
799
    // need to create new WhichRanges
800
392k
    auto aNewRanges = m_aWhichRanges.MergeRange(nFrom, nTo);
801
392k
    RecreateRanges_Impl(aNewRanges);
802
392k
    m_aWhichRanges = std::move(aNewRanges);
803
392k
}
804
805
/**
806
 * Modifies the ranges of settable items. Keeps state of items which
807
 * are new ranges too.
808
 */
809
void SfxItemSet::SetRanges( const WhichRangesContainer& aNewRanges )
810
11.3k
{
811
    // Identical Ranges?
812
11.3k
    if (GetRanges() == aNewRanges)
813
0
        return;
814
815
11.3k
    assert(aNewRanges.validRanges2());
816
11.3k
    RecreateRanges_Impl(aNewRanges);
817
11.3k
    m_aWhichRanges = aNewRanges;
818
11.3k
}
819
820
void SfxItemSet::SetRanges( WhichRangesContainer&& aNewRanges )
821
0
{
822
    // Identical Ranges?
823
0
    if (GetRanges() == aNewRanges)
824
0
        return;
825
826
0
    assert(aNewRanges.validRanges2());
827
0
    RecreateRanges_Impl(aNewRanges);
828
0
    m_aWhichRanges = std::move(aNewRanges);
829
0
}
830
831
void SfxItemSet::RecreateRanges_Impl(const WhichRangesContainer& rNewRanges)
832
403k
{
833
403k
    if (0 == Count())
834
        // no existing items, done
835
314k
        return;
836
837
    // check if existing items are in the new ItemRanges.
838
    // if they are not, remove the item
839
254k
    for (PoolItemMap::iterator aCandidate(m_aPoolItemMap.begin()); aCandidate != m_aPoolItemMap.end();)
840
165k
    {
841
165k
        if (!rNewRanges.doesContainWhich(aCandidate->first))
842
0
        {
843
#ifdef DBG_UTIL
844
            assert(0 == m_nRegisteredSfxItemIter && "ITEM: SfxItemSet RecreateRanges with active SfxItemIters (!)");
845
#endif
846
0
            ClearSingleItem_PrepareRemove(aCandidate->second);
847
0
            aCandidate = m_aPoolItemMap.erase(aCandidate);
848
0
        }
849
165k
        else
850
165k
            aCandidate++;
851
165k
    }
852
89.0k
}
853
854
/**
855
 * The SfxItemSet takes over exactly those SfxPoolItems that are
856
 * set in rSet and are in their own Which range. All others are removed.
857
 * The SfxItemPool is retained, such that SfxPoolItems that have been
858
 * taken over, are moved from the rSet's SfxItemPool to the SfxItemPool
859
 * of *this.
860
 *
861
 * SfxPoolItems in rSet, for which holds 'IsInvalidItem() == true' are
862
 * taken over as invalid items.
863
 *
864
 * @return bool true
865
 *              SfxPoolItems have been taken over
866
 *
867
 *              false
868
 *              No SfxPoolItems have been taken over, because
869
 *              e.g. the Which ranges of SfxItemSets are not intersecting
870
 *              or the intersection does not contain SfxPoolItems that are
871
 *              set in rSet
872
 */
873
bool SfxItemSet::Set
874
(
875
    const SfxItemSet&   rSet,   /*  The SfxItemSet, whose SfxPoolItems are
876
                                    to been taken over */
877
878
    bool                bDeep   /*  true (default)
879
880
                                    The SfxPoolItems from the parents that may
881
                                    be present in rSet, are also taken over into
882
                                    this SfxPoolItemSet
883
884
                                    false
885
                                    The SfxPoolItems from the parents of
886
                                    rSet are not taken into account */
887
)
888
85.4M
{
889
85.4M
    if (Count())
890
4.55M
        ClearItem();
891
892
85.4M
    if (!bDeep)
893
0
        return Put(rSet, false);
894
895
85.4M
    bool bRet = false;
896
85.4M
    SfxWhichIter aIter1(*this);
897
85.4M
    SfxWhichIter aIter2(rSet);
898
85.4M
    sal_uInt16 nWhich1 = aIter1.FirstWhich();
899
85.4M
    sal_uInt16 nWhich2 = aIter2.FirstWhich();
900
85.4M
    for (;;)
901
3.33G
    {
902
3.33G
        if (!nWhich1 || !nWhich2)
903
85.4M
            break;
904
3.25G
        if (nWhich1 > nWhich2)
905
1.45G
        {
906
1.45G
            nWhich2 = aIter2.NextWhich();
907
1.45G
            continue;
908
1.45G
        }
909
1.79G
        if (nWhich1 < nWhich2)
910
5.71M
        {
911
5.71M
            nWhich1 = aIter1.NextWhich();
912
5.71M
            continue;
913
5.71M
        }
914
1.78G
        const SfxPoolItem* pItem;
915
1.78G
        if( SfxItemState::SET == aIter2.GetItemState( true, &pItem ) )
916
92.2M
            bRet |= nullptr != Put( *pItem );
917
1.78G
        nWhich1 = aIter1.NextWhich();
918
1.78G
        nWhich2 = aIter2.NextWhich();
919
1.78G
    }
920
921
85.4M
    return bRet;
922
85.4M
}
923
924
const SfxPoolItem* SfxItemSet::GetItem(sal_uInt16 nId, bool bSearchInParent) const
925
282M
{
926
    // evtl. Convert from SlotID to WhichId
927
282M
    const sal_uInt16 nWhich(GetPool()->GetWhichIDFromSlotID(nId));
928
929
    // Is the Item set or 'bDeep == true' available?
930
282M
    const SfxPoolItem *pItem(nullptr);
931
282M
    const SfxItemState eState(GetItemState_ForWhichID(SfxItemState::UNKNOWN, nWhich, bSearchInParent, &pItem));
932
933
282M
    if (bSearchInParent && SfxItemState::DEFAULT == eState && SfxItemPool::IsWhich(nWhich))
934
95.3M
    {
935
95.3M
        pItem = &GetPool()->GetUserOrPoolDefaultItem(nWhich);
936
95.3M
    }
937
938
282M
    return pItem;
939
282M
}
940
941
const SfxPoolItem& SfxItemSet::Get( sal_uInt16 nWhich, bool bSrchInParent) const
942
1.61G
{
943
1.61G
    if (m_pAccessTracker && !m_bAccessTrackerIgnore) {
944
0
        if (std::find(m_pAccessTracker->begin(), m_pAccessTracker->end(), nWhich)
945
0
                == m_pAccessTracker->end())
946
0
            m_pAccessTracker->push_back(nWhich);
947
0
    }
948
949
1.61G
    PoolItemMap::const_iterator aHit(m_aPoolItemMap.find(nWhich));
950
951
1.61G
    if (aHit != m_aPoolItemMap.end())
952
443M
    {
953
443M
        if (IsInvalidItem(aHit->second))
954
1.32k
        {
955
1.32k
            return GetPool()->GetUserOrPoolDefaultItem(nWhich);
956
1.32k
        }
957
#ifdef DBG_UTIL
958
        if (IsDisabledItem(aHit->second))
959
            SAL_INFO("svl.items", "SFX_WARNING: Getting disabled Item");
960
#endif
961
443M
        return *aHit->second;
962
443M
    }
963
964
1.17G
    if (bSrchInParent && nullptr != GetParent())
965
478M
    {
966
478M
        return GetParent()->Get(nWhich, bSrchInParent);
967
478M
    }
968
969
    // Get the Default from the Pool and return
970
1.17G
    assert(m_pPool);
971
694M
    return GetPool()->GetUserOrPoolDefaultItem(nWhich);
972
1.17G
}
973
974
/**
975
 * Notification callback
976
 */
977
void SfxItemSet::Changed(const SfxPoolItem*, const SfxPoolItem*) const
978
1.33G
{
979
1.33G
}
980
981
void SfxItemSet::Intersect( const SfxItemSet& rSet )
982
938k
{
983
    // Delete all Items *not* contained in rSet
984
938k
    assert(m_pPool && "Not implemented without Pool");
985
986
938k
    if (!Count() || this == &rSet)
987
        // none set -> none to delete
988
        // same ItemSet? -> no Items not contained
989
304k
        return;
990
991
633k
    if (!rSet.Count())
992
0
    {
993
        // no Items contained in rSet -> Delete everything
994
0
        ClearAllItemsImpl();
995
0
        return;
996
0
    }
997
998
    // locally delete all items *not* contained in rSet, independent of their
999
    // values, just dependent of existence. Iterate over all existing local items
1000
6.49M
    for (PoolItemMap::iterator aCandidate(m_aPoolItemMap.begin()); aCandidate != m_aPoolItemMap.end();)
1001
5.86M
    {
1002
        // check if an item with that WhichID exists in rSet
1003
5.86M
        const PoolItemMap::const_iterator aHit(rSet.m_aPoolItemMap.find(aCandidate->first));
1004
1005
5.86M
        if (aHit == rSet.m_aPoolItemMap.end())
1006
1.16M
        {
1007
            // no item with that WhichID exists in rset, so we have to delete
1008
            // aCandidate.
1009
            // tdf#164712: NOTE: This includes all set items (SfxItemState::SET)
1010
            // but *also* SfxItemState::DISABLED and SfxItemState::INVALID.
1011
#ifdef DBG_UTIL
1012
            assert(0 == m_nRegisteredSfxItemIter && "ITEM: SfxItemSet Intersect with active SfxItemIters (!)");
1013
#endif
1014
1.16M
            ClearSingleItem_PrepareRemove(aCandidate->second);
1015
1.16M
            aCandidate = m_aPoolItemMap.erase(aCandidate);
1016
1.16M
        }
1017
4.69M
        else
1018
4.69M
            aCandidate++;
1019
5.86M
    }
1020
633k
}
1021
1022
void SfxItemSet::Differentiate(const SfxItemSet& rSet)
1023
15.4M
{
1024
15.4M
    assert(m_pPool && "Not implemented without Pool");
1025
1026
    // Delete all Items contained in rSet
1027
15.4M
    if (!Count() || !rSet.Count())
1028
        // None set?
1029
5.23M
        return;
1030
1031
10.2M
    if (this == &rSet)
1032
0
    {
1033
        // same ItemSet, all Items are contained -> Delete everything
1034
0
        ClearAllItemsImpl();
1035
0
        return;
1036
0
    }
1037
1038
    // locally delete all items contained in rSet, independent of their
1039
    // values, just dependent of their existence in rSet.
1040
    // tdf#164712: NOTE: This includes all set items (SfxItemState::SET)
1041
    // but also SfxItemState::DISABLED and SfxItemState::INVALID.
1042
    // These are all items that exist in the std::unordered_map (PoolItemMap)
1043
    // of rSet, so we can just iterate over those and use the WhichID to
1044
    // delete the eventually Items in the local set
1045
47.2M
    for (PoolItemMap::const_iterator aCandidate(rSet.m_aPoolItemMap.begin()); aCandidate != rSet.m_aPoolItemMap.end(); aCandidate++)
1046
36.9M
    {
1047
36.9M
        ClearSingleItem_ForWhichID(aCandidate->first);
1048
36.9M
    }
1049
10.2M
}
1050
1051
/**
1052
 * Decision table for MergeValue(s)
1053
 *
1054
 * Principles:
1055
 * 1. If the Which value in the 1st set is "unknown", there's never any action
1056
 * 2. If the Which value in the 2nd set is "unknown", it's made the "default"
1057
 * 3. For comparisons the values of the "default" Items are take into account
1058
 *
1059
 * 1st Item    2nd Item    Values  bIgnoreDefs       Remove      Assign       Add
1060
 *
1061
 * set         set         ==      sal_False           -           -           -
1062
 * default     set         ==      sal_False           -           -           -
1063
 * dontcare    set         ==      sal_False           -           -           -
1064
 * unknown     set         ==      sal_False           -           -           -
1065
 * set         default     ==      sal_False           -           -           -
1066
 * default     default     ==      sal_False           -           -           -
1067
 * dontcare    default     ==      sal_False           -           -           -
1068
 * unknown     default     ==      sal_False           -           -           -
1069
 * set         dontcare    ==      sal_False        1st Item       -1          -
1070
 * default     dontcare    ==      sal_False           -           -1          -
1071
 * dontcare    dontcare    ==      sal_False           -           -           -
1072
 * unknown     dontcare    ==      sal_False           -           -           -
1073
 * set         unknown     ==      sal_False        1st Item       -1          -
1074
 * default     unknown     ==      sal_False           -           -           -
1075
 * dontcare    unknown     ==      sal_False           -           -           -
1076
 * unknown     unknown     ==      sal_False           -           -           -
1077
 *
1078
 * set         set         !=      sal_False        1st Item       -1          -
1079
 * default     set         !=      sal_False           -           -1          -
1080
 * dontcare    set         !=      sal_False           -           -           -
1081
 * unknown     set         !=      sal_False           -           -           -
1082
 * set         default     !=      sal_False        1st Item       -1          -
1083
 * default     default     !=      sal_False           -           -           -
1084
 * dontcare    default     !=      sal_False           -           -           -
1085
 * unknown     default     !=      sal_False           -           -           -
1086
 * set         dontcare    !=      sal_False        1st Item       -1          -
1087
 * default     dontcare    !=      sal_False           -           -1          -
1088
 * dontcare    dontcare    !=      sal_False           -           -           -
1089
 * unknown     dontcare    !=      sal_False           -           -           -
1090
 * set         unknown     !=      sal_False        1st Item       -1          -
1091
 * default     unknown     !=      sal_False           -           -           -
1092
 * dontcare    unknown     !=      sal_False           -           -           -
1093
 * unknown     unknown     !=      sal_False           -           -           -
1094
 *
1095
 * set         set         ==      sal_True            -           -           -
1096
 * default     set         ==      sal_True            -       2nd Item     2nd Item
1097
 * dontcare    set         ==      sal_True            -           -           -
1098
 * unknown     set         ==      sal_True            -           -           -
1099
 * set         default     ==      sal_True            -           -           -
1100
 * default     default     ==      sal_True            -           -           -
1101
 * dontcare    default     ==      sal_True            -           -           -
1102
 * unknown     default     ==      sal_True            -           -           -
1103
 * set         dontcare    ==      sal_True            -           -           -
1104
 * default     dontcare    ==      sal_True            -           -1          -
1105
 * dontcare    dontcare    ==      sal_True            -           -           -
1106
 * unknown     dontcare    ==      sal_True            -           -           -
1107
 * set         unknown     ==      sal_True            -           -           -
1108
 * default     unknown     ==      sal_True            -           -           -
1109
 * dontcare    unknown     ==      sal_True            -           -           -
1110
 * unknown     unknown     ==      sal_True            -           -           -
1111
 *
1112
 * set         set         !=      sal_True         1st Item       -1          -
1113
 * default     set         !=      sal_True            -        2nd Item    2nd Item
1114
 * dontcare    set         !=      sal_True            -           -           -
1115
 * unknown     set         !=      sal_True            -           -           -
1116
 * set         default     !=      sal_True            -           -           -
1117
 * default     default     !=      sal_True            -           -           -
1118
 * dontcare    default     !=      sal_True            -           -           -
1119
 * unknown     default     !=      sal_True            -           -           -
1120
 * set         dontcare    !=      sal_True         1st Item       -1          -
1121
 * default     dontcare    !=      sal_True            -           -1          -
1122
 * dontcare    dontcare    !=      sal_True            -           -           -
1123
 * unknown     dontcare    !=      sal_True            -           -           -
1124
 * set         unknown     !=      sal_True            -           -           -
1125
 * default     unknown     !=      sal_True            -           -           -
1126
 * dontcare    unknown     !=      sal_True            -           -           -
1127
 * unknown     unknown     !=      sal_True            -           -           -
1128
 */
1129
1130
void SfxItemSet::MergeItem_Impl(sal_uInt16 nWhich, const SfxPoolItem *pFnd2, bool bIgnoreDefaults)
1131
5.43M
{
1132
    // callers need to ensure that nWhich is in local range
1133
5.43M
    assert(GetRanges().doesContainWhich(nWhich) && "ITEM: call to MergeItem_Impl with WhichID outside local range (!)");
1134
5.43M
    const PoolItemMap::iterator aHit(m_aPoolItemMap.find(nWhich));
1135
1136
5.43M
    if (aHit == m_aPoolItemMap.end())
1137
4.90M
    {
1138
        // 1st Item nWhich is not set (Default)
1139
4.90M
        const SfxPoolItem* pNew(nullptr);
1140
1141
4.90M
        if (IsInvalidItem(pFnd2))
1142
            // Decision table: default, dontcare, doesn't matter, doesn't matter
1143
1.50k
            pNew = INVALID_POOL_ITEM;
1144
1145
4.89M
        else if (pFnd2 && !bIgnoreDefaults && GetPool()->GetUserOrPoolDefaultItem(nWhich) != *pFnd2)
1146
            // Decision table: default, set, !=, sal_False
1147
3.88k
            pNew = INVALID_POOL_ITEM;
1148
1149
4.89M
        else if (pFnd2 && bIgnoreDefaults)
1150
            // Decision table: default, set, doesn't matter, sal_True
1151
0
            pNew = implCreateItemEntry(*GetPool(), pFnd2, false);
1152
1153
4.90M
        if (pNew)
1154
5.38k
        {
1155
#ifdef DBG_UTIL
1156
            assert(0 == m_nRegisteredSfxItemIter && "ITEM: SfxItemSet MergeItem with active SfxItemIters (!)");
1157
#endif
1158
5.38k
            m_aPoolItemMap[nWhich] = pNew;
1159
5.38k
            checkAddPoolRegistration(pNew);
1160
5.38k
        }
1161
1162
4.90M
        return;
1163
4.90M
    }
1164
1165
534k
    const SfxPoolItem* pFnd1(aHit->second);
1166
1167
534k
    if (IsInvalidItem(pFnd1))
1168
120k
    {
1169
120k
        return;
1170
120k
    }
1171
1172
    // 1st Item is set, check for change
1173
413k
    bool bDoChange(false);
1174
1175
413k
    if (nullptr == pFnd2)
1176
7.92k
    {
1177
        // 2nd Item is not set (Default)
1178
7.92k
        if (!bIgnoreDefaults && *pFnd1 != GetPool()->GetUserOrPoolDefaultItem(nWhich))
1179
6.74k
        {
1180
            // Decision table: set, default, !=, sal_False
1181
6.74k
            bDoChange = true;
1182
6.74k
        }
1183
7.92k
    }
1184
405k
    else if (IsInvalidItem(pFnd2))
1185
1.27k
    {
1186
        // 2nd Item is invalid (dontcare)
1187
1.27k
        if (!bIgnoreDefaults || *pFnd1 != GetPool()->GetUserOrPoolDefaultItem(nWhich))
1188
1.27k
        {
1189
            // Decision table: set, dontcare, doesn't matter, sal_False
1190
            // or:             set, dontcare, !=, sal_True
1191
1.27k
            bDoChange = true;
1192
1.27k
        }
1193
1.27k
    }
1194
404k
    else if (*pFnd1 != *pFnd2)
1195
5.09k
    {
1196
        // 2nd Item is set
1197
        // Decision table: set, set, !=, doesn't matter
1198
5.09k
        bDoChange = true;
1199
5.09k
    }
1200
1201
413k
    if (bDoChange)
1202
13.1k
    {
1203
13.1k
        ClearSingleItem_PrepareRemove(pFnd1);
1204
13.1k
        aHit->second = INVALID_POOL_ITEM;
1205
13.1k
    }
1206
413k
}
1207
1208
void SfxItemSet::MergeValues( const SfxItemSet& rSet )
1209
54.6k
{
1210
    // WARNING! When making changes/fixing bugs, always update the table above!!
1211
54.6k
    assert( GetPool() == rSet.GetPool() && "MergeValues with different Pools" );
1212
1213
    // CAUTION: Old version did *different* things when the WhichRanges
1214
    // were the same (true) or different (false) (which is an error/
1215
    // false optimization):
1216
    // true:  MergeItem_Impl was directly fed with SfxItem*'s
1217
    //        for entry @this & @rSet
1218
    // false: Looped over rSet WhichID's, fetched defaults from pool,
1219
    //        fed all that to SfxItemSet::MergeValue which then
1220
    //        evtl. could not find that WhichID in local WhichRanges
1221
    // Better to loop over local WhichRanges (these get changed) and look
1222
    // for Item with same WhichID in rSet, this is done now.
1223
54.6k
    for (auto const & rRange : GetRanges())
1224
90.7k
    {
1225
5.52M
        for (sal_uInt16 nWhich(rRange.first); nWhich <= rRange.second; nWhich++)
1226
5.43M
        {
1227
5.43M
            PoolItemMap::const_iterator aHit(rSet.m_aPoolItemMap.find(nWhich));
1228
5.43M
            const SfxPoolItem* src(aHit == rSet.m_aPoolItemMap.end() ? nullptr : aHit->second);
1229
5.43M
            MergeItem_Impl(nWhich, src, false/*bIgnoreDefaults*/);
1230
5.43M
        }
1231
90.7k
    }
1232
54.6k
}
1233
1234
void SfxItemSet::MergeValue(const SfxPoolItem& rAttr)
1235
0
{
1236
0
    if (IsDisabledItem(&rAttr))
1237
        // DisabledItem, nothing to do
1238
0
        return;
1239
1240
0
    if (GetRanges().doesContainWhich(rAttr.Which()))
1241
0
    {
1242
0
        MergeItem_Impl(rAttr.Which(), &rAttr, /*bIgnoreDefaults*/true);
1243
0
    }
1244
0
}
1245
1246
void SfxItemSet::DisableOrInvalidateItem_ForWhichID(bool bDisable, sal_uInt16 nWhich)
1247
333k
{
1248
333k
    PoolItemMap::iterator aHit(m_aPoolItemMap.find(nWhich));
1249
1250
333k
    if (aHit != m_aPoolItemMap.end())
1251
147k
    {
1252
147k
        if (bDisable && IsDisabledItem(aHit->second))
1253
            // already disabled item, done
1254
0
            return;
1255
1256
147k
        if (!bDisable && IsInvalidItem(aHit->second))
1257
            // already invalid item, done
1258
143k
            return;
1259
1260
        // cleanup entry
1261
3.98k
        ClearSingleItem_PrepareRemove(aHit->second);
1262
3.98k
        aHit->second = bDisable ? DISABLED_POOL_ITEM : INVALID_POOL_ITEM;
1263
3.98k
    }
1264
186k
    else if (GetRanges().doesContainWhich(nWhich))
1265
151k
    {
1266
#ifdef DBG_UTIL
1267
        assert(0 == m_nRegisteredSfxItemIter && "ITEM: SfxItemSet DisableOrInvalidateItem with active SfxItemIters (!)");
1268
#endif
1269
        // new entry
1270
151k
        m_aPoolItemMap[nWhich] = bDisable ? DISABLED_POOL_ITEM : INVALID_POOL_ITEM;
1271
151k
    }
1272
333k
}
1273
bool SfxItemSet::operator==(const SfxItemSet &rCmp) const
1274
145M
{
1275
145M
    return Equals( rCmp, true);
1276
145M
}
1277
1278
bool SfxItemSet::Equals(const SfxItemSet &rCmp, bool bComparePool) const
1279
145M
{
1280
    // check if same incarnation
1281
145M
    if (this == &rCmp)
1282
3.00M
        return true;
1283
1284
    // check parents (if requested, also bComparePool)
1285
142M
    if (bComparePool && GetParent() != rCmp.GetParent())
1286
308k
        return false;
1287
1288
    // check pools (if requested)
1289
142M
    if (bComparePool && GetPool() != rCmp.GetPool())
1290
7.40M
        return false;
1291
1292
    // check count of set items
1293
135M
    if (Count() != rCmp.Count())
1294
75.3M
        return false;
1295
1296
    // both have no items, done
1297
59.8M
    if (0 == Count())
1298
1.59M
        return true;
1299
1300
186M
    for (PoolItemMap::const_iterator aCandidate(m_aPoolItemMap.begin()); aCandidate != m_aPoolItemMap.end(); aCandidate++)
1301
141M
    {
1302
141M
        const SfxPoolItem *pItem1(nullptr);
1303
141M
        const SfxPoolItem *pItem2(nullptr);
1304
141M
        const sal_uInt16 nWhich(aCandidate->first);
1305
141M
        const SfxItemState aStateA(GetItemState_ForIter(aCandidate, &pItem1));
1306
141M
        const SfxItemState aStateB(rCmp.GetItemState_ForWhichID(SfxItemState::UNKNOWN, nWhich, false, &pItem2));
1307
1308
141M
        if (aStateA != aStateB)
1309
29.9k
            return false;
1310
1311
141M
        if (!SfxPoolItem::areSame(pItem1, pItem2))
1312
13.9M
            return false;
1313
141M
    }
1314
1315
44.2M
    return true;
1316
58.2M
}
1317
1318
size_t SfxItemSet::GetHashCode() const
1319
1.51M
{
1320
    // We are calculating a hash in an odd way here. This is not ideal, because
1321
    // it does not produce as good a result as using o3tl::hash_combine.
1322
    // However, m_aPoolItemMap is an __unordered__ map, and so using o3tl::hash_combine
1323
    // will not necessarily produce the same hash for two otherwise identical SfxItemSet.
1324
    // Using addition is invariant with respect to ordering, so we sacrifice some hashing
1325
    // quality in favour of correctness.
1326
1.51M
    size_t seed = 0;
1327
1328
20.6M
    for (PoolItemMap::const_iterator it(m_aPoolItemMap.begin()); it != m_aPoolItemMap.end(); it++)
1329
19.1M
    {
1330
19.1M
        const sal_uInt16 nWhich = it->first;
1331
19.1M
        const SfxPoolItem *pItem = it->second;
1332
1333
19.1M
        seed += nWhich;
1334
19.1M
        if (!IsInvalidItem(pItem) && !IsDisabledItem(pItem)
1335
19.1M
            && pItem && pItem->supportsHashCode())
1336
11.6M
            seed += pItem->hashCode();
1337
19.1M
    }
1338
1339
1.51M
    return seed;
1340
1.51M
}
1341
1342
std::unique_ptr<SfxItemSet> SfxItemSet::Clone(bool bItems, SfxItemPool *pToPool ) const
1343
514k
{
1344
514k
    if (pToPool && pToPool != GetPool())
1345
0
    {
1346
0
        std::unique_ptr<SfxItemSet> pNewSet(new SfxItemSet(*pToPool, GetRanges()));
1347
0
        if ( bItems )
1348
0
        {
1349
0
            SfxWhichIter aIter(*pNewSet);
1350
0
            sal_uInt16 nWhich = aIter.FirstWhich();
1351
0
            while ( nWhich )
1352
0
            {
1353
0
                const SfxPoolItem* pItem;
1354
0
                if ( SfxItemState::SET == GetItemState_ForWhichID(SfxItemState::UNKNOWN, nWhich, false, &pItem ) )
1355
0
                    pNewSet->Put( *pItem );
1356
0
                nWhich = aIter.NextWhich();
1357
0
            }
1358
0
        }
1359
0
        return pNewSet;
1360
0
    }
1361
514k
    else
1362
514k
        return std::unique_ptr<SfxItemSet>(bItems
1363
514k
                ? new SfxItemSet(*this)
1364
514k
                : new SfxItemSet(*GetPool(), GetRanges()));
1365
514k
}
1366
1367
SfxItemSet SfxItemSet::CloneAsValue(bool bItems, SfxItemPool *pToPool ) const
1368
840k
{
1369
    // if you are trying to clone, then the thing you are cloning is polymorphic, which means
1370
    // it cannot be cloned as a value
1371
840k
    assert((typeid(*this) == typeid(SfxItemSet)) && "cannot call this on a subclass of SfxItemSet");
1372
1373
840k
    if (pToPool && pToPool != GetPool())
1374
0
    {
1375
0
        SfxItemSet aNewSet(*pToPool, GetRanges());
1376
0
        if ( bItems )
1377
0
        {
1378
0
            SfxWhichIter aIter(aNewSet);
1379
0
            sal_uInt16 nWhich = aIter.FirstWhich();
1380
0
            while ( nWhich )
1381
0
            {
1382
0
                const SfxPoolItem* pItem;
1383
0
                if ( SfxItemState::SET == GetItemState_ForWhichID(SfxItemState::UNKNOWN, nWhich, false, &pItem ) )
1384
0
                    aNewSet.Put( *pItem );
1385
0
                nWhich = aIter.NextWhich();
1386
0
            }
1387
0
        }
1388
0
        return aNewSet;
1389
0
    }
1390
840k
    else
1391
840k
        return bItems
1392
840k
                ? *this
1393
840k
                : SfxItemSet(*GetPool(), GetRanges());
1394
840k
}
1395
1396
void SfxItemSet::dumpAsXml(xmlTextWriterPtr pWriter) const
1397
0
{
1398
0
    tools::XmlWriter aWriter(pWriter);
1399
0
    aWriter.startElement("SfxItemSet");
1400
0
    for (SfxItemIter aIter( *this ); !aIter.IsAtEnd(); aIter.Next())
1401
0
    {
1402
0
        const SfxPoolItem* pItem = aIter.GetCurItem();
1403
0
        if (IsInvalidItem(pItem))
1404
0
        {
1405
0
            aWriter.startElement("invalid");
1406
0
            aWriter.endElement();
1407
0
        }
1408
0
        else
1409
0
        {
1410
0
            pItem->dumpAsXml(pWriter);
1411
0
        }
1412
0
    }
1413
0
    aWriter.endElement();
1414
0
}
1415
1416
1417
// ----------------------------------------------- class SfxAllItemSet
1418
1419
SfxAllItemSet::SfxAllItemSet( SfxItemPool &rPool )
1420
347k
:   SfxItemSet(rPool)
1421
347k
{
1422
347k
}
1423
1424
SfxAllItemSet::SfxAllItemSet(const SfxItemSet &rCopy)
1425
0
:   SfxItemSet(rCopy)
1426
0
{
1427
0
}
1428
1429
/**
1430
 * Explicitly define this ctor to avoid auto-generation by the compiler.
1431
 * The compiler does not take the ctor with the 'const SfxItemSet&'!
1432
 */
1433
SfxAllItemSet::SfxAllItemSet(const SfxAllItemSet &rCopy)
1434
0
:   SfxItemSet(rCopy)
1435
0
{
1436
0
}
1437
1438
/**
1439
 * Putting with automatic extension of the WhichId with the ID of the Item.
1440
 */
1441
const SfxPoolItem* SfxAllItemSet::PutImpl( const SfxPoolItem& rItem, bool bPassingOwnership )
1442
745k
{
1443
745k
    MergeRange(rItem.Which(), rItem.Which());
1444
745k
    return SfxItemSet::PutImpl(rItem, bPassingOwnership);
1445
745k
}
1446
1447
std::unique_ptr<SfxItemSet> SfxAllItemSet::Clone(bool bItems, SfxItemPool *pToPool ) const
1448
0
{
1449
0
    if (pToPool && pToPool != GetPool())
1450
0
    {
1451
0
        std::unique_ptr<SfxAllItemSet> pNewSet(new SfxAllItemSet( *pToPool ));
1452
0
        if ( bItems )
1453
0
            pNewSet->Set( *this );
1454
0
        return pNewSet;
1455
0
    }
1456
0
    else
1457
0
        return std::unique_ptr<SfxItemSet>(bItems ? new SfxAllItemSet(*this) : new SfxAllItemSet(*GetPool()));
1458
0
}
1459
1460
1461
void WhichRangesContainer::CountRanges() const
1462
123M
{
1463
123M
    m_TotalCount = 0;
1464
123M
    for (const auto& rPair : *this)
1465
178M
        m_TotalCount += svl::detail::rangeSize(rPair.first, rPair.second);
1466
123M
}
1467
1468
WhichRangesContainer::WhichRangesContainer( const WhichPair* wids, sal_Int32 nSize )
1469
0
: m_pairs(nullptr)
1470
0
, m_size(nSize)
1471
0
, m_TotalCount(0)
1472
0
, m_aLastWhichPairOffset(INVALID_WHICHPAIR_OFFSET)
1473
0
, m_aLastWhichPairFirst(0)
1474
0
, m_aLastWhichPairSecond(0)
1475
0
, m_bOwnRanges(true)
1476
0
{
1477
0
    auto p = new WhichPair[nSize];
1478
0
    for (int i=0; i<nSize; ++i)
1479
0
        p[i] = wids[i];
1480
0
    m_pairs = p;
1481
0
    CountRanges();
1482
0
}
1483
1484
WhichRangesContainer::WhichRangesContainer(sal_uInt16 nWhichStart, sal_uInt16 nWhichEnd)
1485
70.2M
: m_pairs(nullptr)
1486
70.2M
, m_size(1)
1487
70.2M
, m_TotalCount(0)
1488
70.2M
, m_aLastWhichPairOffset(INVALID_WHICHPAIR_OFFSET)
1489
70.2M
, m_aLastWhichPairFirst(0)
1490
70.2M
, m_aLastWhichPairSecond(0)
1491
70.2M
, m_bOwnRanges(true)
1492
70.2M
{
1493
70.2M
    auto p = new WhichPair[1];
1494
70.2M
    p[0] = { nWhichStart, nWhichEnd };
1495
70.2M
    m_pairs = p;
1496
70.2M
    CountRanges();
1497
70.2M
}
1498
1499
WhichRangesContainer::WhichRangesContainer(WhichRangesContainer && other)
1500
404M
{
1501
404M
    std::swap(m_pairs, other.m_pairs);
1502
404M
    std::swap(m_size, other.m_size);
1503
404M
    std::swap(m_TotalCount, other.m_TotalCount);
1504
404M
    std::swap(m_aLastWhichPairOffset, other.m_aLastWhichPairOffset);
1505
404M
    std::swap(m_aLastWhichPairFirst, other.m_aLastWhichPairFirst);
1506
404M
    std::swap(m_aLastWhichPairSecond, other.m_aLastWhichPairSecond);
1507
404M
    std::swap(m_bOwnRanges, other.m_bOwnRanges);
1508
404M
}
1509
1510
WhichRangesContainer& WhichRangesContainer::operator=(WhichRangesContainer && other)
1511
4.53M
{
1512
4.53M
    std::swap(m_pairs, other.m_pairs);
1513
4.53M
    std::swap(m_size, other.m_size);
1514
4.53M
    std::swap(m_TotalCount, other.m_TotalCount);
1515
4.53M
    std::swap(m_aLastWhichPairOffset, other.m_aLastWhichPairOffset);
1516
4.53M
    std::swap(m_aLastWhichPairFirst, other.m_aLastWhichPairFirst);
1517
4.53M
    std::swap(m_aLastWhichPairSecond, other.m_aLastWhichPairSecond);
1518
4.53M
    std::swap(m_bOwnRanges, other.m_bOwnRanges);
1519
4.53M
    return *this;
1520
4.53M
}
1521
1522
WhichRangesContainer& WhichRangesContainer::operator=(WhichRangesContainer const & other)
1523
430M
{
1524
430M
    reset();
1525
1526
430M
    m_size = other.m_size;
1527
430M
    m_TotalCount = other.m_TotalCount;
1528
430M
    m_aLastWhichPairOffset = other.m_aLastWhichPairOffset;
1529
430M
    m_aLastWhichPairFirst = other.m_aLastWhichPairFirst;
1530
430M
    m_aLastWhichPairSecond = other.m_aLastWhichPairSecond;
1531
430M
    m_bOwnRanges = other.m_bOwnRanges;
1532
1533
430M
    if (m_bOwnRanges)
1534
77.1M
    {
1535
77.1M
        auto p = new WhichPair[m_size];
1536
168M
        for (int i=0; i<m_size; ++i)
1537
90.9M
            p[i] = other.m_pairs[i];
1538
77.1M
        m_pairs = p;
1539
77.1M
    }
1540
353M
    else
1541
353M
        m_pairs = other.m_pairs;
1542
1543
430M
    return *this;
1544
430M
}
1545
1546
WhichRangesContainer::~WhichRangesContainer()
1547
959M
{
1548
959M
    reset();
1549
959M
}
1550
1551
bool WhichRangesContainer::operator==(WhichRangesContainer const & other) const
1552
11.3k
{
1553
11.3k
    if (m_size != other.m_size)
1554
11.3k
        return false;
1555
0
    if (m_TotalCount != other.m_TotalCount)
1556
0
        return false;
1557
0
    if (m_pairs == other.m_pairs)
1558
0
        return true;
1559
0
    return std::equal(m_pairs, m_pairs + m_size, other.m_pairs, other.m_pairs + m_size);
1560
0
}
1561
1562
1563
void WhichRangesContainer::reset()
1564
1.94G
{
1565
1.94G
    if (m_bOwnRanges)
1566
151M
    {
1567
151M
        delete [] m_pairs;
1568
151M
        m_bOwnRanges = false;
1569
151M
    }
1570
1.94G
    m_pairs = nullptr;
1571
1.94G
    m_size = 0;
1572
1.94G
    m_TotalCount = 0;
1573
1.94G
    m_aLastWhichPairOffset = INVALID_WHICHPAIR_OFFSET;
1574
1.94G
    m_aLastWhichPairFirst = 0;
1575
1.94G
    m_aLastWhichPairSecond = 0;
1576
1.94G
}
1577
1578
#ifdef DBG_UTIL
1579
static size_t g_nHit(0);
1580
static size_t g_nMiss(1);
1581
static bool g_bShowWhichRangesHitRate(getenv("SVL_SHOW_WHICHRANGES_HITRATE"));
1582
static void isHit() { g_nHit++; }
1583
static void isMiss()
1584
{
1585
    g_nMiss++;
1586
    const double fHitRate(double(g_nHit) /double(g_nMiss));
1587
    if (0 == g_nMiss % 1000 && g_bShowWhichRangesHitRate)
1588
        SAL_WARN("svl", "ITEM: hits: " << g_nHit << " misses: " << g_nMiss << " hits/misses(rate): " << fHitRate);
1589
}
1590
#endif
1591
1592
bool WhichRangesContainer::doesContainWhich(sal_uInt16 nWhich) const
1593
6.46G
{
1594
    // special case for single entry - happens often e.g. UI stuff
1595
6.46G
    if (m_size == 1)
1596
3.51G
    {
1597
3.51G
        if( m_pairs->first <= nWhich && nWhich <= m_pairs->second )
1598
3.11G
            return true;
1599
1600
        // we have only one WhichPair entry and it's not contained -> failed
1601
399M
        return false;
1602
3.51G
    }
1603
1604
2.94G
    if (m_size == 0)
1605
0
        return false;
1606
1607
    // check if nWhich is inside last successfully used WhichPair
1608
2.94G
    if (INVALID_WHICHPAIR_OFFSET != m_aLastWhichPairOffset
1609
2.64G
        && m_aLastWhichPairFirst <= nWhich
1610
2.53G
        && nWhich <= m_aLastWhichPairSecond)
1611
2.35G
    {
1612
#ifdef DBG_UTIL
1613
        isHit();
1614
#endif
1615
        // we can re-use the last found WhichPair
1616
2.35G
        return true;
1617
2.35G
    }
1618
1619
#ifdef DBG_UTIL
1620
    isMiss();
1621
#endif
1622
1623
    // we have to find the correct WhichPair, iterate linear. This
1624
    // also directly updates the buffered m_aLastWhichPair* values
1625
596M
    m_aLastWhichPairOffset = 0;
1626
1627
596M
    for (const WhichPair& rPair : *this)
1628
2.03G
    {
1629
        // Within this range?
1630
2.03G
        if( rPair.first <= nWhich && nWhich <= rPair.second )
1631
334M
        {
1632
            // found, remember parameters for buffered hits
1633
334M
            m_aLastWhichPairFirst = rPair.first;
1634
334M
            m_aLastWhichPairSecond = rPair.second;
1635
1636
            // ...and return
1637
334M
            return true;
1638
334M
        }
1639
1640
1.70G
        m_aLastWhichPairOffset += rPair.second - rPair.first + 1;
1641
1.70G
    }
1642
1643
    // *need* to reset: if 1st WhichPair only one entry it could be 1
1644
    // what could wrongly trigger re-use above for next search
1645
261M
    m_aLastWhichPairOffset = INVALID_WHICHPAIR_OFFSET;
1646
1647
261M
    return false;
1648
596M
}
1649
1650
// Adds a range to which ranges, keeping the ranges in valid state (sorted, non-overlapping)
1651
WhichRangesContainer WhichRangesContainer::MergeRange(sal_uInt16 nFrom,
1652
                                       sal_uInt16 nTo) const
1653
4.53M
{
1654
4.53M
    assert(svl::detail::validRange(nFrom, nTo));
1655
1656
4.53M
    if (empty())
1657
617k
        return WhichRangesContainer(nFrom, nTo);
1658
1659
    // reset buffer
1660
3.91M
    m_aLastWhichPairOffset = INVALID_WHICHPAIR_OFFSET;
1661
1662
    // create vector of ranges (sal_uInt16 pairs of lower and upper bound)
1663
3.91M
    const size_t nOldCount = size();
1664
    // Allocate one item more than we already have.
1665
    // In the worst case scenario we waste a little bit
1666
    // of memory, but we avoid another allocation, which is more important.
1667
3.91M
    std::unique_ptr<WhichPair[]> aRangesTable(new WhichPair[nOldCount+1]);
1668
3.91M
    int aRangesTableSize = 0;
1669
3.91M
    bool bAdded = false;
1670
3.91M
    for (const auto& rPair : *this)
1671
20.2M
    {
1672
20.2M
        if (!bAdded && rPair.first >= nFrom)
1673
2.16M
        { // insert new range, keep ranges sorted
1674
2.16M
            aRangesTable[aRangesTableSize++] = { nFrom, nTo };
1675
2.16M
            bAdded = true;
1676
2.16M
        }
1677
        // insert current range
1678
20.2M
        aRangesTable[aRangesTableSize++] = rPair;
1679
20.2M
    }
1680
3.91M
    if (!bAdded)
1681
1.74M
        aRangesTable[aRangesTableSize++] = { nFrom, nTo };
1682
1683
    // true if ranges overlap or adjoin, false if ranges are separate
1684
20.2M
    auto needMerge = [](WhichPair lhs, WhichPair rhs) {
1685
20.2M
        return (lhs.first - 1) <= rhs.second && (rhs.first - 1) <= lhs.second;
1686
20.2M
    };
1687
1688
3.91M
    auto it = aRangesTable.get();
1689
3.91M
    auto endIt = aRangesTable.get() + aRangesTableSize;
1690
    // we have at least one range at this point
1691
3.91M
    for (;;)
1692
24.1M
    {
1693
24.1M
        auto itNext = std::next(it);
1694
24.1M
        if (itNext == endIt)
1695
3.91M
            break;
1696
        // check if neighbouring ranges overlap or adjoin
1697
20.2M
        if (needMerge(*it, *itNext))
1698
1.98M
        {
1699
            // lower bounds are sorted, implies: it->first = min(it[0].first, it[1].first)
1700
1.98M
            it->second = std::max(it->second, itNext->second);
1701
            // remove next element
1702
1.98M
            std::move(std::next(itNext), endIt, itNext);
1703
1.98M
            --aRangesTableSize;
1704
1.98M
            endIt = aRangesTable.get() + aRangesTableSize;
1705
1.98M
        }
1706
18.2M
        else
1707
18.2M
            ++it;
1708
20.2M
    }
1709
1710
3.91M
    return WhichRangesContainer(std::move(aRangesTable), aRangesTableSize);
1711
4.53M
}
1712
1713
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */