sc/inc/patattr.hxx              |   17 +--
 sc/source/core/data/patattr.cxx |  171 ++++++++++++++++++----------------------
 2 files changed, 88 insertions(+), 100 deletions(-)

New commits:
commit faf60b6443dab44bd46535f88f76b271b33b4d36
Author:     Noel Grandin <[email protected]>
AuthorDate: Mon Dec 22 09:19:59 2025 +0200
Commit:     Noel Grandin <[email protected]>
CommitDate: Thu Jan 1 10:24:21 2026 +0100

    tdf#166684 use unordered_map/sorted_vector in registerAndCheck()
    
    which is little more memory expensive, but about 10% more efficient in load
    time here.
    
    Change-Id: If1cfb7dfe3b6a70f5acf8766db4b83eb386030bf
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196063
    Reviewed-by: Tomaž Vajngerl <[email protected]>
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    (cherry picked from commit b0920fbf83957a245292ea23840d79e0c6385429)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196358
    Reviewed-by: Noel Grandin <[email protected]>
    Tested-by: Jenkins

diff --git a/sc/inc/patattr.hxx b/sc/inc/patattr.hxx
index 1bcc5675b6bb..cc7f6442065b 100644
--- a/sc/inc/patattr.hxx
+++ b/sc/inc/patattr.hxx
@@ -25,11 +25,12 @@
 #include <svl/languageoptions.hxx>
 #include <tools/degree.hxx>
 #include <editeng/svxenum.hxx>
+#include <o3tl/sorted_vector.hxx>
 #include "scdllapi.h"
 #include "fonthelper.hxx"
 #include "scitems.hxx"
 #include "attrib.hxx"
-#include <set>
+#include <unordered_map>
 
 namespace vcl { class Font; }
 namespace model { class ComplexColor; }
@@ -62,20 +63,16 @@ class SC_DLLPUBLIC CellAttributeHelper final
     // 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
+    // (c) scanning through all the entries with the same name is cheap
+    struct RegisteredAttrMapHash
     {
-        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;
+        size_t operator()(const std::optional<OUString>&) const;
     };
-    typedef std::set<const ScPatternAttr*, RegisteredAttrSetLess> 
RegisteredAttrSet;
+    typedef std::unordered_map<std::optional<OUString>, 
o3tl::sorted_vector<const ScPatternAttr*>, RegisteredAttrMapHash> 
RegisteredAttrMap;
 
     SfxItemPool&                                        mrSfxItemPool;
     mutable ScPatternAttr*                              mpDefaultCellAttribute;
-    mutable RegisteredAttrSet                           
maRegisteredCellAttributes;
+    mutable RegisteredAttrMap                           
maRegisteredCellAttributes;
     mutable const ScPatternAttr*                        mpLastHit;
     mutable sal_uInt64                                  mnCurrentMaxKey;
 
diff --git a/sc/source/core/data/patattr.cxx b/sc/source/core/data/patattr.cxx
index e4e3587c807d..094128fc66e0 100644
--- a/sc/source/core/data/patattr.cxx
+++ b/sc/source/core/data/patattr.cxx
@@ -80,17 +80,6 @@ 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())
@@ -114,28 +103,29 @@ const ScPatternAttr* 
CellAttributeHelper::registerAndCheck(const ScPatternAttr&
         return mpLastHit;
     }
     const OUString* pCandidateStyleName = rCandidate.GetStyleName();
-    auto it = maRegisteredCellAttributes.lower_bound(pCandidateStyleName);
-    const size_t nCandidateHashCode = rCandidate.GetHashCode();
-    for (; it != maRegisteredCellAttributes.end(); ++it)
+    auto it = maRegisteredCellAttributes.find(pCandidateStyleName ? 
std::optional<OUString>(*pCandidateStyleName) : std::nullopt);
+    if (it != maRegisteredCellAttributes.end())
     {
-        const ScPatternAttr* pCheck = *it;
-        if (CompareStringPtr(pCheck->GetStyleName(), pCandidateStyleName) != 0)
-            break;
-        if (nCandidateHashCode == pCheck->GetHashCode()
-            && ScPatternAttr::areSame(pCheck, &rCandidate))
+        const size_t nCandidateHashCode = rCandidate.GetHashCode();
+        for (const ScPatternAttr* pCheck : it->second)
         {
-            pCheck->mnRefCount++;
-            if (bPassingOwnership)
-                delete &rCandidate;
-            mpLastHit = pCheck;
-            return pCheck;
+            if (nCandidateHashCode == pCheck->GetHashCode()
+                && ScPatternAttr::areSame(pCheck, &rCandidate))
+            {
+                pCheck->mnRefCount++;
+                if (bPassingOwnership)
+                    delete &rCandidate;
+                mpLastHit = pCheck;
+                return pCheck;
+            }
         }
     }
 
     const ScPatternAttr* pCandidate(bPassingOwnership ? &rCandidate : new 
ScPatternAttr(rCandidate));
     pCandidate->mnRefCount++;
     const_cast<ScPatternAttr*>(pCandidate)->SetPAKey(mnCurrentMaxKey++);
-    maRegisteredCellAttributes.insert(pCandidate);
+    const OUString* pStyleName = pCandidate->GetStyleName();
+    maRegisteredCellAttributes[pStyleName ? 
std::optional<OUString>(*pStyleName) : std::nullopt].insert(pCandidate);
     mpLastHit = pCandidate;
     return pCandidate;
 }
@@ -154,8 +144,12 @@ void CellAttributeHelper::doUnregister(const 
ScPatternAttr& rCandidate)
     if (mpLastHit == &rCandidate)
         mpLastHit = nullptr;
 
-    assert(maRegisteredCellAttributes.find(&rCandidate) != 
maRegisteredCellAttributes.end());
-    maRegisteredCellAttributes.erase(&rCandidate);
+    const OUString* pStyleName = rCandidate.GetStyleName();
+    auto it = maRegisteredCellAttributes.find(pStyleName ? 
std::optional<OUString>(*pStyleName) : std::nullopt);
+    assert(it != maRegisteredCellAttributes.end());
+    it->second.erase(&rCandidate);
+    if (it->second.empty())
+        maRegisteredCellAttributes.erase(it);
     delete &rCandidate;
 }
 
@@ -188,15 +182,13 @@ const ScPatternAttr& 
CellAttributeHelper::getDefaultCellAttribute() const
 void CellAttributeHelper::CellStyleDeleted(const ScStyleSheet& rStyle)
 {
     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();
-    }
+    auto it = maRegisteredCellAttributes.find(rCandidateStyleName);
+    if (it != maRegisteredCellAttributes.end())
+        for (const ScPatternAttr* pCheck : it->second)
+        {
+            if (&rStyle == pCheck->GetStyleSheet())
+                const_cast<ScPatternAttr*>(pCheck)->StyleToName();
+        }
 }
 
 void CellAttributeHelper::RenameCellStyle(ScStyleSheet& rStyle, const 
OUString& rNewName)
@@ -204,26 +196,32 @@ void CellAttributeHelper::RenameCellStyle(ScStyleSheet& 
rStyle, const OUString&
     std::vector<const ScPatternAttr*> aChanged;
 
     const OUString& rCandidateStyleName = rStyle.GetName();
-    auto it = maRegisteredCellAttributes.lower_bound(&rCandidateStyleName);
-    while(it != maRegisteredCellAttributes.end())
+    auto it = maRegisteredCellAttributes.find(rCandidateStyleName);
+    if (it != maRegisteredCellAttributes.end())
     {
-        const ScPatternAttr* pCheck = *it;
-        if (CompareStringPtr(pCheck->GetStyleName(), &rCandidateStyleName) != 
0)
-            break;
-        if (&rStyle == pCheck->GetStyleSheet())
+        for (auto it2 = it->second.begin(); it2 != it->second.end();)
         {
-            aChanged.push_back(pCheck);
-            // The name will change, we have to re-insert it
-            it = maRegisteredCellAttributes.erase(it);
+            const ScPatternAttr* pCheck = *it2;
+            if (&rStyle == pCheck->GetStyleSheet())
+            {
+                aChanged.push_back(pCheck);
+                // The name will change, we have to re-insert it
+                it2 = it->second.erase(it2);
+            }
+            else
+                ++it2;
         }
-        else
-            ++it;
+        if (it->second.empty())
+            maRegisteredCellAttributes.erase(it);
     }
 
     rStyle.SetName(rNewName);
 
     for (const ScPatternAttr* p : aChanged)
-        maRegisteredCellAttributes.insert(p);
+    {
+        const OUString* pStyleName = p->GetStyleName();
+        maRegisteredCellAttributes[pStyleName ? 
std::optional<OUString>(*pStyleName) : std::nullopt].insert(p);
+    }
 }
 
 void CellAttributeHelper::CellStyleCreated(const ScDocument& rDoc, const 
OUString& rName)
@@ -233,12 +231,13 @@ void CellAttributeHelper::CellStyleCreated(const 
ScDocument& rDoc, const OUStrin
     // Calling StyleSheetChanged isn't enough because the pool may still 
contain items
     // for undo or clipboard content.
     std::vector<const ScPatternAttr*> aChanged;
-    auto it = maRegisteredCellAttributes.lower_bound(&rName);
-    while(it != maRegisteredCellAttributes.end())
+    auto it = maRegisteredCellAttributes.find(rName);
+    if (it == maRegisteredCellAttributes.end())
+        return;
+
+    for (auto it2 = it->second.begin(); it2 != it->second.end();)
     {
-        const ScPatternAttr* pCheck = *it;
-        if (CompareStringPtr(pCheck->GetStyleName(), &rName) != 0)
-            break;
+        const ScPatternAttr* pCheck = *it2;
         // tdf#163831 Invalidate cache if the style is modified/created
         const_cast<ScPatternAttr*>(pCheck)->InvalidateCaches();
         if (nullptr == pCheck->GetStyleSheet())
@@ -246,22 +245,29 @@ void CellAttributeHelper::CellStyleCreated(const 
ScDocument& rDoc, const OUStrin
             {
                 aChanged.push_back(pCheck);
                 // if the name changed, we have to re-insert it
-                it = maRegisteredCellAttributes.erase(it);
+                it2 = it->second.erase(it2);
             }
             else
-                ++it;
+                ++it2;
         else
-            ++it;
+            ++it2;
     }
+    if (it->second.empty())
+        maRegisteredCellAttributes.erase(it);
+
     for (const ScPatternAttr* p : aChanged)
-        maRegisteredCellAttributes.insert(p);
+    {
+        const OUString* pStyleName = p->GetStyleName();
+        maRegisteredCellAttributes[pStyleName ? 
std::optional<OUString>(*pStyleName) : std::nullopt].insert(p);
+    }
 }
 
 void CellAttributeHelper::UpdateAllStyleSheets(const ScDocument& rDoc)
 {
     bool bNameChanged = false;
-    for (const ScPatternAttr* pCheck : maRegisteredCellAttributes)
-        bNameChanged |= 
const_cast<ScPatternAttr*>(pCheck)->UpdateStyleSheet(rDoc);
+    for (const auto & rPair : maRegisteredCellAttributes)
+        for (const ScPatternAttr* pCheck : rPair.second)
+            bNameChanged |= 
const_cast<ScPatternAttr*>(pCheck)->UpdateStyleSheet(rDoc);
     if (bNameChanged)
         ReIndexRegistered();
 
@@ -272,8 +278,9 @@ void CellAttributeHelper::UpdateAllStyleSheets(const 
ScDocument& rDoc)
 
 void CellAttributeHelper::AllStylesToNames()
 {
-    for (const ScPatternAttr* pCheck : maRegisteredCellAttributes)
-        const_cast<ScPatternAttr*>(pCheck)->StyleToName();
+    for (const auto & rPair : maRegisteredCellAttributes)
+        for (const ScPatternAttr* pCheck : rPair.second)
+            const_cast<ScPatternAttr*>(pCheck)->StyleToName();
 
     // force existence, then access
     getDefaultCellAttribute();
@@ -283,38 +290,22 @@ void CellAttributeHelper::AllStylesToNames()
 /// 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);
-}
+    RegisteredAttrMap aNewMap;
+    for (const auto & rPair : maRegisteredCellAttributes)
+        for (const ScPatternAttr* p : rPair.second)
+        {
+            const OUString* pStyleName = p->GetStyleName();
+            aNewMap[pStyleName ? std::optional<OUString>(*pStyleName) : 
std::nullopt].insert(p);
+        }
 
-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 false;
+    maRegisteredCellAttributes = std::move(aNewMap);
 }
-bool CellAttributeHelper::RegisteredAttrSetLess::operator()(const OUString* 
lhs, const ScPatternAttr* rhs) const
+
+size_t CellAttributeHelper::RegisteredAttrMapHash::operator()(const 
std::optional<OUString>& p) const
 {
-    int cmp = CompareStringPtr(lhs, rhs->GetStyleName());
-    if (cmp < 0)
-        return true;
-    if (cmp > 0)
-        return false;
-    return true;
+    if (!p)
+        return 0;
+    return p->hashCode();
 }
 
 

Reply via email to