tdf#120445 File-open ODS: Slower as compared to LibO 4.4.7.2

This takes opening the file from 21s to 9.4s on my machine

Change-Id: I38248f3c9acfa413fc105fcbe9ece06192389d46
Reviewed-on: https://gerrit.libreoffice.org/70073
Tested-by: Jenkins
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
diff --git a/sc/inc/markarr.hxx b/sc/inc/markarr.hxx
index 4b538d9..d30168d 100644
--- a/sc/inc/markarr.hxx
+++ b/sc/inc/markarr.hxx
@@ -43,17 +43,19 @@
public:
            ScMarkArray();
            ScMarkArray( ScMarkArray&& rArray );
            ScMarkArray( const ScMarkArray& rArray );
            ~ScMarkArray();
    void    Reset( bool bMarked = false, SCSIZE nNeeded = 1 );
    bool    GetMark( SCROW nRow ) const;
    void    SetMarkArea( SCROW nStartRow, SCROW nEndRow, bool bMarked );
    bool    IsAllMarked( SCROW nStartRow, SCROW nEndRow ) const;
    bool    HasOneMark( SCROW& rStartRow, SCROW& rEndRow ) const;
    bool    HasEqualRowsMarked( const ScMarkArray& rOther ) const;

    bool    HasMarks() const    { return ( nCount > 1 || ( nCount == 1 && pData[0].bMarked ) ); }

    void    CopyMarksTo( ScMarkArray& rDestMarkArray ) const;
    ScMarkArray& operator=( ScMarkArray const & rSource );
    ScMarkArray& operator=( ScMarkArray&& rSource );
    bool operator==(ScMarkArray const & rOther ) const;

    bool    Search( SCROW nRow, SCSIZE& nIndex ) const;

diff --git a/sc/inc/markmulti.hxx b/sc/inc/markmulti.hxx
index ee92da3..dc0f0b2 100644
--- a/sc/inc/markmulti.hxx
+++ b/sc/inc/markmulti.hxx
@@ -23,13 +23,13 @@
#include "segmenttree.hxx"
#include "markarr.hxx"

#include <map>
#include <vector>

class ScMultiSel
{

private:
    typedef std::map<SCCOL, ScMarkArray> MapType;
    typedef std::vector<ScMarkArray> MapType;
    MapType aMultiSelContainer;
    ScMarkArray aRowSel;

@@ -43,11 +43,7 @@
    ScMultiSel& operator=(const ScMultiSel& rMultiSel);
    ScMultiSel& operator=(const ScMultiSel&& rMultiSel) = delete;

    SCCOL size() const
    {
        return static_cast<SCCOL>( aMultiSelContainer.size() );
    }

    SCCOL GetMultiSelectionCount() const;
    bool HasMarks( SCCOL nCol ) const;
    bool HasOneMark( SCCOL nCol, SCROW& rStartRow, SCROW& rEndRow ) const;
    bool GetMark( SCCOL nCol, SCROW nRow ) const;
diff --git a/sc/qa/unit/mark_test.cxx b/sc/qa/unit/mark_test.cxx
index f8d041c..ad6124c 100644
--- a/sc/qa/unit/mark_test.cxx
+++ b/sc/qa/unit/mark_test.cxx
@@ -243,7 +243,7 @@
    ScMarkData aMark;
    ScMultiSel aMultiSel;
    CPPUNIT_ASSERT( !aMark.IsMarked() && !aMark.IsMultiMarked() );
    CPPUNIT_ASSERT_EQUAL( SCCOL(0), aMultiSel.size() );
    CPPUNIT_ASSERT_EQUAL( SCCOL(0), aMultiSel.GetMultiSelectionCount() );
    CPPUNIT_ASSERT( !aMultiSel.HasAnyMarks() );

    for ( const auto& rAreaTestData : rMarksData.aMarks )
@@ -392,7 +392,7 @@
        CPPUNIT_ASSERT( !aMultiSel.HasEqualRowsMarked( rColsWithUnequalMarks.first, rColsWithUnequalMarks.second ) );

    aMultiSel.Clear();
    CPPUNIT_ASSERT_EQUAL( SCCOL(0), aMultiSel.size() );
    CPPUNIT_ASSERT_EQUAL( SCCOL(0), aMultiSel.GetMultiSelectionCount() );
    CPPUNIT_ASSERT( !aMultiSel.HasAnyMarks() );
}

diff --git a/sc/source/core/data/markarr.cxx b/sc/source/core/data/markarr.cxx
index 727b563..c981c98 100644
--- a/sc/source/core/data/markarr.cxx
+++ b/sc/source/core/data/markarr.cxx
@@ -31,13 +31,15 @@
}

// Move constructor
ScMarkArray::ScMarkArray( ScMarkArray&& rArray ) :
    nCount( rArray.nCount ),
    nLimit( rArray.nLimit ),
    pData( rArray.pData.release() )
ScMarkArray::ScMarkArray( ScMarkArray&& rOther )
{
    rArray.nCount = 0;
    rArray.nLimit = 0;
    operator=(std::move(rOther));
}

// Copy constructor
ScMarkArray::ScMarkArray( const ScMarkArray & rOther )
{
    operator=(rOther);
}

ScMarkArray::~ScMarkArray()
@@ -293,7 +295,7 @@
    return bRet;
}

bool ScMarkArray::HasEqualRowsMarked( const ScMarkArray& rOther ) const
bool ScMarkArray::operator==( const ScMarkArray& rOther ) const
{
    if (nCount != rOther.nCount)
        return false;
@@ -308,17 +310,28 @@
    return true;
}

void ScMarkArray::CopyMarksTo( ScMarkArray& rDestMarkArray ) const
ScMarkArray& ScMarkArray::operator=( const ScMarkArray& rOther )
{
    if (pData)
    if (rOther.pData)
    {
        rDestMarkArray.pData.reset( new ScMarkEntry[nCount] );
        memcpy( rDestMarkArray.pData.get(), pData.get(), nCount * sizeof(ScMarkEntry) );
        pData.reset( new ScMarkEntry[rOther.nCount] );
        memcpy( pData.get(), rOther.pData.get(), rOther.nCount * sizeof(ScMarkEntry) );
    }
    else
        rDestMarkArray.pData.reset();
        pData.reset();

    rDestMarkArray.nCount = rDestMarkArray.nLimit = nCount;
    nCount = nLimit = rOther.nCount;
    return *this;
}

ScMarkArray& ScMarkArray::operator=( ScMarkArray&& rOther )
{
    nCount = rOther.nCount;
    nLimit = rOther.nLimit;
    pData = std::move( rOther.pData );
    rOther.nCount = 0;
    rOther.nLimit = 0;
    return *this;
}

SCROW ScMarkArray::GetNextMarked( SCROW nRow, bool bUp ) const
diff --git a/sc/source/core/data/markmulti.cxx b/sc/source/core/data/markmulti.cxx
index 8dc8ab3..61a77ac 100644
--- a/sc/source/core/data/markmulti.cxx
+++ b/sc/source/core/data/markmulti.cxx
@@ -23,43 +23,24 @@

#include <algorithm>

ScMultiSel::ScMultiSel():
    aMultiSelContainer(),
    aRowSel()
ScMultiSel::ScMultiSel()
{
}

ScMultiSel::ScMultiSel( const ScMultiSel& rMultiSel )
ScMultiSel::ScMultiSel( const ScMultiSel& rOther )
{
    MapType::iterator aDestEnd = aMultiSelContainer.end();
    MapType::iterator aDestIter = aDestEnd;
    for ( const auto& aSourcePair : rMultiSel.aMultiSelContainer )
    {
        // correct hint is always aDestEnd as keys come in ascending order
        // Amortized constant time operation as we always give the correct hint
        aDestIter = aMultiSelContainer.emplace_hint( aDestEnd, aSourcePair.first, ScMarkArray() );
        aSourcePair.second.CopyMarksTo( aDestIter->second );
    }
    rMultiSel.aRowSel.CopyMarksTo( aRowSel );
    aRowSel = rOther.aRowSel;
    aMultiSelContainer = rOther.aMultiSelContainer;
}

ScMultiSel::~ScMultiSel()
{
}

ScMultiSel& ScMultiSel::operator=(const ScMultiSel& rMultiSel)
ScMultiSel& ScMultiSel::operator=(const ScMultiSel& rOther)
{
    Clear();
    MapType::iterator aDestEnd = aMultiSelContainer.end();
    MapType::iterator aDestIter = aDestEnd;
    for ( const auto& aSourcePair : rMultiSel.aMultiSelContainer )
    {
        // correct hint is always aDestEnd as keys come in ascending order
        // Amortized constant time operation as we always give the correct hint
        aDestIter = aMultiSelContainer.emplace_hint( aDestEnd, aSourcePair.first, ScMarkArray() );
        aSourcePair.second.CopyMarksTo( aDestIter->second );
    }
    rMultiSel.aRowSel.CopyMarksTo( aRowSel );
    aRowSel = rOther.aRowSel;
    aMultiSelContainer = rOther.aMultiSelContainer;
    return *this;
}

@@ -69,24 +50,28 @@
    aRowSel.Reset();
}

SCCOL ScMultiSel::GetMultiSelectionCount() const
{
    SCCOL nCount = 0;
    for (const auto & i : aMultiSelContainer)
        if (i.HasMarks())
            ++nCount;
    return nCount;
}

bool ScMultiSel::HasMarks( SCCOL nCol ) const
{
    if ( aRowSel.HasMarks() )
        return true;
    MapType::const_iterator aIter = aMultiSelContainer.find( nCol );
    if ( aIter == aMultiSelContainer.end() )
        return false;
    return aIter->second.HasMarks();
    return nCol < static_cast<SCCOL>(aMultiSelContainer.size()) && aMultiSelContainer[nCol].HasMarks();
}

bool ScMultiSel::HasOneMark( SCCOL nCol, SCROW& rStartRow, SCROW& rEndRow ) const
{
    bool aResult2 = false;
    SCROW nRow1 = -1, nRow2 = -1, nRow3 = -1, nRow4 = -1;
    bool aResult1 = aRowSel.HasOneMark( nRow1, nRow2 );
    MapType::const_iterator aIter = aMultiSelContainer.find( nCol );
    if ( aIter != aMultiSelContainer.end() )
        aResult2 = aIter->second.HasOneMark( nRow3, nRow4 );
    bool aResult2 = nCol < static_cast<SCCOL>(aMultiSelContainer.size())
                    && aMultiSelContainer[nCol].HasOneMark( nRow3, nRow4 );

    if ( aResult1 || aResult2 )
    {
@@ -121,17 +106,13 @@
{
    if ( aRowSel.GetMark( nRow ) )
        return true;
    MapType::const_iterator aIter = aMultiSelContainer.find( nCol );
    if ( aIter != aMultiSelContainer.end() )
        return aIter->second.GetMark( nRow );
    return false;
    return nCol < static_cast<SCCOL>(aMultiSelContainer.size()) && aMultiSelContainer[nCol].GetMark(nRow);
}

bool ScMultiSel::IsAllMarked( SCCOL nCol, SCROW nStartRow, SCROW nEndRow ) const
{
    bool bHasMarks1 = aRowSel.HasMarks();
    MapType::const_iterator aIter = aMultiSelContainer.find( nCol );
    bool bHasMarks2 = ( aIter != aMultiSelContainer.end() && aIter->second.HasMarks() );
    bool bHasMarks2 = nCol < static_cast<SCCOL>(aMultiSelContainer.size()) && aMultiSelContainer[nCol].HasMarks();

    if ( !bHasMarks1 && !bHasMarks2 )
        return false;
@@ -139,7 +120,7 @@
    if ( bHasMarks1 && bHasMarks2 )
    {
        if ( aRowSel.IsAllMarked( nStartRow, nEndRow ) ||
             aIter->second.IsAllMarked( nStartRow, nEndRow ) )
             aMultiSelContainer[nCol].IsAllMarked( nStartRow, nEndRow ) )
            return true;
        ScMultiSelIter aMultiIter( *this, nCol );
        ScFlatBoolRowSegments::RangeData aRowRange;
@@ -150,24 +131,21 @@
    if ( bHasMarks1 )
        return aRowSel.IsAllMarked( nStartRow, nEndRow );

    return aIter->second.IsAllMarked( nStartRow, nEndRow );
    return aMultiSelContainer[nCol].IsAllMarked( nStartRow, nEndRow );
}

bool ScMultiSel::HasEqualRowsMarked( SCCOL nCol1, SCCOL nCol2 ) const
{
    MapType::const_iterator aIter1 = aMultiSelContainer.find( nCol1 );
    MapType::const_iterator aIter2 = aMultiSelContainer.find( nCol2 );
    MapType::const_iterator aEnd = aMultiSelContainer.end();
    bool bCol1Exists = ( aIter1 != aEnd );
    bool bCol2Exists = ( aIter2 != aEnd );
    bool bCol1Exists = nCol1 < static_cast<SCCOL>(aMultiSelContainer.size());
    bool bCol2Exists = nCol2 < static_cast<SCCOL>(aMultiSelContainer.size());
    if ( bCol1Exists || bCol2Exists )
    {
        if ( bCol1Exists && bCol2Exists )
            return aIter1->second.HasEqualRowsMarked( aIter2->second );
            return aMultiSelContainer[nCol1] == aMultiSelContainer[nCol2];
        else if ( bCol1Exists )
            return !aIter1->second.HasMarks();
            return !aMultiSelContainer[nCol1].HasMarks();
        else
            return !aIter2->second.HasMarks();
            return !aMultiSelContainer[nCol2].HasMarks();
    }

    return true;
@@ -175,13 +153,12 @@

SCROW ScMultiSel::GetNextMarked( SCCOL nCol, SCROW nRow, bool bUp ) const
{
    MapType::const_iterator aIter = aMultiSelContainer.find( nCol );
    if ( aIter == aMultiSelContainer.end() )
    if ( nCol >= static_cast<SCCOL>(aMultiSelContainer.size()) || !aMultiSelContainer[nCol].HasMarks() )
        return aRowSel.GetNextMarked( nRow, bUp );

    SCROW nRow1, nRow2;
    nRow1 = aRowSel.GetNextMarked( nRow, bUp );
    nRow2 = aIter->second.GetNextMarked( nRow, bUp );
    nRow2 = aMultiSelContainer[nCol].GetNextMarked( nRow, bUp );
    if ( nRow1 == nRow2 )
        return nRow1;
    if ( nRow1 == -1 )
@@ -195,11 +172,10 @@

void ScMultiSel::MarkAllCols( SCROW nStartRow, SCROW nEndRow )
{
    MapType::iterator aIter = aMultiSelContainer.end();
    aMultiSelContainer.resize(MAXCOL+1);
    for ( SCCOL nCol = MAXCOL; nCol >= 0; --nCol )
    {
        aIter = aMultiSelContainer.emplace_hint( aIter, nCol, ScMarkArray() );
        aIter->second.SetMarkArea( nStartRow, nEndRow, true );
        aMultiSelContainer[nCol].SetMarkArea( nStartRow, nEndRow, true );
    }
}

@@ -212,8 +188,8 @@
        {
            // Remove any per column marks for the row range.
            for ( auto& aIter : aMultiSelContainer )
                if ( aIter.second.HasMarks() )
                    aIter.second.SetMarkArea( nStartRow, nEndRow, false );
                if ( aIter.HasMarks() )
                    aIter.SetMarkArea( nStartRow, nEndRow, false );
        }
        return;
    }
@@ -253,14 +229,10 @@
        aRowSel.SetMarkArea( nStartRow, nEndRow, false );
    }

    MapType::iterator aIter = aMultiSelContainer.end();
    if (nEndCol >= static_cast<SCCOL>(aMultiSelContainer.size()))
        aMultiSelContainer.resize(nEndCol+1);
    for ( SCCOL nColIter = nEndCol; nColIter >= nStartCol; --nColIter )
    {
        // First hint is usually off, so the first emplace operation will take up to
        // logarithmic in map size, all other iterations will take only constant time.
        aIter = aMultiSelContainer.emplace_hint( aIter, nColIter, ScMarkArray() );
        aIter->second.SetMarkArea( nStartRow, nEndRow, bMark );
    }
        aMultiSelContainer[nColIter].SetMarkArea( nStartRow, nEndRow, bMark );
}

bool ScMultiSel::IsRowMarked( SCROW nRow ) const
@@ -291,7 +263,7 @@
    if ( aRowSel.HasMarks() )
        return true;
    for ( const auto& aPair : aMultiSelContainer )
        if ( aPair.second.HasMarks() )
        if ( aPair.HasMarks() )
            return true;
    return false;
}
@@ -307,66 +279,50 @@
    if (nColOffset < 0)
    {
        // columns that would be moved on the left of nStartCol must be removed
        const SCCOL nEndPos = nStartCol - nColOffset;
        const SCCOL nEndPos = std::min<SCCOL>(aNewMultiSel.aMultiSelContainer.size(), nStartCol - nColOffset);
        for (SCCOL nSearchPos = nStartCol; nSearchPos < nEndPos; ++nSearchPos)
        {
            const auto& aColIt = aNewMultiSel.aMultiSelContainer.find(nSearchPos);
            if (aColIt != aNewMultiSel.aMultiSelContainer.end())
            {
                aNewMultiSel.aMultiSelContainer.erase(aColIt);
            }
        }
            aNewMultiSel.aMultiSelContainer[nSearchPos].Reset();
    }

    MapType::iterator aDestEnd = aMultiSelContainer.end();
    MapType::iterator aDestIter = aDestEnd;
    for (const auto& aSourcePair : aNewMultiSel.aMultiSelContainer)
    SCCOL nCol = 0;
    for (const auto& aSourceArray : aNewMultiSel.aMultiSelContainer)
    {
        SCCOL nCol = aSourcePair.first;
        if (aSourcePair.first >= nStartCol)
        SCCOL nDestCol = nCol;
        if (nDestCol >= nStartCol)
        {
            nCol += nColOffset;
            if (nCol < 0)
                nCol = 0;
            else if (nCol > MAXCOL)
                nCol = MAXCOL;
            nDestCol += nColOffset;
            if (nDestCol < 0)
                nDestCol = 0;
            else if (nDestCol > MAXCOL)
                nDestCol = MAXCOL;
        }
        // correct hint is always aDestEnd as keys come in ascending order
        // Amortized constant time operation as we always give the correct hint
        aDestIter = aMultiSelContainer.emplace_hint( aDestEnd, nCol, ScMarkArray() );
        aSourcePair.second.CopyMarksTo(aDestIter->second);
        if (nDestCol >= static_cast<SCCOL>(aMultiSelContainer.size()))
            aMultiSelContainer.resize(nDestCol);
        aMultiSelContainer[nDestCol] = aSourceArray;
        ++nCol;
    }
    aNewMultiSel.aRowSel.CopyMarksTo(aRowSel);
    aRowSel = aNewMultiSel.aRowSel;

    if (nColOffset > 0 && nStartCol > 0)
    if (nColOffset > 0 && nStartCol > 0 && nStartCol < static_cast<SCCOL>(aNewMultiSel.aMultiSelContainer.size()))
    {
        // insert nColOffset new columns, and select their cells if they are selected
        // both in the old column at nStartPos and in the previous column
        const auto& aPrevPosIt = aNewMultiSel.aMultiSelContainer.find(nStartCol - 1);
        if (aPrevPosIt != aNewMultiSel.aMultiSelContainer.end())
        {
            const auto& aStartPosIt = aNewMultiSel.aMultiSelContainer.find(nStartCol);
            if (aStartPosIt != aNewMultiSel.aMultiSelContainer.end())
            {
                MapType::iterator aNewColIt = aMultiSelContainer.emplace_hint(aDestEnd, nStartCol, ScMarkArray());
                aStartPosIt->second.CopyMarksTo(aNewColIt->second);
                aNewColIt->second.Intersect(aPrevPosIt->second);
                for (long i = 1; i < nColOffset; ++i)
                {
                    aDestIter = aMultiSelContainer.emplace_hint(aDestEnd, nStartCol + i, ScMarkArray());
                    aNewColIt->second.CopyMarksTo(aDestIter->second);
                }
            }
        }
        auto& rPrevPos = aNewMultiSel.aMultiSelContainer[nStartCol - 1];
        auto& rStartPos = aNewMultiSel.aMultiSelContainer[nStartCol];
        auto& rNewCol = aMultiSelContainer[nStartCol];
        rNewCol = rStartPos;
        rNewCol.Intersect(rPrevPos);
        if (nStartCol + nColOffset >= static_cast<SCCOL>(aNewMultiSel.aMultiSelContainer.size()))
            aNewMultiSel.aMultiSelContainer.resize(nStartCol + nColOffset);
        for (long i = 1; i < nColOffset; ++i)
            aMultiSelContainer[nStartCol + i] = rNewCol;
    }
}

void ScMultiSel::ShiftRows(SCROW nStartRow, long nRowOffset)
{
    for (auto& aPair: aMultiSelContainer)
    {
        aPair.second.Shift(nStartRow, nRowOffset);
    }
        aPair.Shift(nStartRow, nRowOffset);
    aRowSel.Shift(nStartRow, nRowOffset);
}

@@ -377,8 +333,9 @@

const ScMarkArray* ScMultiSel::GetMultiSelArray( SCCOL nCol ) const
{
    ScMultiSel::MapType::const_iterator aIter = aMultiSelContainer.find( nCol );
    return (aIter != aMultiSelContainer.end()) ? &aIter->second : nullptr;
    if (nCol >= static_cast<SCCOL>(aMultiSelContainer.size()))
        return nullptr;
    return &aMultiSelContainer[nCol];
}

ScMultiSelIter::ScMultiSelIter( const ScMultiSel& rMultiSel, SCCOL nCol ) :
@@ -386,8 +343,8 @@
    nNextSegmentStart(0)
{
    bool bHasMarks1 = rMultiSel.aRowSel.HasMarks();
    ScMultiSel::MapType::const_iterator aIter = rMultiSel.aMultiSelContainer.find( nCol );
    bool bHasMarks2 = ( ( aIter != rMultiSel.aMultiSelContainer.end() ) && aIter->second.HasMarks() );
    bool bHasMarks2 = nCol < static_cast<SCCOL>(rMultiSel.aMultiSelContainer.size())
                    && rMultiSel.aMultiSelContainer[nCol].HasMarks();

    if (bHasMarks1 && bHasMarks2)
    {
@@ -401,7 +358,7 @@
        }

        {
            ScMarkArrayIter aMarkIter( &aIter->second );
            ScMarkArrayIter aMarkIter( &rMultiSel.aMultiSelContainer[nCol] );
            SCROW nTop, nBottom;
            while ( aMarkIter.Next( nTop, nBottom ) )
                pRowSegs->setTrue( nTop, nBottom );
@@ -413,7 +370,7 @@
    }
    else if (bHasMarks2)
    {
        aMarkArrayIter.reset( &aIter->second);
        aMarkArrayIter.reset( &rMultiSel.aMultiSelContainer[nCol]);
    }
}