tdf#126021 speed up saving xls with lots of styles

extend the find-map so we can do partial-match searching.
Makes it 10x faster for me.

Change-Id: I1952a221a919707af078fac9fd1eb63781d9188d
Reviewed-on: https://gerrit.libreoffice.org/81488
Tested-by: Jenkins
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
diff --git a/sc/source/filter/excel/xestyle.cxx b/sc/source/filter/excel/xestyle.cxx
index 7f3ac9c..7b6a3b5a 100644
--- a/sc/source/filter/excel/xestyle.cxx
+++ b/sc/source/filter/excel/xestyle.cxx
@@ -2666,24 +2666,58 @@ void XclExpXFBuffer::SaveXFXml( XclExpXmlStream& rStrm, XclExpXF& rXF )
sal_uInt32 XclExpXFBuffer::FindXF( const ScPatternAttr& rPattern,
        sal_uInt32 nForceScNumFmt, sal_uInt16 nForceXclFont, bool bForceLineBreak ) const
{
    auto it = maXFFindMap.find(&rPattern.GetItemSet());
    if (it == maXFFindMap.end())
        return EXC_XFID_NOTFOUND;
    for (auto const & nPos : it->second)
        if( maXFList.GetRecord( nPos )->Equals( rPattern, nForceScNumFmt, nForceXclFont, bForceLineBreak ) )
            return nPos;
    if (nForceScNumFmt != NUMBERFORMAT_ENTRY_NOT_FOUND && nForceXclFont == EXC_FONT_NOTFOUND)
    {
        FindKey key1 { /*mbCellXF*/true, &rPattern.GetItemSet(), nForceScNumFmt, 0 };
        FindKey key2 { /*mbCellXF*/true, &rPattern.GetItemSet(), nForceScNumFmt, EXC_FONT_NOTFOUND };
        auto it1 = maXFFindMap.lower_bound(key1);
        if (it1 != maXFFindMap.end())
        {
            auto it2 = maXFFindMap.upper_bound(key2);
            for (auto it = it1; it != it2; ++it)
                for (auto const & nPos : it->second)
                    if( maXFList.GetRecord( nPos )->Equals( rPattern, nForceScNumFmt, nForceXclFont, bForceLineBreak ) )
                        return nPos;
        }
    }
    else if (nForceScNumFmt == NUMBERFORMAT_ENTRY_NOT_FOUND || nForceXclFont == EXC_FONT_NOTFOUND)
    {
        FindKey key1 { /*mbCellXF*/true, &rPattern.GetItemSet(), 0, 0 };
        FindKey key2 { /*mbCellXF*/true, &rPattern.GetItemSet(), NUMBERFORMAT_ENTRY_NOT_FOUND, EXC_FONT_NOTFOUND };
        auto it1 = maXFFindMap.lower_bound(key1);
        if (it1 != maXFFindMap.end())
        {
            auto it2 = maXFFindMap.upper_bound(key2);
            for (auto it = it1; it != it2; ++it)
                for (auto const & nPos : it->second)
                    if( maXFList.GetRecord( nPos )->Equals( rPattern, nForceScNumFmt, nForceXclFont, bForceLineBreak ) )
                        return nPos;
        }
    }
    else
    {
        FindKey key { /*mbCellXF*/true, &rPattern.GetItemSet(), nForceScNumFmt, nForceXclFont };
        auto it = maXFFindMap.find(key);
        if (it == maXFFindMap.end())
            return EXC_XFID_NOTFOUND;
        for (auto const & nPos : it->second)
            if( maXFList.GetRecord( nPos )->Equals( rPattern, nForceScNumFmt, nForceXclFont, bForceLineBreak ) )
                return nPos;
    }
    return EXC_XFID_NOTFOUND;
}

sal_uInt32 XclExpXFBuffer::FindXF( const SfxStyleSheetBase& rStyleSheet ) const
{
    const SfxItemSet* pItemSet = &const_cast< SfxStyleSheetBase& >( rStyleSheet ).GetItemSet();
    auto it = maXFFindMap.find(pItemSet);
    if (it == maXFFindMap.end())
        return EXC_XFID_NOTFOUND;
    for (auto const & nPos : it->second)
        if( maXFList.GetRecord( nPos )->Equals( rStyleSheet ) )
            return nPos;
    FindKey key1 { /*mbCellXF*/false, pItemSet, 0, 0 };
    FindKey key2 { /*mbCellXF*/false, pItemSet, NUMBERFORMAT_ENTRY_NOT_FOUND, EXC_FONT_NOTFOUND };
    auto it1 = maXFFindMap.lower_bound(key1);
    auto it2 = maXFFindMap.upper_bound(key2);
    for (auto it = it1; it != it2; ++it)
        for (auto const & nPos : it->second)
            if( maXFList.GetRecord( nPos )->Equals( rStyleSheet ) )
                return nPos;
    return EXC_XFID_NOTFOUND;
}

@@ -2698,6 +2732,11 @@ sal_uInt32 XclExpXFBuffer::FindBuiltInXF( sal_uInt8 nStyleId, sal_uInt8 nLevel )
    return EXC_XFID_NOTFOUND;
}

XclExpXFBuffer::FindKey XclExpXFBuffer::ToFindKey(XclExpXF const & rRec)
{
    return { rRec.IsCellXF(), rRec.GetItemSet(), rRec.GetScNumFmt(), rRec.GetXclFont() };
}

sal_uInt32 XclExpXFBuffer::InsertCellXF( const ScPatternAttr* pPattern, sal_Int16 nScript,
        sal_uInt32 nForceScNumFmt, sal_uInt16 nForceXclFont, bool bForceLineBreak )
{
@@ -2715,14 +2754,14 @@ sal_uInt32 XclExpXFBuffer::InsertCellXF( const ScPatternAttr* pPattern, sal_Int1
        if( rbPredefined )
        {
            // remove old entry in find-map
            auto & rPositions = maXFFindMap[maXFList.GetRecord(EXC_XF_DEFAULTCELL)->GetItemSet()];
            auto & rPositions = maXFFindMap[ToFindKey(*maXFList.GetRecord(EXC_XF_DEFAULTCELL))];
            auto it = std::find(rPositions.begin(), rPositions.end(), EXC_XF_DEFAULTCELL);
            rPositions.erase(it);
            // replace default cell pattern
            XclExpXFRef xNewXF( new XclExpXF( GetRoot(), *pPattern, nScript ) );
            maXFList.ReplaceRecord( xNewXF, EXC_XF_DEFAULTCELL );
            // and add new entry in find-map
            maXFFindMap[xNewXF->GetItemSet()].push_back(EXC_XF_DEFAULTCELL);
            maXFFindMap[ToFindKey(*xNewXF)].push_back(EXC_XF_DEFAULTCELL);
            rbPredefined = false;
        }
        return GetDefCellXFId();
@@ -2739,7 +2778,7 @@ sal_uInt32 XclExpXFBuffer::InsertCellXF( const ScPatternAttr* pPattern, sal_Int1
            maXFList.AppendNewRecord( pNewExp );
            // do not set nXFId before the AppendNewRecord() call - it may insert 2 XFs (style+cell)
            nXFId = static_cast< sal_uInt32 >( maXFList.GetSize() - 1 );
            maXFFindMap[pNewExp->GetItemSet()].push_back(nXFId);
            maXFFindMap[ToFindKey(*pNewExp)].push_back(nXFId);
        }
        else
        {
@@ -2775,14 +2814,14 @@ sal_uInt32 XclExpXFBuffer::InsertStyleXF( const SfxStyleSheetBase& rStyleSheet )
            if( rbPredefined )
            {
                // remove old entry in find-map
                auto & rPositions = maXFFindMap[maXFList.GetRecord(nXFId)->GetItemSet()];
                auto & rPositions = maXFFindMap[ToFindKey(*maXFList.GetRecord(nXFId))];
                auto it = std::find(rPositions.begin(), rPositions.end(), nXFId);
                rPositions.erase(it);
                // replace predefined built-in style (ReplaceRecord() deletes old record)
                auto pNewExp = std::make_shared<XclExpXF>( GetRoot(), rStyleSheet );
                maXFList.ReplaceRecord( pNewExp, nXFId );
                // and add new entry in find-map
                maXFFindMap[pNewExp->GetItemSet()].push_back(nXFId);
                maXFFindMap[ToFindKey(*pNewExp)].push_back(nXFId);
                rbPredefined = false;
            }
        }
@@ -2812,7 +2851,7 @@ sal_uInt32 XclExpXFBuffer::InsertStyleXF( const SfxStyleSheetBase& rStyleSheet )
            // create the STYLE record
            if( !rStyleSheet.GetName().isEmpty() )
                maStyleList.AppendNewRecord( new XclExpStyle( nXFId, rStyleSheet.GetName() ) );
            maXFFindMap[pNewExp->GetItemSet()].push_back(nXFId);
            maXFFindMap[ToFindKey(*pNewExp)].push_back(nXFId);
        }
        else
            // list full - fall back to default style XF
@@ -2833,7 +2872,7 @@ sal_uInt32 XclExpXFBuffer::AppendBuiltInXF( XclExpXFRef const & xXF, sal_uInt8 n
{
    sal_uInt32 nXFId = static_cast< sal_uInt32 >( maXFList.GetSize() );
    maXFList.AppendRecord( xXF );
    maXFFindMap[xXF->GetItemSet()].push_back(nXFId);
    maXFFindMap[ToFindKey(*xXF)].push_back(nXFId);
    XclExpBuiltInInfo& rInfo = maBuiltInMap[ nXFId ];
    rInfo.mnStyleId = nStyleId;
    rInfo.mnLevel = nLevel;
@@ -2906,7 +2945,7 @@ void XclExpXFBuffer::InsertDefaultRecords()

    // index 15: default hard cell format, placeholder to be able to add more built-in styles
    maXFList.AppendNewRecord( new XclExpDefaultXF( GetRoot(), true ) );
    maXFFindMap[maXFList.GetRecord(maXFList.GetSize()-1)->GetItemSet()].push_back(maXFList.GetSize()-1);
    maXFFindMap[ToFindKey(*maXFList.GetRecord(maXFList.GetSize()-1))].push_back(maXFList.GetSize()-1);
    maBuiltInMap[ EXC_XF_DEFAULTCELL ].mbPredefined = true;

    // index 16-20: other built-in styles
diff --git a/sc/source/filter/inc/xestyle.hxx b/sc/source/filter/inc/xestyle.hxx
index 48b1e53..ae96053 100644
--- a/sc/source/filter/inc/xestyle.hxx
+++ b/sc/source/filter/inc/xestyle.hxx
@@ -30,6 +30,7 @@
#include <fonthelper.hxx>
#include <memory>
#include <vector>
#include <o3tl/sorted_vector.hxx>

/* ============================================================================
- Buffers for style records (PALETTE, FONT, FORMAT, XF, STYLE).
@@ -470,6 +471,9 @@ public:

    const SfxItemSet*   GetItemSet() const { return mpItemSet; }

    sal_uInt32          GetScNumFmt() const { return mnScNumFmt; }
    sal_uInt16          GetXclFont() const { return mnXclFont; }

protected:
    explicit            XclExpXF( const XclExpRoot& rRoot, bool bCellXF );

@@ -681,8 +685,29 @@ private:
    typedef ::std::vector< XclExpCellBorder >           XclExpBorderList;
    typedef ::std::vector< XclExpCellArea >             XclExpFillList;

    /** composite key for the find-map, so we can do partial key searching */
    struct FindKey
    {
        bool mbCellXF; // is this a hard cell format, or a cell style
        const SfxItemSet* mpItemSet;
        sal_uInt32 mnScNumFmt;
        sal_uInt16 mnXclFont;

        bool operator<(const FindKey& other) const
        {
            if (mbCellXF != other.mbCellXF)
                return mbCellXF < other.mbCellXF;
            if (mpItemSet != other.mpItemSet)
                return mpItemSet < other.mpItemSet;
            if (mnScNumFmt != other.mnScNumFmt)
                return mnScNumFmt < other.mnScNumFmt;
            return mnXclFont < other.mnXclFont;
        }
    };
    static FindKey ToFindKey(XclExpXF const &);

    XclExpXFList        maXFList;           /// List of all XF records.
    std::unordered_map<const SfxItemSet*, std::vector<sal_uInt32>>
    std::map<FindKey, std::vector<sal_uInt32>>
                        maXFFindMap;        /// map of itemset to vector of positions, to speed up find
    XclExpStyleList     maStyleList;        /// List of all STYLE records.
    XclExpBuiltInMap    maBuiltInMap;       /// Contained elements describe built-in XFs.