Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/sc/source/core/data/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 45 kB image not shown  

Quelle  bcaslot.cxx   Sprache: C

 
/* -*- 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 .
 */


#include <svl/listener.hxx>
#include <sal/log.hxx>
#include <osl/diagnose.h>

#include <document.hxx>
#include <docsh.hxx>
#include <brdcst.hxx>
#include <bcaslot.hxx>
#include <scerrors.hxx>
#include <refupdat.hxx>
#include <bulkdatahint.hxx>
#include <columnspanset.hxx>
#include <formulacell.hxx>
#include <grouparealistener.hxx>
#include <broadcast.hxx>

ScBroadcastArea::ScBroadcastArea( const ScRange& rRange ) :
    pUpdateChainNext(nullptr),
    aRange(rRange),
    nRefCount(0),
    mbInUpdateChain(false),
    mbGroupListening(false) {}

ScBroadcastAreaSlot::ScBroadcastAreaSlot( ScDocument& rDocument,
        ScBroadcastAreaSlotMachine* pBASMa ) :
    aTmpSeekBroadcastArea( ScRange()),
    rDoc( rDocument ),
    pBASM( pBASMa ),
    mbInBroadcastIteration( false),
    mbHasErasedArea(false)
{
}

ScBroadcastAreaSlot::~ScBroadcastAreaSlot()
{
    for ( ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
            aIter != aBroadcastAreaTbl.end(); /* none */)
    {
        // Prevent hash from accessing dangling pointer in case area is
        // deleted.
        ScBroadcastArea* pArea = (*aIter).mpArea;
        // Erase all so no hash will be accessed upon destruction of the
        // unordered_map.
        aIter = aBroadcastAreaTbl.erase(aIter);
        if (!pArea->DecRef())
            delete pArea;
    }
}

ScDocument::HardRecalcState ScBroadcastAreaSlot::CheckHardRecalcStateCondition() const
{
    ScDocument::HardRecalcState eState = rDoc.GetHardRecalcState();
    if (eState == ScDocument::HardRecalcState::OFF)
    {
        if (aBroadcastAreaTbl.size() >= aBroadcastAreaTbl.max_size())
        {   // this is more hypothetical now, check existed for old SV_PTRARR_SORT
            ScDocShell* pShell = rDoc.GetDocumentShell();
            OSL_ENSURE( pShell, "Missing DocShell :-/" );

            if ( pShell )
                pShell->SetError(SCWARN_CORE_HARD_RECALC);

            rDoc.SetAutoCalc( false );
            eState = ScDocument::HardRecalcState::ETERNAL;
            rDoc.SetHardRecalcState( eState );
        }
    }
    return eState;
}

bool ScBroadcastAreaSlot::StartListeningArea(
    const ScRange& rRange, bool bGroupListening, SvtListener* pListener, ScBroadcastArea*& rpArea )
{
    bool bNewArea = false;
    OSL_ENSURE(pListener, "StartListeningArea: pListener Null");
    assert(!rDoc.IsDelayedFormulaGrouping()); // otherwise the group size might be incorrect
    if (CheckHardRecalcStateCondition() == ScDocument::HardRecalcState::ETERNAL)
        return false;
    if ( !rpArea )
    {
        // Even if most times the area doesn't exist yet and immediately trying
        // to new and insert it would save an attempt to find it, on massive
        // operations like identical large [HV]LOOKUP() areas the new/delete
        // would add quite some penalty for all but the first formula cell.
        ScBroadcastAreas::const_iterator aIter( FindBroadcastArea( rRange, bGroupListening));
        if (aIter != aBroadcastAreaTbl.end())
            rpArea = (*aIter).mpArea;
        else
        {
            rpArea = new ScBroadcastArea( rRange);
            rpArea->SetGroupListening(bGroupListening);
            if (aBroadcastAreaTbl.insert( rpArea).second)
            {
                rpArea->IncRef();
                bNewArea = true;
            }
            else
            {
                OSL_FAIL("StartListeningArea: area not found and not inserted in slot?!?");
                delete rpArea;
                rpArea = nullptr;
            }
        }
        if (rpArea)
            pListener->StartListening( rpArea->GetBroadcaster());
    }
    else
    {
        if (aBroadcastAreaTbl.insert( rpArea).second)
            rpArea->IncRef();
    }
    return bNewArea;
}

void ScBroadcastAreaSlot::InsertListeningArea( ScBroadcastArea* pArea )
{
    OSL_ENSURE( pArea, "InsertListeningArea: pArea NULL");
    if (CheckHardRecalcStateCondition() == ScDocument::HardRecalcState::ETERNAL)
        return;
    if (aBroadcastAreaTbl.insert( pArea).second)
        pArea->IncRef();
}

// If rpArea != NULL then no listeners are stopped, only the area is removed
// and the reference count decremented.
void ScBroadcastAreaSlot::EndListeningArea(
    const ScRange& rRange, bool bGroupListening, SvtListener* pListener, ScBroadcastArea*& rpArea )
{
    OSL_ENSURE(pListener, "EndListeningArea: pListener Null");
    if ( !rpArea )
    {
        ScBroadcastAreas::iterator aIter( FindBroadcastArea( rRange, bGroupListening));
        if (aIter == aBroadcastAreaTbl.end() || isMarkedErased( aIter))
            return;
        rpArea = (*aIter).mpArea;
        pListener->EndListening( rpArea->GetBroadcaster() );
        if ( !rpArea->GetBroadcaster().HasListeners() )
        {   // if nobody is listening we can dispose it
            if (rpArea->GetRef() == 1)
                rpArea = nullptr;      // will be deleted by erase
            EraseArea( aIter);
        }
    }
    else
    {
        if (rpArea && !rpArea->GetBroadcaster().HasListeners())
        {
            ScBroadcastAreas::iterator aIter( FindBroadcastArea( rRange, bGroupListening));
            if (aIter == aBroadcastAreaTbl.end() || isMarkedErased( aIter))
                return;
            OSL_ENSURE( (*aIter).mpArea == rpArea, "EndListeningArea: area pointer mismatch");
            if (rpArea->GetRef() == 1)
                rpArea = nullptr;      // will be deleted by erase
            EraseArea( aIter);
        }
    }
}

ScBroadcastAreas::iterator ScBroadcastAreaSlot::FindBroadcastArea(
        const ScRange& rRange, bool bGroupListening )
{
    aTmpSeekBroadcastArea.UpdateRange( rRange);
    aTmpSeekBroadcastArea.SetGroupListening(bGroupListening);
    return aBroadcastAreaTbl.find( &aTmpSeekBroadcastArea);
}

namespace {

void broadcastRangeByCell( SvtBroadcaster& rBC, const ScRange& rRange, SfxHintId nHint )
{
    ScHint aHint(nHint, ScAddress());
    for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
    {
        aHint.SetAddressTab(nTab);
        for (SCCOL nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); ++nCol)
        {
            aHint.SetAddressCol(nCol);
            for (SCROW nRow = rRange.aStart.Row(); nRow <= rRange.aEnd.Row(); ++nRow)
            {
                aHint.SetAddressRow(nRow);
                rBC.Broadcast(aHint);
            }
        }
    }
}

}

bool ScBroadcastAreaSlot::AreaBroadcast( const ScRange& rRange, SfxHintId nHint )
{
    if (aBroadcastAreaTbl.empty())
        return false;

    bool bInBroadcast = mbInBroadcastIteration;
    mbInBroadcastIteration = true;
    bool bIsBroadcasted = false;

    mbHasErasedArea = false;

    for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin()),
            aIterEnd( aBroadcastAreaTbl.end()); aIter != aIterEnd; ++aIter )
    {
        if (mbHasErasedArea && isMarkedErased( aIter))
            continue;

        ScBroadcastArea* pArea = (*aIter).mpArea;
        const ScRange& rAreaRange = pArea->GetRange();

        // Take the intersection of the area range and the broadcast range.
        ScRange aIntersection = rAreaRange.Intersection(rRange);
        if (!aIntersection.IsValid())
            continue;

        if (pArea->IsGroupListening())
        {
            if (pBASM->IsInBulkBroadcast())
            {
                pBASM->InsertBulkGroupArea(pArea, aIntersection);
            }
            else
            {
                broadcastRangeByCell(pArea->GetBroadcaster(), aIntersection, nHint);
                bIsBroadcasted = true;
            }
        }
        else if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea))
        {
            broadcastRangeByCell(pArea->GetBroadcaster(), aIntersection, nHint);
            bIsBroadcasted = true;
        }
    }

    mbInBroadcastIteration = bInBroadcast;

    // A Notify() during broadcast may call EndListeningArea() and thus dispose
    // an area if it was the last listener, which would invalidate an iterator
    // pointing to it, hence the real erase is done afterwards.
    FinallyEraseAreas();

    return bIsBroadcasted;
}

bool ScBroadcastAreaSlot::AreaBroadcast( const ScHint& rHint)
{
    if (aBroadcastAreaTbl.empty())
        return false;

    bool bInBroadcast = mbInBroadcastIteration;
    mbInBroadcastIteration = true;
    bool bIsBroadcasted = false;

    mbHasErasedArea = false;

    const ScRange aRange = rHint.GetRange();
    for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin()),
            aIterEnd( aBroadcastAreaTbl.end()); aIter != aIterEnd; ++aIter )
    {
        if (mbHasErasedArea && isMarkedErased( aIter))
            continue;

        ScBroadcastArea* pArea = (*aIter).mpArea;
        const ScRange& rAreaRange = pArea->GetRange();
        if (rAreaRange.Intersects( aRange))
        {
            if (pArea->IsGroupListening())
            {
                if (pBASM->IsInBulkBroadcast())
                {
                    pBASM->InsertBulkGroupArea(pArea, aRange);
                }
                else
                {
                    pArea->GetBroadcaster().Broadcast( rHint);
                    bIsBroadcasted = true;
                }
            }
            else if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea))
            {
                pArea->GetBroadcaster().Broadcast( rHint);
                bIsBroadcasted = true;
            }
        }
    }

    mbInBroadcastIteration = bInBroadcast;

    // A Notify() during broadcast may call EndListeningArea() and thus dispose
    // an area if it was the last listener, which would invalidate an iterator
    // pointing to it, hence the real erase is done afterwards.
    FinallyEraseAreas();

    return bIsBroadcasted;
}

void ScBroadcastAreaSlot::DelBroadcastAreasInRange( const ScRange& rRange )
{
    if (aBroadcastAreaTbl.empty())
        return;
    for (ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
            aIter != aBroadcastAreaTbl.end(); /* increment in body */ )
    {
        const ScRange& rAreaRange = (*aIter).mpArea->GetRange();
        if (rRange.Contains( rAreaRange))
        {
            ScBroadcastArea* pArea = (*aIter).mpArea;
            aIter = aBroadcastAreaTbl.erase(aIter);  // erase before modifying
            if (!pArea->DecRef())
            {
                if (pBASM->IsInBulkBroadcast())
                    pBASM->RemoveBulkArea( pArea);
                delete pArea;
            }
        }
        else
            ++aIter;
    }
}

void ScBroadcastAreaSlot::UpdateRemove( UpdateRefMode eUpdateRefMode,
        const ScRange& rRange, SCCOL nDx, SCROW nDy, SCTAB nDz )
{
    if (aBroadcastAreaTbl.empty())
        return;

    SCCOL nCol1, nCol2, theCol1, theCol2;
    SCROW nRow1, nRow2, theRow1, theRow2;
    SCTAB nTab1, nTab2, theTab1, theTab2;
    rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
    for ( ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin());
            aIter != aBroadcastAreaTbl.end(); /* increment in body */ )
    {
        ScBroadcastArea* pArea = (*aIter).mpArea;
        if ( pArea->IsInUpdateChain() )
        {
            aIter = aBroadcastAreaTbl.erase(aIter);
            pArea->DecRef();
        }
        else
        {
            pArea->GetRange().GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2);
            if ( ScRefUpdate::Update( rDoc, eUpdateRefMode,
                    nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz,
                    theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ))
            {
                aIter = aBroadcastAreaTbl.erase(aIter);
                pArea->DecRef();
                if (pBASM->IsInBulkBroadcast())
                    pBASM->RemoveBulkArea( pArea);
                pArea->SetInUpdateChain( true );
                ScBroadcastArea* pUC = pBASM->GetEOUpdateChain();
                if ( pUC )
                    pUC->SetUpdateChainNext( pArea );
                else    // no tail => no head
                    pBASM->SetUpdateChain( pArea );
                pBASM->SetEOUpdateChain( pArea );
            }
            else
                ++aIter;
        }
    }
}

void ScBroadcastAreaSlot::UpdateRemoveArea( ScBroadcastArea* pArea )
{
    ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.find( pArea));
    if (aIter == aBroadcastAreaTbl.end())
        return;
    if ((*aIter).mpArea != pArea)
        OSL_FAIL( "UpdateRemoveArea: area pointer mismatch");
    else
    {
        aBroadcastAreaTbl.erase( aIter);
        pArea->DecRef();
    }
}

void ScBroadcastAreaSlot::UpdateInsert( ScBroadcastArea* pArea )
{
    ::std::pair< ScBroadcastAreas::iterator, bool > aPair =
        aBroadcastAreaTbl.insert( pArea);
    if (aPair.second)
        pArea->IncRef();
    else
    {
        // Identical area already exists, add listeners.
        ScBroadcastArea* pTarget = (*(aPair.first)).mpArea;
        if (pArea != pTarget)
        {
            SvtBroadcaster& rTarget = pTarget->GetBroadcaster();
            SvtBroadcaster::ListenersType& rListeners = pArea->GetBroadcaster().GetAllListeners();
            for (auto& pListener : rListeners)
            {
                SvtListener& rListener = *pListener;
                rListener.StartListening(rTarget);
            }
        }
    }
}

void ScBroadcastAreaSlot::EraseArea( ScBroadcastAreas::iterator& rIter )
{
    if (mbInBroadcastIteration)
    {
        (*rIter).mbErasure = true;      // mark for erasure
        mbHasErasedArea = true// at least one area is marked for erasure.
        pBASM->PushAreaToBeErased( this, rIter);
    }
    else
    {
        ScBroadcastArea* pArea = (*rIter).mpArea;
        aBroadcastAreaTbl.erase( rIter);
        if (!pArea->DecRef())
        {
            if (pBASM->IsInBulkBroadcast())
                pBASM->RemoveBulkGroupArea(pArea);
            delete pArea;
        }
    }
}

void ScBroadcastAreaSlot::GetAllListeners(
    const ScRange& rRange, std::vector<sc::AreaListener>& rListeners,
    sc::AreaOverlapType eType, sc::ListenerGroupType eGroup )
{
    for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin()),
            aIterEnd( aBroadcastAreaTbl.end()); aIter != aIterEnd; ++aIter )
    {
        if (isMarkedErased( aIter))
            continue;

        ScBroadcastArea* pArea = (*aIter).mpArea;
        const ScRange& rAreaRange = pArea->GetRange();
        switch (eGroup)
        {
            case sc::ListenerGroupType::Group:
                if (!pArea->IsGroupListening())
                    continue;
            break;
            case sc::ListenerGroupType::Both:
            default:
                ;
        }

        switch (eType)
        {
            case sc::AreaOverlapType::Inside:
                if (!rRange.Contains(rAreaRange))
                    // The range needs to be fully inside specified range.
                    continue;
                break;
            case sc::AreaOverlapType::InsideOrOverlap:
                if (!rRange.Intersects(rAreaRange))
                    // The range needs to be partially overlapping or fully inside.
                    continue;
                break;
            case sc::AreaOverlapType::OneRowInside:
                if (rAreaRange.aStart.Row() != rAreaRange.aEnd.Row() || !rRange.Contains(rAreaRange))
                    // The range needs to be one single row and fully inside
                    // specified range.
                    continue;
                break;
            case sc::AreaOverlapType::OneColumnInside:
                if (rAreaRange.aStart.Col() != rAreaRange.aEnd.Col() || !rRange.Contains(rAreaRange))
                    // The range needs to be one single column and fully inside
                    // specified range.
                    continue;
                break;
        }

        SvtBroadcaster::ListenersType& rLst = pArea->GetBroadcaster().GetAllListeners();
        for (const auto& pListener : rLst)
        {
            sc::AreaListener aEntry;
            aEntry.maArea = rAreaRange;
            aEntry.mbGroupListening = pArea->IsGroupListening();
            aEntry.mpListener = pListener;
            rListeners.push_back(aEntry);
        }
    }
}

void ScBroadcastAreaSlot::CollectBroadcasterState(sc::BroadcasterState& rStateconst
{
    for (const ScBroadcastAreaEntry& rEntry : aBroadcastAreaTbl)
    {
        const ScRange& rRange = rEntry.mpArea->GetRange();
        auto aRes = rState.aAreaListenerStore.try_emplace(rRange);
        auto& rLisStore = aRes.first->second;

        for (const SvtListener* pLis : rEntry.mpArea->GetBroadcaster().GetAllListeners())
        {
            if (auto pFC = dynamic_cast<const ScFormulaCell*>(pLis); pFC)
            {
                rLisStore.emplace_back(pFC);
                continue;
            }

            if (auto pFGL = dynamic_cast<const sc::FormulaGroupAreaListener*>(pLis); pFGL)
            {
                rLisStore.emplace_back(pFGL);
                continue;
            }

            rLisStore.emplace_back(pLis);
        }
    }
}

void ScBroadcastAreaSlot::FinallyEraseAreas()
{
    pBASM->FinallyEraseAreas( this);
}

// --- ScBroadcastAreaSlotMachine -------------------------------------

ScBroadcastAreaSlotMachine::TableSlots::TableSlots(SCSIZE nBcaSlots)
    : mnBcaSlots(nBcaSlots)
{
    ppSlots.reset( new ScBroadcastAreaSlot* [ nBcaSlots ] );
    memset( ppSlots.get(), 0 , sizeof( ScBroadcastAreaSlot* ) * nBcaSlots );
}

ScBroadcastAreaSlotMachine::TableSlots::TableSlots(TableSlots&& rOther) noexcept
    : mnBcaSlots(rOther.mnBcaSlots)
    , ppSlots( std::move(rOther.ppSlots) )
{
}

ScBroadcastAreaSlotMachine::TableSlots::~TableSlots()
{
    if (ppSlots)
        for ( ScBroadcastAreaSlot** pp = ppSlots.get() + mnBcaSlots; --pp >= ppSlots.get(); /* nothing */ )
            delete *pp;
}

ScBroadcastAreaSlotMachine::ScBroadcastAreaSlotMachine(
        ScDocument& rDocument ) :
    rDoc( rDocument ),
    pUpdateChain( nullptr ),
    pEOUpdateChain( nullptr ),
    nInBulkBroadcast( 0 )
{
    // initSlotDistribution ---------
    // Logarithmic or any other distribution.
    // Upper and leftmost sheet part usually is more populated and referenced and gets fine
    // grained resolution, larger data in larger hunks.
    // Just like with cells, slots are organized in columns. Slot 0 is for first nSliceRow x nSliceCol
    // cells, slot 1 is for next nSliceRow x nSliceCel cells below, etc. After a while the size of row
    // slice doubles (making more cells share the same slot), this distribution data is stored
    // in ScSlotData including ranges of cells. This is repeated for another column of nSliceCol cells,
    // again with the column slice doubling after some time.
    // Functions ComputeSlotOffset(), ComputeArePoints() and ComputeNextSlot() do the necessary
    // calculations.
    SCSIZE nSlots = 0;
    // This should be SCCOL, but that's only 16bit and would overflow when doubling 16k columns.
    sal_Int32 nCol1 = 0;
    sal_Int32 nCol2 = 1024;
    SCSIZE nSliceCol = 16;
    while (nCol2 <= rDoc.GetMaxColCount())
    {
        SCROW nRow1 = 0;
        SCROW nRow2 = 32*1024;
        SCSIZE nSliceRow = 128;
        SCSIZE nSlotsCol = 0;
        SCSIZE nSlotsStartCol = nSlots;
        // Must be sorted by row1,row2!
        while (nRow2 <= rDoc.GetMaxRowCount())
        {
            maSlotDistribution.emplace_back(nRow1, nRow2, nSliceRow, nSlotsCol, nCol1, nCol2, nSliceCol, nSlotsStartCol);
            nSlotsCol += (nRow2 - nRow1) / nSliceRow;
            nRow1 = nRow2;
            nRow2 *= 2;
            nSliceRow *= 2;
        }
        // Store the number of slots in a column in mnBcaSlotsCol, so that finding a slot
        // to the right can be computed quickly in ComputeNextSlot().
        if(nCol1 == 0)
            mnBcaSlotsCol = nSlotsCol;
        assert(nSlotsCol == mnBcaSlotsCol);
        nSlots += (nCol2 - nCol1) / nSliceCol * nSlotsCol;
        nCol1 = nCol2;
        nCol2 *= 2;
        nSliceCol *= 2;
    }
    mnBcaSlots = nSlots;
#ifdef DBG_UTIL
    DoChecks();
#endif
}

ScBroadcastAreaSlotMachine::~ScBroadcastAreaSlotMachine()
{
    aTableSlotsMap.clear();
    pBCAlways.reset();
    // Areas to-be-erased still present is a serious error in handling, but at
    // this stage there's nothing we can do anymore.
    SAL_WARN_IF( !maAreasToBeErased.empty(), "sc.core""ScBroadcastAreaSlotMachine::dtor: maAreasToBeErased not empty");
}

inline SCSIZE ScBroadcastAreaSlotMachine::ComputeSlotOffset(
        const ScAddress& rAddress ) const
{
    SCROW nRow = rAddress.Row();
    SCCOL nCol = rAddress.Col();
    if ( !rDoc.ValidRow(nRow) || !rDoc.ValidCol(nCol) )
    {
        OSL_FAIL( "Row/Col invalid, using first slot!" );
        return 0;
    }
    for (const ScSlotData& rSD : maSlotDistribution)
    {
        if (nRow < rSD.nStopRow && nCol < rSD.nStopCol)
        {
            assert(nRow >= rSD.nStartRow);
            assert(nCol >= rSD.nStartCol);
            SCSIZE slot = rSD.nCumulatedRow
                + static_cast<SCSIZE>(nRow - rSD.nStartRow) / rSD.nSliceRow
                + rSD.nCumulatedCol
                + static_cast<SCSIZE>(nCol - rSD.nStartCol) / rSD.nSliceCol * mnBcaSlotsCol;
            assert(slot < mnBcaSlots);
            return slot;
        }
    }
    OSL_FAIL( "No slot found, using last!" );
    return mnBcaSlots - 1;
}

void ScBroadcastAreaSlotMachine::ComputeAreaPoints( const ScRange& rRange,
        SCSIZE& rStart, SCSIZE& rEnd, SCSIZE& rRowBreak ) const
{
    rStart = ComputeSlotOffset( rRange.aStart );
    rEnd = ComputeSlotOffset( rRange.aEnd );
    // count of row slots per column minus one
    rRowBreak = ComputeSlotOffset(
        ScAddress( rRange.aStart.Col(), rRange.aEnd.Row(), 0 ) ) - rStart;
}

static void ComputeNextSlot( SCSIZE & nOff, SCSIZE & nBreak, ScBroadcastAreaSlot** & pp,
        SCSIZE & nStart, ScBroadcastAreaSlot** const & ppSlots, SCSIZE nRowBreak, SCSIZE nBcaSlotsCol )
{
    if ( nOff < nBreak )
    {
        ++nOff;
        ++pp;
    }
    else
    {
        nStart += nBcaSlotsCol;
        nOff = nStart;
        pp = ppSlots + nOff;
        nBreak = nOff + nRowBreak;
    }
}

#ifdef DBG_UTIL
static void compare(SCSIZE value1, SCSIZE value2, int line)
{
    if(value1!=value2)
        SAL_WARN("sc""V1:" << value1 << " V2:" << value2 << " (" << line << ")");
    assert(value1 == value2);
}

// Basic checks that the calculations work correctly.
void ScBroadcastAreaSlotMachine::DoChecks()
{
    // Copy&paste from the ctor.
    constexpr SCSIZE nSliceRow = 128;
    constexpr SCSIZE nSliceCol = 16;
    // First and second column are in the same slice and so get the same slot.
    compare( ComputeSlotOffset( ScAddress( 0, 0, 0 )), ComputeSlotOffset( ScAddress( 1, 0, 0 )), __LINE__);
    // Each nSliceRow rows are offset by one slot (at the start of the logarithmic distribution).
    compare( ComputeSlotOffset( ScAddress( 0, 0, 0 )),
             ComputeSlotOffset( ScAddress( 0, nSliceRow, 0 )) - 1, __LINE__ );
    compare( ComputeSlotOffset( ScAddress( nSliceCol - 1, 0, 0 )),
             ComputeSlotOffset( ScAddress( nSliceCol, 0, 0 )) - mnBcaSlotsCol, __LINE__ );
    // Check that last cell is the last slot.
    compare( ComputeSlotOffset( ScAddress( rDoc.GetMaxColCount() - 1, rDoc.GetMaxRowCount() - 1, 0 )),
             mnBcaSlots - 1, __LINE__ );
    // Check that adjacent rows in the same column but in different distribution areas differ by one slot.
    for( size_t i = 0; i < maSlotDistribution.size() - 1; ++i )
    {
        const ScSlotData& s1 = maSlotDistribution[ i ];
        const ScSlotData& s2 = maSlotDistribution[ i + 1 ];
        if( s1.nStartCol == s2.nStartCol )
        {
            assert( s1.nStopRow == s2.nStartRow );
            compare( ComputeSlotOffset( ScAddress( s1.nStartCol, s1.nStopRow - 1, 0 )),
                     ComputeSlotOffset( ScAddress( s1.nStartCol, s1.nStopRow, 0 )) - 1, __LINE__ );
        }
    }
    // Check that adjacent columns in the same row but in different distribution areas differ by mnBcaSlotsCol.
    for( size_t i = 0; i < maSlotDistribution.size() - 1; ++i )
    {
        const ScSlotData& s1 = maSlotDistribution[ i ];
        for( size_t j = i + 1; j < maSlotDistribution.size(); ++j )
        {
            const ScSlotData& s2 = maSlotDistribution[ i + 1 ];
            if( s1.nStartRow == s2.nStartRow && s1.nStopCol == s2.nStartCol )
            {
                assert( s1.nStopRow == s2.nStartRow );
                compare( ComputeSlotOffset( ScAddress( s1.nStopCol - 1, s1.nStartRow, 0 )),
                         ComputeSlotOffset( ScAddress( s1.nStopCol, s1.nStartRow, 0 )) - mnBcaSlotsCol, __LINE__ );
            }
        }
    }
    // Iterate all slots.
    ScRange range( ScAddress( 0, 0, 0 ), ScAddress( rDoc.MaxCol(), rDoc.MaxRow(), 0 ));
    SCSIZE nStart, nEnd, nRowBreak;
    ComputeAreaPoints( range, nStart, nEnd, nRowBreak );
    assert( nStart == 0 );
    assert( nEnd == mnBcaSlots - 1 );
    SCSIZE nOff = nStart;
    SCSIZE nBreak = nOff + nRowBreak;
    std::unique_ptr<ScBroadcastAreaSlot*[]> slots( new ScBroadcastAreaSlot*[ mnBcaSlots ] ); // dummy, not accessed
    ScBroadcastAreaSlot** ppSlots = slots.get();
    ScBroadcastAreaSlot** pp = ppSlots;
    while ( nOff <= nEnd )
    {
        SCSIZE previous = nOff;
        ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
        compare( nOff, previous + 1, __LINE__ );
    }
    // Iterate slots in the last row (each will differ by mnBcaSlotsCol).
    range = ScRange( ScAddress( 0, rDoc.MaxRow(), 0 ),
                     ScAddress( rDoc.MaxCol(), rDoc.MaxRow() - 1, 0 ));
    ComputeAreaPoints( range, nStart, nEnd, nRowBreak );
    assert( nStart == mnBcaSlotsCol - 1 );
    assert( nEnd == mnBcaSlots - 1 );
    nOff = nStart;
    nBreak = nOff + nRowBreak;
    ppSlots = slots.get();
    pp = ppSlots;
    while ( nOff <= nEnd )
    {
        SCSIZE previous = nOff;
        ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
        compare( nOff, previous + mnBcaSlotsCol, __LINE__ );
    }
}
#endif

void ScBroadcastAreaSlotMachine::StartListeningArea(
    const ScRange& rRange, bool bGroupListening, SvtListener* pListener )
{
    if ( rRange == BCA_LISTEN_ALWAYS  )
    {
        if ( !pBCAlways )
            pBCAlways.reset( new SvtBroadcaster );
        pListener->StartListening( *pBCAlways );
    }
    else
    {
        // A new area needs to be inserted to the corresponding slots, for 3D
        // ranges for all sheets, do not slice into per sheet areas or the
        // !bDone will break too early (i.e. after the first sheet) if
        // subsequent listeners are to be added.
        ScBroadcastArea* pArea = nullptr;
        bool bDone = false;
        for (SCTAB nTab = rRange.aStart.Tab();
                !bDone && nTab <= rRange.aEnd.Tab(); ++nTab)
        {
            TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
            if (iTab == aTableSlotsMap.end())
                iTab = aTableSlotsMap.emplace( std::piecewise_construct,
                        std::forward_as_tuple(nTab), std::forward_as_tuple(mnBcaSlots) ).first;
            ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
            SCSIZE nStart, nEnd, nRowBreak;
            ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
            SCSIZE nOff = nStart;
            SCSIZE nBreak = nOff + nRowBreak;
            ScBroadcastAreaSlot** pp = ppSlots + nOff;
            while ( !bDone && nOff <= nEnd )
            {
                if ( !*pp )
                    *pp = new ScBroadcastAreaSlot( rDoc, this );
                if (!pArea)
                {
                    // If the call to StartListeningArea didn't create the
                    // ScBroadcastArea, listeners were added to an already
                    // existing identical area that doesn't need to be inserted
                    // to slots again.
                    if (!(*pp)->StartListeningArea( rRange, bGroupListening, pListener, pArea))
                        bDone = true;
                }
                else
                    (*pp)->InsertListeningArea( pArea);
                ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
            }
        }
    }
}

void ScBroadcastAreaSlotMachine::EndListeningArea(
    const ScRange& rRange, bool bGroupListening, SvtListener* pListener )
{
    if ( rRange == BCA_LISTEN_ALWAYS  )
    {
        if ( pBCAlways )
        {
            pListener->EndListening( *pBCAlways);
            if (!pBCAlways->HasListeners())
            {
                pBCAlways.reset();
            }
        }
    }
    else
    {
        SCTAB nEndTab = rRange.aEnd.Tab();
        for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
                iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
        {
            ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
            SCSIZE nStart, nEnd, nRowBreak;
            ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
            SCSIZE nOff = nStart;
            SCSIZE nBreak = nOff + nRowBreak;
            ScBroadcastAreaSlot** pp = ppSlots + nOff;
            ScBroadcastArea* pArea = nullptr;
            if (nOff == 0 && nEnd == mnBcaSlots-1)
            {
                // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
                // happen for insertion and deletion of sheets.
                ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
                do
                {
                    if ( *pp )
                        (*pp)->EndListeningArea( rRange, bGroupListening, pListener, pArea);
                } while (++pp < pStop);
            }
            else
            {
                while ( nOff <= nEnd )
                {
                    if ( *pp )
                        (*pp)->EndListeningArea( rRange, bGroupListening, pListener, pArea);
                    ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
                }
            }
        }
    }
}

bool ScBroadcastAreaSlotMachine::AreaBroadcast( const ScRange& rRange, SfxHintId nHint )
{
    bool bBroadcasted = false;
    SCTAB nEndTab = rRange.aEnd.Tab();
    for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
            iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
    {
        ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
        SCSIZE nStart, nEnd, nRowBreak;
        ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
        SCSIZE nOff = nStart;
        SCSIZE nBreak = nOff + nRowBreak;
        ScBroadcastAreaSlot** pp = ppSlots + nOff;
        while ( nOff <= nEnd )
        {
            if ( *pp )
                bBroadcasted |= (*pp)->AreaBroadcast( rRange, nHint );
            ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
        }
    }
    return bBroadcasted;
}

bool ScBroadcastAreaSlotMachine::AreaBroadcast( const ScHint& rHint ) const
{
    const ScAddress& rAddress = rHint.GetStartAddress();
    if ( rAddress == BCA_BRDCST_ALWAYS )
    {
        if ( pBCAlways )
        {
            pBCAlways->Broadcast( rHint );
            return true;
        }
        else
            return false;
    }
    else
    {
        TableSlotsMap::const_iterator iTab( aTableSlotsMap.find( rAddress.Tab()));
        if (iTab == aTableSlotsMap.end())
            return false;
        // Process all slots for the given row range.
        ScRange broadcastRange( rAddress,
            ScAddress( rAddress.Col(), rAddress.Row() + rHint.GetRowCount() - 1, rAddress.Tab()));
        bool bBroadcasted = false;
        ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
        SCSIZE nStart, nEnd, nRowBreak;
        ComputeAreaPoints( broadcastRange, nStart, nEnd, nRowBreak );
        SCSIZE nOff = nStart;
        SCSIZE nBreak = nOff + nRowBreak;
        ScBroadcastAreaSlot** pp = ppSlots + nOff;
        while ( nOff <= nEnd )
        {
            if ( *pp )
                bBroadcasted |= (*pp)->AreaBroadcast( rHint );
            ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
        }
        return bBroadcasted;
    }
}

void ScBroadcastAreaSlotMachine::DelBroadcastAreasInRange(
        const ScRange& rRange )
{
    SCTAB nEndTab = rRange.aEnd.Tab();
    for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
            iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
    {
        ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
        SCSIZE nStart, nEnd, nRowBreak;
        ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
        SCSIZE nOff = nStart;
        SCSIZE nBreak = nOff + nRowBreak;
        ScBroadcastAreaSlot** pp = ppSlots + nOff;
        if (nOff == 0 && nEnd == mnBcaSlots-1)
        {
            // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
            // happen for insertion and deletion of sheets.
            ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
            do
            {
                if ( *pp )
                    (*pp)->DelBroadcastAreasInRange( rRange );
            } while (++pp < pStop);
        }
        else
        {
            while ( nOff <= nEnd )
            {
                if ( *pp )
                    (*pp)->DelBroadcastAreasInRange( rRange );
                ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
            }
        }
    }
}

// for all affected: remove, chain, update range, insert, and maybe delete
void ScBroadcastAreaSlotMachine::UpdateBroadcastAreas(
        UpdateRefMode eUpdateRefMode,
        const ScRange& rRange, SCCOL nDx, SCROW nDy, SCTAB nDz )
{
    // remove affected and put in chain
    SCTAB nEndTab = rRange.aEnd.Tab();
    for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
            iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
    {
        ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
        SCSIZE nStart, nEnd, nRowBreak;
        ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
        SCSIZE nOff = nStart;
        SCSIZE nBreak = nOff + nRowBreak;
        ScBroadcastAreaSlot** pp = ppSlots + nOff;
        if (nOff == 0 && nEnd == mnBcaSlots-1)
        {
            // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they
            // happen for insertion and deletion of sheets.
            ScBroadcastAreaSlot** const pStop = ppSlots + nEnd;
            do
            {
                if ( *pp )
                    (*pp)->UpdateRemove( eUpdateRefMode, rRange, nDx, nDy, nDz );
            } while (++pp < pStop);
        }
        else
        {
            while ( nOff <= nEnd )
            {
                if ( *pp )
                    (*pp)->UpdateRemove( eUpdateRefMode, rRange, nDx, nDy, nDz );
                ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
            }
        }
    }

    // Updating an area's range will modify the hash key, remove areas from all
    // affected slots. Will be reinserted later with the updated range.
    ScBroadcastArea* pChain = pUpdateChain;
    while (pChain)
    {
        ScBroadcastArea* pArea = pChain;
        pChain = pArea->GetUpdateChainNext();
        ScRange aRange( pArea->GetRange());
        // remove from slots
        for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab() && pArea->GetRef(); ++nTab)
        {
            TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
            if (iTab == aTableSlotsMap.end())
            {
                OSL_FAIL( "UpdateBroadcastAreas: Where's the TableSlot?!?");
                continue;   // for
            }
            ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
            SCSIZE nStart, nEnd, nRowBreak;
            ComputeAreaPoints( aRange, nStart, nEnd, nRowBreak );
            SCSIZE nOff = nStart;
            SCSIZE nBreak = nOff + nRowBreak;
            ScBroadcastAreaSlot** pp = ppSlots + nOff;
            while ( nOff <= nEnd && pArea->GetRef() )
            {
                if (*pp)
                    (*pp)->UpdateRemoveArea( pArea);
                ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
            }
        }

    }

    // shift sheets
    if (nDz)
    {
        if (nDz < 0)
        {
            TableSlotsMap::iterator iDel( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
            TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab() - nDz));
            // Remove sheets, if any, iDel or/and iTab may as well point to end().
            while (iDel != iTab)
            {
                iDel = aTableSlotsMap.erase(iDel);
            }
            // shift remaining down
            while (iTab != aTableSlotsMap.end())
            {
                SCTAB nTab = (*iTab).first + nDz;
                aTableSlotsMap.emplace(nTab, std::move((*iTab).second));
                iTab = aTableSlotsMap.erase(iTab);
            }
        }
        else
        {
            TableSlotsMap::iterator iStop( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
            if (iStop != aTableSlotsMap.end())
            {
                bool bStopIsBegin = (iStop == aTableSlotsMap.begin());
                if (!bStopIsBegin)
                    --iStop;
                TableSlotsMap::iterator iTab( aTableSlotsMap.end());
                --iTab;
                while (iTab != iStop)
                {
                    SCTAB nTab = (*iTab).first + nDz;
                    aTableSlotsMap.emplace(nTab, std::move((*iTab).second));
                    aTableSlotsMap.erase( iTab--);
                }
                // Shift the very first, iTab==iStop in this case.
                if (bStopIsBegin)
                {
                    SCTAB nTab = (*iTab).first + nDz;
                    aTableSlotsMap.emplace(nTab, std::move((*iTab).second));
                    aTableSlotsMap.erase( iStop);
                }
            }
        }
    }

    // work off chain
    SCCOL nCol1, nCol2, theCol1, theCol2;
    SCROW nRow1, nRow2, theRow1, theRow2;
    SCTAB nTab1, nTab2, theTab1, theTab2;
    rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
    while ( pUpdateChain )
    {
        ScBroadcastArea* pArea = pUpdateChain;
        ScRange aRange( pArea->GetRange());
        pUpdateChain = pArea->GetUpdateChainNext();

        // update range
        aRange.GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2);
        if ( ScRefUpdate::Update( rDoc, eUpdateRefMode,
                nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz,
                theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ))
        {
            aRange = ScRange( theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 );
            pArea->UpdateRange( aRange );
            // For DDE and ScLookupCache
            pArea->GetBroadcaster().Broadcast( ScAreaChangedHint( aRange ) );
        }

        // insert to slots
        for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab(); ++nTab)
        {
            TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab));
            if (iTab == aTableSlotsMap.end())
                iTab = aTableSlotsMap.emplace( std::piecewise_construct,
                        std::forward_as_tuple(nTab), std::forward_as_tuple(mnBcaSlots) ).first;
            ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
            SCSIZE nStart, nEnd, nRowBreak;
            ComputeAreaPoints( aRange, nStart, nEnd, nRowBreak );
            SCSIZE nOff = nStart;
            SCSIZE nBreak = nOff + nRowBreak;
            ScBroadcastAreaSlot** pp = ppSlots + nOff;
            while ( nOff <= nEnd )
            {
                if (!*pp)
                    *pp = new ScBroadcastAreaSlot( rDoc, this );
                (*pp)->UpdateInsert( pArea );
                ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
            }
        }

        // unchain
        pArea->SetUpdateChainNext( nullptr );
        pArea->SetInUpdateChain( false );

        // Delete if not inserted to any slot. RemoveBulkArea(pArea) was
        // already executed in UpdateRemove().
        if (!pArea->GetRef())
            delete pArea;
    }
    pEOUpdateChain = nullptr;
}

void ScBroadcastAreaSlotMachine::EnterBulkBroadcast()
{
    ++nInBulkBroadcast;
}

void ScBroadcastAreaSlotMachine::LeaveBulkBroadcast( SfxHintId nHintId )
{
    if (nInBulkBroadcast <= 0)
        return;

    if (--nInBulkBroadcast == 0)
    {
        ScBroadcastAreasBulk().swap( aBulkBroadcastAreas);
        bool bBroadcasted = BulkBroadcastGroupAreas();
        // Trigger the "final" tracking.
        if (rDoc.IsTrackFormulasPending())
            rDoc.FinalTrackFormulas( nHintId );
        else if (bBroadcasted)
            rDoc.TrackFormulas( nHintId );
    }
}

bool ScBroadcastAreaSlotMachine::InsertBulkArea( const ScBroadcastArea* pArea )
{
    return aBulkBroadcastAreas.insert( pArea ).second;
}

void ScBroadcastAreaSlotMachine::InsertBulkGroupArea( ScBroadcastArea* pArea, const ScRange& rRange )
{
    BulkGroupAreasType::iterator it = m_BulkGroupAreas.lower_bound(pArea);
    if (it == m_BulkGroupAreas.end() || m_BulkGroupAreas.key_comp()(pArea, it->first))
    {
        // Insert a new one.
        it = m_BulkGroupAreas.insert(it, std::make_pair(pArea, sc::ColumnSpanSet()));
    }

    sc::ColumnSpanSet& rSet = it->second;
    rSet.set(rDoc, rRange, true);
}

bool ScBroadcastAreaSlotMachine::BulkBroadcastGroupAreas()
{
    if (m_BulkGroupAreas.empty())
        return false;

    sc::BulkDataHint aHint( rDoc );

    bool bBroadcasted = false;
    for (const auto& [pArea, rSpans] : m_BulkGroupAreas)
    {
        assert(pArea);
        SvtBroadcaster& rBC = pArea->GetBroadcaster();
        if (!rBC.HasListeners())
        {
            /* FIXME: find the cause where the last listener is removed and
             * this area is still listed here. */

            SAL_WARN("sc.core","ScBroadcastAreaSlotMachine::BulkBroadcastGroupAreas - pArea has no listeners and should had been removed already");
        }
        else
        {
            aHint.setSpans(&rSpans);
            rBC.Broadcast(aHint);
            bBroadcasted = true;
        }
    }

    m_BulkGroupAreas.clear();

    return bBroadcasted;
}

size_t ScBroadcastAreaSlotMachine::RemoveBulkArea( const ScBroadcastArea* pArea )
{
    return aBulkBroadcastAreas.erase( pArea );
}

void ScBroadcastAreaSlotMachine::RemoveBulkGroupArea( ScBroadcastArea* pArea )
{
    m_BulkGroupAreas.erase(pArea);
}

void ScBroadcastAreaSlotMachine::PushAreaToBeErased( ScBroadcastAreaSlot* pSlot,
        ScBroadcastAreas::iterator& rIter )
{
    maAreasToBeErased.emplace_back( pSlot, rIter);
}

void ScBroadcastAreaSlotMachine::FinallyEraseAreas( ScBroadcastAreaSlot* pSlot )
{
    SAL_WARN_IF( pSlot->IsInBroadcastIteration(), "sc.core",
            "ScBroadcastAreaSlotMachine::FinallyEraseAreas: during iteration? NO!");
    if (pSlot->IsInBroadcastIteration())
        return;

    // maAreasToBeErased is a simple vector so erasing an element may
    // invalidate iterators and would be inefficient anyway. Instead, copy
    // elements to be preserved (usually none!) to temporary vector and swap.
    AreasToBeErased aCopy;
    for (auto& rArea : maAreasToBeErased)
    {
        if (rArea.first == pSlot)
            pSlot->EraseArea( rArea.second);
        else
            aCopy.push_back( rArea);
    }
    maAreasToBeErased.swap( aCopy);
}

std::vector<sc::AreaListener> ScBroadcastAreaSlotMachine::GetAllListeners(
    const ScRange& rRange, sc::AreaOverlapType eType, sc::ListenerGroupType eGroup )
{
    std::vector<sc::AreaListener> aRet;

    SCTAB nEndTab = rRange.aEnd.Tab();
    for (TableSlotsMap::const_iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab()));
            iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab)
    {
        ScBroadcastAreaSlot** ppSlots = (*iTab).second.getSlots();
        SCSIZE nStart, nEnd, nRowBreak;
        ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak );
        SCSIZE nOff = nStart;
        SCSIZE nBreak = nOff + nRowBreak;
        ScBroadcastAreaSlot** pp = ppSlots + nOff;
        while ( nOff <= nEnd )
        {
            ScBroadcastAreaSlot* p = *pp;
            if (p)
                p->GetAllListeners(rRange, aRet, eType, eGroup);
            ComputeNextSlot( nOff, nBreak, pp, nStart, ppSlots, nRowBreak, mnBcaSlotsCol);
        }
    }

    return aRet;
}

void ScBroadcastAreaSlotMachine::CollectBroadcasterState(sc::BroadcasterState& ;rState) const
{
    for (const auto& [rTab, rTabSlots] : aTableSlotsMap)
    {
        (void)rTab;

        ScBroadcastAreaSlot** pp = rTabSlots.getSlots();
        for (SCSIZE i = 0; i < mnBcaSlots; ++i)
        {
            const ScBroadcastAreaSlot* pSlot = pp[i];
            if (pSlot)
                pSlot->CollectBroadcasterState(rState);
        }
    }
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5
C=92 H=76 G=83

¤ Dauer der Verarbeitung: 0.14 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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.