sc/inc/patattr.hxx              |   25 ++++++--
 sc/source/core/data/patattr.cxx |  121 ++++++++++++++++++++++++++++++++--------
 2 files changed, 119 insertions(+), 27 deletions(-)

New commits:
commit 9b1bba2f9633b27cd822270d79f01be19db9c640
Author:     Noel Grandin <noel.gran...@collabora.co.uk>
AuthorDate: Thu Apr 18 16:21:44 2024 +0200
Commit:     Noel Grandin <noel.gran...@collabora.co.uk>
CommitDate: Fri Apr 19 22:10:11 2024 +0200

    tdf#160706 speed up loading conditional formatting rule in XLS (4)
    
    speed up the matching of duplicates in CellAttributeHelper by using
    std::set and partial sorting by name
    
    Takes my time from 33s to 6s
    
    Change-Id: I06376c1e253981cb5a3020142d24fa5776513d4d
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/166262
    Tested-by: Jenkins
    Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk>

diff --git a/sc/inc/patattr.hxx b/sc/inc/patattr.hxx
index 1dabeaa07102..9ca1ff0250d8 100644
--- a/sc/inc/patattr.hxx
+++ b/sc/inc/patattr.hxx
@@ -29,7 +29,7 @@
 #include "fonthelper.hxx"
 #include "scitems.hxx"
 #include "attrib.hxx"
-#include <unordered_set>
+#include <set>
 
 namespace vcl { class Font; }
 namespace model { class ComplexColor; }
@@ -59,9 +59,23 @@ class SC_DLLPUBLIC CellAttributeHelper final
 {
     friend class CellAttributeHolder;
 
+    // Data structure chosen so that
+    // (a) we can find by name
+    // (b) we can erase quickly, by using name and pointer.
+    // so we sort the set first by name, and then by pointer.
+    struct RegisteredAttrSetLess
+    {
+        bool operator()(const ScPatternAttr* lhs, const ScPatternAttr* rhs) 
const;
+        // so we can search in std::set without a ScPatternAttr
+        using is_transparent = void;
+        bool operator()(const ScPatternAttr* lhs, const OUString* rhs) const;
+        bool operator()(const OUString* lhs, const ScPatternAttr* rhs) const;
+    };
+    typedef std::set<const ScPatternAttr*, RegisteredAttrSetLess> 
RegisteredAttrSet;
+
     SfxItemPool&                                        mrSfxItemPool;
     mutable ScPatternAttr*                              mpDefaultCellAttribute;
-    mutable std::unordered_set<const ScPatternAttr*>    
maRegisteredCellAttributes;
+    mutable RegisteredAttrSet                           
maRegisteredCellAttributes;
     mutable const ScPatternAttr*                        mpLastHit;
     mutable sal_uInt64                                  mnCurrentMaxKey;
 
@@ -77,9 +91,10 @@ public:
     SfxItemPool& GetPool() const { return mrSfxItemPool; }
 
     void CellStyleDeleted(const ScStyleSheet& rStyle);
-    void CellStyleCreated(ScDocument& rDoc, std::u16string_view rName);
+    void CellStyleCreated(ScDocument& rDoc, const OUString& rName);
     void UpdateAllStyleSheets(ScDocument& rDoc);
     void AllStylesToNames();
+    void ReIndexRegistered();
 };
 
 class SC_DLLPUBLIC CellAttributeHolder final
@@ -111,7 +126,7 @@ class SC_DLLPUBLIC ScPatternAttr final
     friend class CellAttributeHelper;
 
     SfxItemSet                  maLocalSfxItemSet;
-    std::optional<OUString>     pName;
+    std::optional<OUString>     moName;
     mutable std::optional<bool> mxVisible;
     ScStyleSheet*               pStyle;
     CellAttributeHelper*        pCellAttributeHelper;
@@ -228,7 +243,7 @@ public:
     void                    SetStyleSheet(ScStyleSheet* pNewStyle, bool 
bClearDirectFormat = true);
     const ScStyleSheet*     GetStyleSheet() const  { return pStyle; }
     const OUString*         GetStyleName() const;
-    void                    UpdateStyleSheet(const ScDocument& rDoc);
+    bool                    UpdateStyleSheet(const ScDocument& rDoc);
     void                    StyleToName();
 
     bool                    IsVisible() const;
diff --git a/sc/source/core/data/patattr.cxx b/sc/source/core/data/patattr.cxx
index 486806e7ccc9..08b120339aa7 100644
--- a/sc/source/core/data/patattr.cxx
+++ b/sc/source/core/data/patattr.cxx
@@ -81,6 +81,17 @@ CellAttributeHelper::~CellAttributeHelper()
     delete mpDefaultCellAttribute;
 }
 
+static int CompareStringPtr(const OUString* lhs, const OUString* rhs)
+{
+    if (lhs == rhs)
+        return 0;
+    if (lhs && rhs)
+        return (*lhs).compareTo(*rhs);
+    if (!lhs && rhs)
+        return -1;
+    return 1;
+}
+
 const ScPatternAttr* CellAttributeHelper::registerAndCheck(const 
ScPatternAttr& rCandidate, bool bPassingOwnership) const
 {
     if (&rCandidate == &getDefaultCellAttribute())
@@ -103,13 +114,13 @@ const ScPatternAttr* 
CellAttributeHelper::registerAndCheck(const ScPatternAttr&
             delete &rCandidate;
         return mpLastHit;
     }
-
-    for (const ScPatternAttr* pCheck : maRegisteredCellAttributes)
+    const OUString* pCandidateStyleName = rCandidate.GetStyleName();
+    auto it = maRegisteredCellAttributes.lower_bound(pCandidateStyleName);
+    for (; it != maRegisteredCellAttributes.end(); ++it)
     {
-        if (mpLastHit == pCheck)
-            // ptr compare: already checked above, skip this one
-            continue;
-
+        const ScPatternAttr* pCheck = *it;
+        if (CompareStringPtr(pCheck->GetStyleName(), pCandidateStyleName) != 0)
+            break;
         if (ScPatternAttr::areSame(pCheck, &rCandidate))
         {
             pCheck->mnRefCount++;
@@ -174,30 +185,54 @@ const ScPatternAttr& 
CellAttributeHelper::getDefaultCellAttribute() const
 
 void CellAttributeHelper::CellStyleDeleted(const ScStyleSheet& rStyle)
 {
-    for (const ScPatternAttr* pCheck : maRegisteredCellAttributes)
+    const OUString& rCandidateStyleName = rStyle.GetName();
+    auto it = maRegisteredCellAttributes.lower_bound(&rCandidateStyleName);
+    for (; it != maRegisteredCellAttributes.end(); ++it)
     {
+        const ScPatternAttr* pCheck = *it;
+        if (CompareStringPtr(pCheck->GetStyleName(), &rCandidateStyleName) != 
0)
+            break;
         if (&rStyle == pCheck->GetStyleSheet())
             const_cast<ScPatternAttr*>(pCheck)->StyleToName();
     }
 }
 
-void CellAttributeHelper::CellStyleCreated(ScDocument& rDoc, 
std::u16string_view rName)
+void CellAttributeHelper::CellStyleCreated(ScDocument& rDoc, const OUString& 
rName)
 {
     // If a style was created, don't keep any pattern with its name string in 
the pool,
     // because it would compare equal to a pattern with a pointer to the new 
style.
     // Calling StyleSheetChanged isn't enough because the pool may still 
contain items
     // for undo or clipboard content.
-    for (const ScPatternAttr* pCheck : maRegisteredCellAttributes)
+    std::vector<const ScPatternAttr*> aChanged;
+    auto it = maRegisteredCellAttributes.lower_bound(&rName);
+    while(it != maRegisteredCellAttributes.end())
     {
-        if (nullptr == pCheck->GetStyleSheet() && pCheck->GetStyleName() && 
rName == *pCheck->GetStyleName())
-            const_cast<ScPatternAttr*>(pCheck)->UpdateStyleSheet(rDoc); // 
find and store style pointer
+        const ScPatternAttr* pCheck = *it;
+        if (CompareStringPtr(pCheck->GetStyleName(), &rName) != 0)
+            break;
+        if (nullptr == pCheck->GetStyleSheet())
+            if (const_cast<ScPatternAttr*>(pCheck)->UpdateStyleSheet(rDoc)) // 
find and store style pointer
+            {
+                aChanged.push_back(pCheck);
+                // if the name changed, we have to re-insert it
+                it = maRegisteredCellAttributes.erase(it);
+            }
+            else
+                ++it;
+        else
+            ++it;
     }
+    for (const ScPatternAttr* p : aChanged)
+        maRegisteredCellAttributes.insert(p);
 }
 
 void CellAttributeHelper::UpdateAllStyleSheets(ScDocument& rDoc)
 {
+    bool bNameChanged = false;
     for (const ScPatternAttr* pCheck : maRegisteredCellAttributes)
-        const_cast<ScPatternAttr*>(pCheck)->UpdateStyleSheet(rDoc);
+        bNameChanged |= 
const_cast<ScPatternAttr*>(pCheck)->UpdateStyleSheet(rDoc);
+    if (bNameChanged)
+        ReIndexRegistered();
 
     // force existence, then access
     getDefaultCellAttribute();
@@ -214,6 +249,44 @@ void CellAttributeHelper::AllStylesToNames()
     mpDefaultCellAttribute->StyleToName();
 }
 
+/// If the style name changed, we need to reindex.
+void CellAttributeHelper::ReIndexRegistered()
+{
+    RegisteredAttrSet aNewSet;
+    for (auto const & p : maRegisteredCellAttributes)
+        aNewSet.insert(p);
+    maRegisteredCellAttributes = std::move(aNewSet);
+}
+
+bool CellAttributeHelper::RegisteredAttrSetLess::operator()(const 
ScPatternAttr* lhs, const ScPatternAttr* rhs) const
+{
+    int cmp = CompareStringPtr(lhs->GetStyleName(), rhs->GetStyleName());
+    if (cmp < 0)
+        return true;
+    if (cmp > 0)
+        return false;
+    return lhs < rhs;
+}
+bool CellAttributeHelper::RegisteredAttrSetLess::operator()(const 
ScPatternAttr* lhs, const OUString* rhs) const
+{
+    int cmp = CompareStringPtr(lhs->GetStyleName(), rhs);
+    if (cmp < 0)
+        return true;
+    if (cmp > 0)
+        return false;
+    return lhs < static_cast<const ScPatternAttr*>(nullptr);
+}
+bool CellAttributeHelper::RegisteredAttrSetLess::operator()(const OUString* 
lhs, const ScPatternAttr* rhs) const
+{
+    int cmp = CompareStringPtr(lhs, rhs->GetStyleName());
+    if (cmp < 0)
+        return true;
+    if (cmp > 0)
+        return false;
+    return static_cast<const ScPatternAttr*>(nullptr) < rhs;
+}
+
+
 CellAttributeHolder::CellAttributeHolder(const ScPatternAttr* pNew, bool 
bPassingOwnership)
 : mpScPatternAttr(nullptr)
 {
@@ -309,7 +382,6 @@ const WhichRangesContainer 
aScPatternAttrSchema(svl::Items<ATTR_PATTERN_START, A
 
 ScPatternAttr::ScPatternAttr(CellAttributeHelper& rHelper, const SfxItemSet* 
pItemSet, const OUString* pStyleName)
 : maLocalSfxItemSet(rHelper.GetPool(), aScPatternAttrSchema)
-, pName()
 , mxVisible()
 , pStyle(nullptr)
 , pCellAttributeHelper(&rHelper)
@@ -321,7 +393,7 @@ ScPatternAttr::ScPatternAttr(CellAttributeHelper& rHelper, 
const SfxItemSet* pIt
 #endif
 {
     if (nullptr != pStyleName)
-        pName = *pStyleName;
+        moName = *pStyleName;
 
     // We need to ensure that ScPatternAttr is using the correct WhichRange,
     // see comments in commit message. This does transfers the items with
@@ -338,7 +410,7 @@ ScPatternAttr::ScPatternAttr(CellAttributeHelper& rHelper, 
const SfxItemSet* pIt
 
 ScPatternAttr::ScPatternAttr(const ScPatternAttr& rPatternAttr)
 : maLocalSfxItemSet(rPatternAttr.maLocalSfxItemSet)
-, pName(rPatternAttr.pName)
+, moName(rPatternAttr.moName)
 , mxVisible()
 , pStyle(rPatternAttr.pStyle)
 , pCellAttributeHelper(rPatternAttr.pCellAttributeHelper)
@@ -1548,7 +1620,7 @@ bool ScPatternAttr::IsVisibleEqual( const ScPatternAttr& 
rOther ) const
 
 const OUString* ScPatternAttr::GetStyleName() const
 {
-    return pName ? &*pName : ( pStyle ? &pStyle->GetName() : nullptr );
+    return moName ? &*moName : ( pStyle ? &pStyle->GetName() : nullptr );
 }
 
 void ScPatternAttr::SetStyleSheet( ScStyleSheet* pNewStyle, bool 
bClearDirectFormat )
@@ -1568,7 +1640,7 @@ void ScPatternAttr::SetStyleSheet( ScStyleSheet* 
pNewStyle, bool bClearDirectFor
         }
         rPatternSet.SetParent(&pNewStyle->GetItemSet());
         pStyle = pNewStyle;
-        pName.reset();
+        moName.reset();
     }
     else
     {
@@ -1579,11 +1651,12 @@ void ScPatternAttr::SetStyleSheet( ScStyleSheet* 
pNewStyle, bool bClearDirectFor
     mxVisible.reset();
 }
 
-void ScPatternAttr::UpdateStyleSheet(const ScDocument& rDoc)
+bool ScPatternAttr::UpdateStyleSheet(const ScDocument& rDoc)
 {
-    if (pName)
+    bool bNameChanged = false;
+    if (moName)
     {
-        pStyle = 
static_cast<ScStyleSheet*>(rDoc.GetStyleSheetPool()->Find(*pName, 
SfxStyleFamily::Para));
+        pStyle = 
static_cast<ScStyleSheet*>(rDoc.GetStyleSheetPool()->Find(*moName, 
SfxStyleFamily::Para));
 
         //  use Standard if Style is not found,
         //  to avoid empty display in Toolbox-Controller
@@ -1597,12 +1670,16 @@ void ScPatternAttr::UpdateStyleSheet(const ScDocument& 
rDoc)
         if (pStyle)
         {
             GetItemSet().SetParent(&pStyle->GetItemSet());
-            pName.reset();
+            moName.reset();
         }
     }
     else
+    {
         pStyle = nullptr;
+        bNameChanged = true;
+    }
     mxVisible.reset();
+    return bNameChanged;
 }
 
 void ScPatternAttr::StyleToName()
@@ -1611,7 +1688,7 @@ void ScPatternAttr::StyleToName()
 
     if ( pStyle )
     {
-        pName = pStyle->GetName();
+        moName = pStyle->GetName();
         pStyle = nullptr;
         GetItemSet().SetParent( nullptr );
         mxVisible.reset();

Reply via email to