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();