tdf#148616 Speed up finding custom shape guide value

Use a lookup hash map instead of linear search

Change-Id: I54c9509740d90ca3f7479dfc16a6aeffd82a405d
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/166879
Tested-by: Jenkins
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
Reviewed-by: Aron Budea <aron.budea@collabora.com>
diff --git a/oox/inc/drawingml/customshapeproperties.hxx b/oox/inc/drawingml/customshapeproperties.hxx
index 4b9ee40..1c627ec 100644
--- a/oox/inc/drawingml/customshapeproperties.hxx
+++ b/oox/inc/drawingml/customshapeproperties.hxx
@@ -42,6 +42,28 @@ struct CustomShapeGuide
    OUString   maFormula;
};

class CustomShapeGuideContainer
{
public:
    sal_Int32 GetCustomShapeGuideValue( const OUString& rFormulaName ) const;
    sal_Int32 SetCustomShapeGuideValue( const CustomShapeGuide& rGuide );

    void push_back( const CustomShapeGuide& rGuide );
    size_t size() const { return maGuideList.size(); };
    bool empty() const { return maGuideList.empty(); };
    std::vector< CustomShapeGuide >::const_iterator begin() const { return maGuideList.begin(); };
    std::vector< CustomShapeGuide >::const_iterator end() const { return maGuideList.end(); };
    const CustomShapeGuide& operator[](size_t nIndex) const { return maGuideList[ nIndex ]; };

private:
    std::vector< CustomShapeGuide > maGuideList;
    mutable std::unordered_map< OUString, sal_Int32 > maGuideListLookupMap;
    mutable bool mbLookupMapStale = false;
    mutable sal_Int32 mnPreviousActSize = 0;

    void ActualizeLookupMap() const;
};

struct AdjustHandle
{
    bool                                    polar;
@@ -106,8 +128,8 @@ public:
    bool                                getShapeTypeOverride() const { return mbShapeTypeOverride; };
    void                                setShapeTypeOverride( bool bShapeTypeOverride ) { mbShapeTypeOverride = bShapeTypeOverride; };

    std::vector< CustomShapeGuide >&    getAdjustmentGuideList(){ return maAdjustmentGuideList; };
    std::vector< CustomShapeGuide >&    getGuideList(){ return maGuideList; };
    CustomShapeGuideContainer&          getAdjustmentGuideList(){ return maAdjustmentGuideList; };
    CustomShapeGuideContainer&          getGuideList(){ return maGuideList; };
    std::vector< AdjustHandle >&        getAdjustHandleList(){ return maAdjustHandleList; };
    std::vector< ConnectionSite >&      getConnectionSiteList(){ return maConnectionSiteList; };
    std::optional< GeomRect >&          getTextRect(){ return maTextRect; };
@@ -119,9 +141,6 @@ public:
    void                                setTextCameraZRotateAngle( sal_Int32 nAngle ) { mnTextCameraZRotateAngle = nAngle; };
    void                                setTextAreaRotateAngle(sal_Int32 nAngle) { moTextAreaRotateAngle = nAngle; };

    static sal_Int32 SetCustomShapeGuideValue( std::vector< CustomShapeGuide >& rGuideList, const CustomShapeGuide& rGuide );
    static sal_Int32 GetCustomShapeGuideValue( const std::vector< CustomShapeGuide >& rGuideList, std::u16string_view rFormulaName );

    sal_Int32 getArcNum() { return mnArcNum++; }
    sal_Int32 countArcTo() { return mnArcNum; }
    PropertyMap& getExtrusionPropertyMap() { return maExtrusionPropertyMap; }
@@ -136,8 +155,8 @@ private:

    sal_Int32                       mnShapePresetType;
    bool                            mbShapeTypeOverride;
    std::vector< CustomShapeGuide > maAdjustmentGuideList;
    std::vector< CustomShapeGuide > maGuideList;
    CustomShapeGuideContainer       maAdjustmentGuideList;
    CustomShapeGuideContainer       maGuideList;
    std::vector< AdjustHandle >     maAdjustHandleList;
    std::vector< ConnectionSite >   maConnectionSiteList;
    std::optional< GeomRect >       maTextRect;
diff --git a/oox/source/drawingml/customshapegeometry.cxx b/oox/source/drawingml/customshapegeometry.cxx
index 01b86b4..b029957 100644
--- a/oox/source/drawingml/customshapegeometry.cxx
+++ b/oox/source/drawingml/customshapegeometry.cxx
@@ -224,7 +224,7 @@ static EnhancedCustomShapeParameter GetAdjCoordinate( CustomShapeProperties& rCu
                    aGuide.maName = rValue;
                    aGuide.maFormula = "logheight" ;

                    aRet.Value <<= CustomShapeProperties::SetCustomShapeGuideValue( rCustomShapeProperties.getGuideList(), aGuide );
                    aRet.Value <<= rCustomShapeProperties.getGuideList().SetCustomShapeGuideValue( aGuide );
                    aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
                }
                else
@@ -259,7 +259,7 @@ static EnhancedCustomShapeParameter GetAdjCoordinate( CustomShapeProperties& rCu
                aGuide.maName = rValue;
                aGuide.maFormula = "logheight/" + OUString::number( nIntVal );

                aRet.Value <<= CustomShapeProperties::SetCustomShapeGuideValue( rCustomShapeProperties.getGuideList(), aGuide );
                aRet.Value <<= rCustomShapeProperties.getGuideList().SetCustomShapeGuideValue( aGuide );
                aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
            }
            break;
@@ -278,7 +278,7 @@ static EnhancedCustomShapeParameter GetAdjCoordinate( CustomShapeProperties& rCu
                aGuide.maName = rValue;
                aGuide.maFormula = "max(logwidth,logheight)";

                aRet.Value <<= CustomShapeProperties::SetCustomShapeGuideValue( rCustomShapeProperties.getGuideList(), aGuide );
                aRet.Value <<= rCustomShapeProperties.getGuideList().SetCustomShapeGuideValue( aGuide );
                aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
            }
            break;
@@ -288,7 +288,7 @@ static EnhancedCustomShapeParameter GetAdjCoordinate( CustomShapeProperties& rCu
                aGuide.maName = rValue;
                aGuide.maFormula = "min(logwidth,logheight)";

                aRet.Value <<= CustomShapeProperties::SetCustomShapeGuideValue( rCustomShapeProperties.getGuideList(), aGuide );
                aRet.Value <<= rCustomShapeProperties.getGuideList().SetCustomShapeGuideValue( aGuide );
                aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
            }
            break;
@@ -315,7 +315,7 @@ static EnhancedCustomShapeParameter GetAdjCoordinate( CustomShapeProperties& rCu
                aGuide.maName = rValue;
                aGuide.maFormula = "min(logwidth,logheight)/" + OUString::number( nIntVal );

                aRet.Value <<= CustomShapeProperties::SetCustomShapeGuideValue( rCustomShapeProperties.getGuideList(), aGuide );
                aRet.Value <<= rCustomShapeProperties.getGuideList().SetCustomShapeGuideValue( aGuide );
                aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
            }
            break;
@@ -329,7 +329,7 @@ static EnhancedCustomShapeParameter GetAdjCoordinate( CustomShapeProperties& rCu
                    aGuide.maName = rValue;
                    aGuide.maFormula = "logwidth" ;

                    aRet.Value <<= CustomShapeProperties::SetCustomShapeGuideValue( rCustomShapeProperties.getGuideList(), aGuide );
                    aRet.Value <<= rCustomShapeProperties.getGuideList().SetCustomShapeGuideValue( aGuide );
                    aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
                }
                else
@@ -370,7 +370,7 @@ static EnhancedCustomShapeParameter GetAdjCoordinate( CustomShapeProperties& rCu
                aGuide.maName = rValue;
                aGuide.maFormula = "logwidth/" + OUString::number( nIntVal );

                aRet.Value <<= CustomShapeProperties::SetCustomShapeGuideValue( rCustomShapeProperties.getGuideList(), aGuide );
                aRet.Value <<= rCustomShapeProperties.getGuideList().SetCustomShapeGuideValue( aGuide );;
                aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
            }
            break;
@@ -401,7 +401,7 @@ static EnhancedCustomShapeParameter GetAdjCoordinate( CustomShapeProperties& rCu
            }
            else
            {
                sal_Int32 nGuideIndex = CustomShapeProperties::GetCustomShapeGuideValue( rCustomShapeProperties.getAdjustmentGuideList(), rValue );
                sal_Int32 nGuideIndex = rCustomShapeProperties.getAdjustmentGuideList().GetCustomShapeGuideValue( rValue );
                if ( nGuideIndex >= 0 )
                {
                    aRet.Value <<= nGuideIndex;
@@ -409,7 +409,7 @@ static EnhancedCustomShapeParameter GetAdjCoordinate( CustomShapeProperties& rCu
                }
                else
                {
                    nGuideIndex = CustomShapeProperties::GetCustomShapeGuideValue( rCustomShapeProperties.getGuideList(), rValue );
                    nGuideIndex = rCustomShapeProperties.getGuideList().GetCustomShapeGuideValue( rValue );
                    if ( nGuideIndex >= 0 )
                    {
                        aRet.Value <<= nGuideIndex;
@@ -433,17 +433,17 @@ namespace {
class GeomGuideListContext : public ContextHandler2
{
public:
    GeomGuideListContext( ContextHandler2Helper const & rParent, CustomShapeProperties& rCustomShapeProperties, std::vector< CustomShapeGuide >& rGuideList );
    GeomGuideListContext( ContextHandler2Helper const & rParent, CustomShapeProperties& rCustomShapeProperties, CustomShapeGuideContainer& rGuideList );
    virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 aElementToken, const ::oox::AttributeList& rAttribs ) override;

protected:
    std::vector< CustomShapeGuide >&    mrGuideList;
    CustomShapeProperties&              mrCustomShapeProperties;
    CustomShapeGuideContainer&    mrGuideList;
    CustomShapeProperties&        mrCustomShapeProperties;
};

}

GeomGuideListContext::GeomGuideListContext( ContextHandler2Helper const & rParent, CustomShapeProperties& rCustomShapeProperties, std::vector< CustomShapeGuide >& rGuideList )
GeomGuideListContext::GeomGuideListContext( ContextHandler2Helper const & rParent, CustomShapeProperties& rCustomShapeProperties, CustomShapeGuideContainer& rGuideList )
: ContextHandler2( rParent )
, mrGuideList( rGuideList )
, mrCustomShapeProperties( rCustomShapeProperties )
@@ -1110,7 +1110,7 @@ ContextHandlerRef Path2DContext::onCreateContext( sal_Int32 aElementToken,
            aGuide.maFormula = "("
                + GetFormulaParameter( GetAdjCoordinate( mrCustomShapeProperties, rAttribs.getStringDefaulted( XML_stAng ) ) )
                + ")/60000.0";
            aAngles.First.Value <<= CustomShapeProperties::SetCustomShapeGuideValue( mrCustomShapeProperties.getGuideList(), aGuide );
            aAngles.First.Value <<= mrCustomShapeProperties.getGuideList().SetCustomShapeGuideValue( aGuide );
            aAngles.First.Type = EnhancedCustomShapeParameterType::EQUATION;

            // swing angle
@@ -1118,7 +1118,7 @@ ContextHandlerRef Path2DContext::onCreateContext( sal_Int32 aElementToken,
            aGuide.maFormula = "("
                + GetFormulaParameter( GetAdjCoordinate( mrCustomShapeProperties, rAttribs.getStringDefaulted( XML_swAng ) ) )
                + ")/60000.0";
            aAngles.Second.Value <<= CustomShapeProperties::SetCustomShapeGuideValue( mrCustomShapeProperties.getGuideList(), aGuide );
            aAngles.Second.Value <<= mrCustomShapeProperties.getGuideList().SetCustomShapeGuideValue( aGuide );
            aAngles.Second.Type = EnhancedCustomShapeParameterType::EQUATION;

            mrPath2D.parameter.push_back( aScale );
diff --git a/oox/source/drawingml/customshapeproperties.cxx b/oox/source/drawingml/customshapeproperties.cxx
index a3e2dd5..f5dfae2 100644
--- a/oox/source/drawingml/customshapeproperties.cxx
+++ b/oox/source/drawingml/customshapeproperties.cxx
@@ -43,6 +43,67 @@ using namespace ::com::sun::star::drawing;

namespace oox::drawingml {

void CustomShapeGuideContainer::ActualizeLookupMap() const
{
    if ( mbLookupMapStale )
    {
        // maGuideListLookupMap maps guide name to index in maGuideList
        // guides were added since last actualization, need to update map based on those
        // guide names can be reused, and current is the latest one
        // (see a1 guide in gear6 custom shape preset as example):
        //  go backwards and update if index is higher than previously
        for ( sal_Int32 nIndex = static_cast<sal_Int32>( maGuideList.size() ) - 1; nIndex >= mnPreviousActSize; --nIndex )
        {
            const auto it = maGuideListLookupMap.find( maGuideList[ nIndex ].maName );
            if ( it != maGuideListLookupMap.end() )
            {
                if ( nIndex > it->second )
                    it->second = nIndex;
            }
            else
                maGuideListLookupMap[ maGuideList[ nIndex ].maName ] = nIndex;
        }
        mbLookupMapStale = false;
        mnPreviousActSize = static_cast<sal_Int32>( maGuideList.size() );
    }
}

void CustomShapeGuideContainer::push_back( const CustomShapeGuide& rGuide )
{
    if ( !mbLookupMapStale )
    {
        mbLookupMapStale = true;
        mnPreviousActSize = static_cast<sal_Int32>( maGuideList.size() );
    }
    maGuideList.push_back( rGuide );
}

// returns the index into the guidelist for a given formula name,
// if the return value is < 0 then the guide value could not be found
sal_Int32 CustomShapeGuideContainer::GetCustomShapeGuideValue( const OUString &rFormulaName ) const
{
    ActualizeLookupMap();
    const auto it = maGuideListLookupMap.find( rFormulaName );
    if ( it != maGuideListLookupMap.end() )
        return it->second;

    return -1;
}

sal_Int32 CustomShapeGuideContainer::SetCustomShapeGuideValue( const CustomShapeGuide& rGuide )
{
    ActualizeLookupMap();
    // change from previous SetCustomShapeGuideValue behavior: searching using cache traverses backwards
    const auto it = maGuideListLookupMap.find( rGuide.maName );
    if ( it != maGuideListLookupMap.end() )
        return it->second;

    maGuideList.push_back( rGuide );
    maGuideListLookupMap[ rGuide.maName ] = mnPreviousActSize;
    mnPreviousActSize++;
    return mnPreviousActSize - 1;
}

CustomShapeProperties::CustomShapeProperties()
: mnShapePresetType ( -1 )
, mbShapeTypeOverride(false)
@@ -59,36 +120,6 @@ OUString CustomShapeProperties::getShapePresetTypeName() const
    return StaticTokenMap().getUnicodeTokenName(mnShapePresetType);
}

sal_Int32 CustomShapeProperties::SetCustomShapeGuideValue( std::vector< CustomShapeGuide >& rGuideList, const CustomShapeGuide& rGuide )
{
    std::vector<CustomShapeGuide>::size_type nIndex = 0;
    for( ; nIndex < rGuideList.size(); nIndex++ )
    {
        if ( rGuideList[ nIndex ].maName == rGuide.maName )
            break;
    }
    if ( nIndex == rGuideList.size() )
        rGuideList.push_back( rGuide );
    return static_cast< sal_Int32 >( nIndex );
}

// returns the index into the guidelist for a given formula name,
// if the return value is < 0 then the guide value could not be found
sal_Int32 CustomShapeProperties::GetCustomShapeGuideValue( const std::vector< CustomShapeGuide >& rGuideList, std::u16string_view rFormulaName )
{
    // traverse the list from the end, because guide names can be reused
    // and current is the last one
    // see a1 guide in gear6 custom shape preset as example
    sal_Int32 nIndex = static_cast< sal_Int32 >( rGuideList.size() ) - 1;
    for( ; nIndex >= 0; nIndex-- )
    {
        if ( rGuideList[ nIndex ].maName == rFormulaName )
            break;
    }

    return nIndex;
}

bool CustomShapeProperties::representsDefaultShape() const
{
    return !((getShapePresetType() >= 0 || maPath2DList.size() > 0) &&
@@ -315,13 +346,13 @@ void CustomShapeProperties::pushToPropSet(
                aHandle.setProperty( PROP_Position, maAdjustHandleList[ i ].pos);
                if ( maAdjustHandleList[ i ].gdRef1.has_value() )
                {
                    sal_Int32 nIndex = GetCustomShapeGuideValue( maAdjustmentGuideList, maAdjustHandleList[ i ].gdRef1.value() );
                    sal_Int32 nIndex = maAdjustmentGuideList.GetCustomShapeGuideValue( maAdjustHandleList[ i ].gdRef1.value() );
                    if ( nIndex >= 0 )
                        aHandle.setProperty( PROP_RefR, nIndex);
                }
                if ( maAdjustHandleList[ i ].gdRef2.has_value() )
                {
                    sal_Int32 nIndex = GetCustomShapeGuideValue( maAdjustmentGuideList, maAdjustHandleList[ i ].gdRef2.value() );
                    sal_Int32 nIndex = maAdjustmentGuideList.GetCustomShapeGuideValue( maAdjustHandleList[ i ].gdRef2.value() );
                    if ( nIndex >= 0 )
                        aHandle.setProperty( PROP_RefAngle, nIndex);
                }
@@ -344,13 +375,13 @@ void CustomShapeProperties::pushToPropSet(
                {
                    // TODO: PROP_RefX and PROP_RefY are not yet part of our file format,
                    // so the handles will not work after save/reload
                    sal_Int32 nIndex = GetCustomShapeGuideValue( maAdjustmentGuideList, maAdjustHandleList[ i ].gdRef1.value() );
                    sal_Int32 nIndex = maAdjustmentGuideList.GetCustomShapeGuideValue( maAdjustHandleList[ i ].gdRef1.value() );
                    if ( nIndex >= 0 )
                        aHandle.setProperty( PROP_RefX, nIndex);
                }
                if ( maAdjustHandleList[ i ].gdRef2.has_value() )
                {
                    sal_Int32 nIndex = GetCustomShapeGuideValue( maAdjustmentGuideList, maAdjustHandleList[ i ].gdRef2.value() );
                    sal_Int32 nIndex = maAdjustmentGuideList.GetCustomShapeGuideValue( maAdjustHandleList[ i ].gdRef2.value() );
                    if ( nIndex >= 0 )
                        aHandle.setProperty( PROP_RefY, nIndex);
                }
diff --git a/oox/source/drawingml/shape.cxx b/oox/source/drawingml/shape.cxx
index 23be28b..5b2109c 100644
--- a/oox/source/drawingml/shape.cxx
+++ b/oox/source/drawingml/shape.cxx
@@ -1878,7 +1878,7 @@ Reference< XShape > const & Shape::createAndInsert(
        {
            msConnectorName = mpCustomShapePropertiesPtr->getShapePresetTypeName();

            auto aAdjustmentList = mpCustomShapePropertiesPtr->getAdjustmentGuideList();
            const auto& aAdjustmentList = mpCustomShapePropertiesPtr->getAdjustmentGuideList();
            for (size_t i = 0; i < aAdjustmentList.size(); i++)
                maConnectorAdjustmentList.push_back(aAdjustmentList[i].maFormula);

diff --git a/oox/source/drawingml/transform2dcontext.cxx b/oox/source/drawingml/transform2dcontext.cxx
index 1cd67d1..656cb41 100644
--- a/oox/source/drawingml/transform2dcontext.cxx
+++ b/oox/source/drawingml/transform2dcontext.cxx
@@ -80,7 +80,7 @@ bool ConstructPresetTextRectangle(Shape& rShape, awt::Rectangle& rRect)
        case XML_round2SameRect:
        {
            // Second handle of round2SameRect used in preset diagrams has value 0.
            auto aAdjGdList = rShape.getCustomShapeProperties()->getAdjustmentGuideList();
            const auto& aAdjGdList = rShape.getCustomShapeProperties()->getAdjustmentGuideList();
            double fAdj = aAdjGdList.empty() ? 16667 : aAdjGdList[0].maFormula.toDouble();
            sal_Int32 nWidth = rShape.getSize().Width;
            sal_Int32 nHeight = rShape.getSize().Height;
@@ -98,7 +98,7 @@ bool ConstructPresetTextRectangle(Shape& rShape, awt::Rectangle& rRect)
        }
        case XML_trapezoid:
        {
            auto aAdjGdList = rShape.getCustomShapeProperties()->getAdjustmentGuideList();
            const auto& aAdjGdList = rShape.getCustomShapeProperties()->getAdjustmentGuideList();
            double fAdj = aAdjGdList.empty() ? 25000 : aAdjGdList[0].maFormula.toDouble();
            sal_Int32 nWidth = rShape.getSize().Width;
            sal_Int32 nHeight = rShape.getSize().Height;
@@ -144,7 +144,7 @@ bool ConstructPresetTextRectangle(Shape& rShape, awt::Rectangle& rRect)
                return false;
            double a1(15000.0);
            double a2(3526.0);
            auto aAdjGdList = rShape.getCustomShapeProperties()->getAdjustmentGuideList();
            const auto& aAdjGdList = rShape.getCustomShapeProperties()->getAdjustmentGuideList();
            if (aAdjGdList.size() == 2)
            {
                a1 = aAdjGdList[0].maFormula.toDouble();
@@ -186,7 +186,7 @@ bool ConstructPresetTextRectangle(Shape& rShape, awt::Rectangle& rRect)
        }
        case XML_hexagon:
        {
            auto aAdjGdList = rShape.getCustomShapeProperties()->getAdjustmentGuideList();
            const auto& aAdjGdList = rShape.getCustomShapeProperties()->getAdjustmentGuideList();
            double fAdj = aAdjGdList.empty() ? 25000 : aAdjGdList[0].maFormula.toDouble();
            sal_Int32 nWidth = rShape.getSize().Width;
            sal_Int32 nHeight = rShape.getSize().Height;
@@ -209,7 +209,7 @@ bool ConstructPresetTextRectangle(Shape& rShape, awt::Rectangle& rRect)
            sal_Int32 nHeight = rShape.getSize().Height;
            if (nWidth == 0 || nHeight == 0)
                return false;
            auto aAdjGdList = rShape.getCustomShapeProperties()->getAdjustmentGuideList();
            const auto& aAdjGdList = rShape.getCustomShapeProperties()->getAdjustmentGuideList();
            double fAdj = aAdjGdList.empty() ? 16667.0 : aAdjGdList[0].maFormula.toDouble();
            fAdj = std::clamp<double>(fAdj, 0.0, 50000.0);
            double fDx = std::min(nWidth, nHeight) * fAdj / 100000.0 * 0.29289;
@@ -228,7 +228,7 @@ bool ConstructPresetTextRectangle(Shape& rShape, awt::Rectangle& rRect)
                return false;
            double a1(50000.0);
            double a2(50000.0);
            auto aAdjGdList = rShape.getCustomShapeProperties()->getAdjustmentGuideList();
            const auto& aAdjGdList = rShape.getCustomShapeProperties()->getAdjustmentGuideList();
            if (aAdjGdList.size() == 2)
            {
                a1 = aAdjGdList[0].maFormula.toDouble();