[Libreoffice-commits] core.git: sw/inc sw/source

Noel Grandin (via logerrit) logerrit at kemper.freedesktop.org
Tue May 28 14:42:23 UTC 2019


 sw/inc/ndhints.hxx                  |   65 ++++++++++++----------
 sw/inc/txatbase.hxx                 |   18 ++++--
 sw/inc/txtrfmrk.hxx                 |    3 -
 sw/inc/txttxmrk.hxx                 |    3 -
 sw/source/core/crsr/findattr.cxx    |    3 -
 sw/source/core/fields/reffld.cxx    |    2 
 sw/source/core/txtnode/atrref.cxx   |    9 ++-
 sw/source/core/txtnode/atrtox.cxx   |    9 ++-
 sw/source/core/txtnode/ndhints.cxx  |  103 ++++++++++++++++++++++++------------
 sw/source/core/txtnode/ndtxt.cxx    |   68 ++++++++++++-----------
 sw/source/core/txtnode/thints.cxx   |   83 ++++++++++++++++-------------
 sw/source/core/txtnode/txatbase.cxx |   16 ++++-
 sw/source/core/txtnode/txtedt.cxx   |   13 ++--
 13 files changed, 242 insertions(+), 153 deletions(-)

New commits:
commit 7ac940d278f9bb3fbb1988a74dfa4909960bd998
Author:     Noel Grandin <noel.grandin at collabora.co.uk>
AuthorDate: Tue May 28 11:06:23 2019 +0200
Commit:     Noel Grandin <noel.grandin at collabora.co.uk>
CommitDate: Tue May 28 16:41:26 2019 +0200

    tdf#125372 writer, file with lots of hints very slow to open, part3
    
    There were O(n^2) issues all over the places where we walk these lists.
    
    This takes the opening time from 10m+ to 4m.
    
    (*) Invalidate the sorting on a much less aggressive basis, by having
    the SwTextAttr objects tell the SwpHints object when start and end pos
    changes
    (*) Add a bool field to indicate when the maps become unsorted, so we
    can resort them only when we actually need sorted access
    (*) Add an API for walking the list of SwTextAttr* without triggering
    sorting, which is particularly important when the RedlineManager starts
    moving things around.
    (*) Add various asserts to catch code that tries to iterate over these
    sorted lists but triggers resorting during the loop.
    
    I also moved some of the logic around so that instead of
        update hint
        delete hint
        insert hint
    it now goes
        delete hint
        update hint
        insert hint
    which means that the needToSort flag does not get set because the
    hint is disconnected while it is being updated.
    
    Change-Id: Ie153ddafc9ef9e3470d588db2be2457c676232a8
    Reviewed-on: https://gerrit.libreoffice.org/73090
    Reviewed-by: Noel Grandin <noel.grandin at collabora.co.uk>
    Tested-by: Noel Grandin <noel.grandin at collabora.co.uk>

diff --git a/sw/inc/ndhints.hxx b/sw/inc/ndhints.hxx
index c3112844db8e..299218a55399 100644
--- a/sw/inc/ndhints.hxx
+++ b/sw/inc/ndhints.hxx
@@ -54,23 +54,8 @@ SwTextAttr* MakeRedlineTextAttr(
     SwDoc & rDoc,
     SfxPoolItem const & rAttr );
 
-
-/// SwTextAttr's, sorted by start
-struct CompareSwpHtStart
-{
-    bool operator()(SwTextAttr const * const lhs, SwTextAttr const * const rhs) const;
-};
-class SwpHtStart : public o3tl::sorted_vector<SwTextAttr*, CompareSwpHtStart,
-    o3tl::find_partialorder_ptrequals> {};
-
-/// SwTextAttr's, sorted by end
-struct CompareSwpHtEnd
-{
-    bool operator()(SwTextAttr const * const lhs, SwTextAttr const * const rhs) const;
-};
-class SwpHtEnd : public o3tl::sorted_vector<SwTextAttr*, CompareSwpHtEnd,
-    o3tl::find_partialorder_ptrequals> {};
-
+bool CompareSwpHtEnd( const SwTextAttr* lhs, const SwTextAttr* rhs );
+bool CompareSwpHtWhichStart( const SwTextAttr* lhs, const SwTextAttr* rhs );
 
 /// An SwTextAttr container, stores all directly formatted text portions for a text node.
 class SwpHints
@@ -82,8 +67,8 @@ private:
     // failure, so just allow SAL_MAX_SIZE-1 hints
     static const size_t MAX_HINTS = SAL_MAX_SIZE-1;
 
-    SwpHtStart    m_HintsByStart;
-    SwpHtEnd      m_HintsByEnd;
+    std::vector<SwTextAttr*> m_HintsByStart;
+    std::vector<SwTextAttr*> m_HintsByEnd;
 
     SwRegHistory* m_pHistory;                   ///< for Undo
 
@@ -96,6 +81,9 @@ private:
     mutable bool  m_bHiddenByParaField   : 1;
     bool          m_bFootnote            : 1;   ///< footnotes
     bool          m_bDDEFields           : 1;   ///< the TextNode has DDE fields
+    // Sort on demand to avoid O(n^2) behaviour
+    mutable bool  m_bStartMapNeedsSorting : 1;
+    mutable bool  m_bEndMapNeedsSorting : 1;
 
     /// records a new attribute in m_pHistory.
     void NoteInHistory( SwTextAttr *pAttr, const bool bNew = false );
@@ -129,18 +117,11 @@ private:
     bool MergePortions( SwTextNode& rNode );
 
     void Insert( const SwTextAttr *pHt );
-    void Resort();
+    SW_DLLPUBLIC void Resort() const;
+    SW_DLLPUBLIC void ResortStartMap() const;
+    SW_DLLPUBLIC void ResortEndMap() const;
 
-    size_t GetIndexOf( const SwTextAttr *pHt ) const
-    {
-        SwpHtStart::const_iterator const it =
-            m_HintsByStart.find(const_cast<SwTextAttr*>(pHt));
-        if ( it == m_HintsByStart.end() )
-        {
-            return SAL_MAX_SIZE;
-        }
-        return it - m_HintsByStart.begin();
-    }
+    size_t GetIndexOf( const SwTextAttr *pHt ) const;
 
 #ifdef DBG_UTIL
     bool Check(bool) const;
@@ -153,12 +134,32 @@ public:
     bool Contains( const SwTextAttr *pHt ) const;
     SwTextAttr * Get( size_t nPos ) const
     {
+        assert( !(nPos != 0 && m_bStartMapNeedsSorting) && "going to trigger a resort in the middle of an iteration, that's bad" );
+        if (m_bStartMapNeedsSorting)
+            ResortStartMap();
+        return m_HintsByStart[nPos];
+    }
+    // Get without triggering resorting - useful if we are modifying start/end pos while iterating
+    SwTextAttr * GetWithoutResorting( size_t nPos ) const
+    {
         return m_HintsByStart[nPos];
     }
+    int GetLastPosSortedByEnd(sal_Int32 nEndPos) const;
     SwTextAttr * GetSortedByEnd( size_t nPos ) const
     {
+        assert( !(nPos != 0 && m_bEndMapNeedsSorting) && "going to trigger a resort in the middle of an iteration, that's bad" );
+        if (m_bEndMapNeedsSorting)
+            ResortEndMap();
         return m_HintsByEnd[nPos];
     }
+    /// Trigger the sorting if necessary
+    void SortIfNeedBe() const
+    {
+        if (m_bStartMapNeedsSorting)
+            ResortStartMap();
+        if (m_bEndMapNeedsSorting)
+            ResortEndMap();
+    }
     SwTextAttr * Cut( const size_t nPosInStart )
     {
         SwTextAttr *pHt = m_HintsByStart[nPosInStart];
@@ -184,6 +185,10 @@ public:
 
     // calc current value of m_bHiddenByParaField, returns true iff changed
     bool CalcHiddenParaField() const; // changes mutable state
+
+    // Marks the hint-maps as needing sorting because the position of something has changed
+    void StartPosChanged() const { m_bStartMapNeedsSorting = true; m_bEndMapNeedsSorting = true; }
+    void EndPosChanged() const { m_bStartMapNeedsSorting = true; m_bEndMapNeedsSorting = true; }
 };
 
 #endif
diff --git a/sw/inc/txatbase.hxx b/sw/inc/txatbase.hxx
index 4be53ba7a408..0e2aebdb846f 100644
--- a/sw/inc/txatbase.hxx
+++ b/sw/inc/txatbase.hxx
@@ -30,11 +30,13 @@
 #include "fmtftn.hxx"
 #include "fchrfmt.hxx"
 #include "tox.hxx"
+#include "ndhints.hxx"
 
 class SfxItemPool;
 
 class SAL_DLLPUBLIC_RTTI SwTextAttr
 {
+friend class SwpHints;
 private:
     SfxPoolItem * const m_pAttr;
     sal_Int32 m_nStart;
@@ -56,6 +58,8 @@ private:
     SwTextAttr& operator=(SwTextAttr const&) = delete;
 
 protected:
+    SwpHints * m_pHints = nullptr;  // the SwpHints holds a pointer to this, and needs to be notified if the start/end changes
+
     SwTextAttr( SfxPoolItem& rAttr, sal_Int32 nStart );
     virtual ~SwTextAttr() COVERITY_NOEXCEPT_FALSE;
 
@@ -74,11 +78,12 @@ public:
     static void Destroy( SwTextAttr * pToDestroy, SfxItemPool& rPool );
 
     /// start position
-                  sal_Int32& GetStart()        { return m_nStart; }
-            const sal_Int32& GetStart() const  { return m_nStart; }
+    void SetStart(sal_Int32 n) { m_nStart = n; if (m_pHints) m_pHints->StartPosChanged(); }
+    sal_Int32 GetStart() const { return m_nStart; }
 
     /// end position
-    virtual      sal_Int32* GetEnd(); // also used to change the end position
+    virtual const sal_Int32* GetEnd() const;
+    virtual void SetEnd(sal_Int32);
     inline const sal_Int32* End() const;
     /// end (if available), else start
     inline const sal_Int32* GetAnyEnd() const;
@@ -127,7 +132,8 @@ protected:
 public:
     SwTextAttrEnd( SfxPoolItem& rAttr, sal_Int32 nStart, sal_Int32 nEnd );
 
-    virtual sal_Int32* GetEnd() override;
+    virtual const sal_Int32* GetEnd() const override;
+    virtual void SetEnd(sal_Int32) override;
 };
 
 // attribute that must not overlap others
@@ -141,13 +147,13 @@ protected:
 
 inline const sal_Int32* SwTextAttr::End() const
 {
-    return const_cast<SwTextAttr * >(this)->GetEnd();
+    return GetEnd();
 }
 
 inline const sal_Int32* SwTextAttr::GetAnyEnd() const
 {
     const sal_Int32* pEnd = End();
-    return pEnd ? pEnd : &GetStart();
+    return pEnd ? pEnd : &m_nStart;
 }
 
 inline const SfxPoolItem& SwTextAttr::GetAttr() const
diff --git a/sw/inc/txtrfmrk.hxx b/sw/inc/txtrfmrk.hxx
index f756a587ce18..c8261c0e5ce4 100644
--- a/sw/inc/txtrfmrk.hxx
+++ b/sw/inc/txtrfmrk.hxx
@@ -33,7 +33,8 @@ public:
     SwTextRefMark( SwFormatRefMark& rAttr,
             sal_Int32 const nStart, sal_Int32 const*const pEnd = nullptr);
 
-    virtual sal_Int32* GetEnd() override;       // SwTextAttr
+    virtual const sal_Int32* GetEnd() const override;       // SwTextAttr
+    virtual void SetEnd(sal_Int32) override;       // SwTextAttr
 
     // get and set TextNode pointer
     inline const SwTextNode& GetTextNode() const;
diff --git a/sw/inc/txttxmrk.hxx b/sw/inc/txttxmrk.hxx
index a0c26987b707..bcb0a5b06acf 100644
--- a/sw/inc/txttxmrk.hxx
+++ b/sw/inc/txttxmrk.hxx
@@ -35,7 +35,8 @@ public:
             sal_Int32 const nStart, sal_Int32 const*const pEnd);
     virtual ~SwTextTOXMark() override;
 
-    virtual sal_Int32 *GetEnd() override;     // SwTextAttr
+    virtual const sal_Int32 *GetEnd() const override;     // SwTextAttr
+    virtual void SetEnd(sal_Int32) override;     // SwTextAttr
 
     void CopyTOXMark( SwDoc* pDestDoc );
 
diff --git a/sw/source/core/crsr/findattr.cxx b/sw/source/core/crsr/findattr.cxx
index 1af7577d2c58..d826878b4e23 100644
--- a/sw/source/core/crsr/findattr.cxx
+++ b/sw/source/core/crsr/findattr.cxx
@@ -726,8 +726,9 @@ static bool lcl_SearchForward( const SwTextNode& rTextNd, SwAttrCheckArr& rCmpAr
                 if( rCmpArr.GetNdStt() < pAttr->GetStart() )
                 {
                     // found end
+                    auto nTmpStart = pAttr->GetStart();
                     lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(),
-                                &pAttr->GetStart(), true );
+                                &nTmpStart, true );
                     return true;
                 }
                 // continue search
diff --git a/sw/source/core/fields/reffld.cxx b/sw/source/core/fields/reffld.cxx
index 526ceb5de12d..6c7efa8ce3c2 100644
--- a/sw/source/core/fields/reffld.cxx
+++ b/sw/source/core/fields/reffld.cxx
@@ -1116,7 +1116,7 @@ bool IsMarkHintHidden(SwRootFrame const& rLayout,
     {
         return true;
     }
-    sal_Int32 const*const pEnd(const_cast<SwTextAttrEnd &>(rHint).GetEnd());
+    sal_Int32 const*const pEnd(rHint.GetEnd());
     if (pEnd)
     {
         return pFrame->MapModelToView(&rNode, rHint.GetStart())
diff --git a/sw/source/core/txtnode/atrref.cxx b/sw/source/core/txtnode/atrref.cxx
index f0d5b7131769..6423c21cfaa1 100644
--- a/sw/source/core/txtnode/atrref.cxx
+++ b/sw/source/core/txtnode/atrref.cxx
@@ -95,9 +95,16 @@ SwTextRefMark::SwTextRefMark( SwFormatRefMark& rAttr,
     SetOverlapAllowedAttr( true );
 }
 
-sal_Int32* SwTextRefMark::GetEnd()
+const sal_Int32* SwTextRefMark::GetEnd() const
 {
     return m_pEnd;
 }
 
+void SwTextRefMark::SetEnd(sal_Int32 n)
+{
+    *m_pEnd = n;
+    if (m_pHints)
+        m_pHints->EndPosChanged();
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/atrtox.cxx b/sw/source/core/txtnode/atrtox.cxx
index f7e0fee43569..0569cc7e03fc 100644
--- a/sw/source/core/txtnode/atrtox.cxx
+++ b/sw/source/core/txtnode/atrtox.cxx
@@ -48,11 +48,18 @@ SwTextTOXMark::~SwTextTOXMark()
 {
 }
 
-sal_Int32* SwTextTOXMark::GetEnd()
+const sal_Int32* SwTextTOXMark::GetEnd() const
 {
     return m_pEnd;
 }
 
+void SwTextTOXMark::SetEnd(sal_Int32 n)
+{
+    *m_pEnd = n;
+    if (m_pHints)
+        m_pHints->EndPosChanged();
+}
+
 void SwTextTOXMark::CopyTOXMark( SwDoc* pDoc )
 {
     SwTOXMark& rTOX = const_cast<SwTOXMark&>(GetTOXMark());
diff --git a/sw/source/core/txtnode/ndhints.cxx b/sw/source/core/txtnode/ndhints.cxx
index a68c33878f4c..00647e23cd1f 100644
--- a/sw/source/core/txtnode/ndhints.cxx
+++ b/sw/source/core/txtnode/ndhints.cxx
@@ -19,6 +19,7 @@
 
 #include <editeng/rsiditem.hxx>
 #include <sal/log.hxx>
+#include <svl/eitem.hxx>
 #include <txatbase.hxx>
 #include <ndhints.hxx>
 #include <txtatr.hxx>
@@ -31,8 +32,10 @@
 
 /// sort order: Start, End (reverse), Which (reverse),
 /// (char style: sort number), at last the pointer
-static bool lcl_IsLessStart( const SwTextAttr &rHt1, const SwTextAttr &rHt2 )
+static bool CompareSwpHtStart( const SwTextAttr* lhs, const SwTextAttr* rhs )
 {
+    const SwTextAttr &rHt1 = *lhs;
+    const SwTextAttr &rHt2 = *rhs;
     if ( rHt1.GetStart() == rHt2.GetStart() )
     {
         const sal_Int32 nHt1 = *rHt1.GetAnyEnd();
@@ -65,8 +68,10 @@ static bool lcl_IsLessStart( const SwTextAttr &rHt1, const SwTextAttr &rHt2 )
 
 /// sort order: End (reverse), Start, Which (reverse),
 /// (char style: sort number), at last the pointer
-static bool lcl_IsLessEnd( const SwTextAttr &rHt1, const SwTextAttr &rHt2 )
+bool CompareSwpHtEnd( const SwTextAttr* lhs, const SwTextAttr* rhs )
 {
+    const SwTextAttr &rHt1 = *lhs;
+    const SwTextAttr &rHt2 = *rhs;
     const sal_Int32 nHt1 = *rHt1.GetAnyEnd();
     const sal_Int32 nHt2 = *rHt2.GetAnyEnd();
     if ( nHt1 == nHt2 )
@@ -98,25 +103,23 @@ static bool lcl_IsLessEnd( const SwTextAttr &rHt1, const SwTextAttr &rHt2 )
     return ( nHt1 < nHt2 );
 }
 
-bool CompareSwpHtStart::operator()(SwTextAttr const * const lhs, SwTextAttr const * const rhs) const
-{
-  return lcl_IsLessStart( *lhs, *rhs );
-}
-
-bool CompareSwpHtEnd::operator()(SwTextAttr const * const lhs, SwTextAttr const * const rhs) const
-{
-  return lcl_IsLessEnd( *lhs, *rhs );
-}
-
 void SwpHints::Insert( const SwTextAttr *pHt )
 {
-    Resort();
-    assert(m_HintsByStart.find(const_cast<SwTextAttr*>(pHt))
+    assert(std::find(m_HintsByStart.begin(), m_HintsByStart.end(), const_cast<SwTextAttr*>(pHt))
             == m_HintsByStart.end()); // "Insert: hint already in HtStart"
-    assert(m_HintsByEnd.find(const_cast<SwTextAttr*>(pHt))
-            == m_HintsByEnd.end());   // "Insert: hint already in HtEnd"
-    m_HintsByStart.insert( const_cast<SwTextAttr*>(pHt) );
-    m_HintsByEnd  .insert( const_cast<SwTextAttr*>(pHt) );
+    assert( pHt->m_pHints == nullptr );
+    const_cast<SwTextAttr*>(pHt)->m_pHints = this;
+
+    if (m_bStartMapNeedsSorting)
+        ResortStartMap();
+    if (m_bEndMapNeedsSorting)
+        ResortEndMap();
+
+    auto it1 = std::lower_bound(m_HintsByStart.begin(), m_HintsByStart.end(), const_cast<SwTextAttr*>(pHt), CompareSwpHtStart);
+    m_HintsByStart.insert(it1, const_cast<SwTextAttr*>(pHt));
+
+    auto it2 = std::lower_bound(m_HintsByEnd.begin(), m_HintsByEnd.end(), const_cast<SwTextAttr*>(pHt), CompareSwpHtEnd);
+    m_HintsByEnd.insert(it2, const_cast<SwTextAttr*>(pHt));
 }
 
 bool SwpHints::Contains( const SwTextAttr *pHt ) const
@@ -124,14 +127,8 @@ bool SwpHints::Contains( const SwTextAttr *pHt ) const
     // DO NOT use find() or CHECK here!
     // if called from SwTextNode::InsertItem, pHt has already been deleted,
     // so it cannot be dereferenced
-    for (size_t i = 0; i < m_HintsByStart.size(); ++i)
-    {
-        if (m_HintsByStart[i] == pHt)
-        {
-            return true;
-        }
-    }
-    return false;
+    return std::find(m_HintsByStart.begin(), m_HintsByStart.end(), pHt)
+        != m_HintsByStart.end();
 }
 
 #ifdef DBG_UTIL
@@ -140,7 +137,7 @@ bool SwpHints::Contains( const SwTextAttr *pHt ) const
         if(!(cond)) \
         { \
             SAL_WARN("sw.core", text); \
-            (const_cast<SwpHints*>(this))->Resort(); \
+            Resort(); \
             return false; \
         }
 
@@ -186,7 +183,7 @@ bool SwpHints::Check(bool bPortionsMerged) const
 
         // 4a) IsLessStart consistency
         if( pLastStart )
-            CHECK_ERR( lcl_IsLessStart( *pLastStart, *pHt ), "HintsCheck: IsLastStart" );
+            CHECK_ERR( CompareSwpHtStart( pLastStart, pHt ), "HintsCheck: IsLastStart" );
 
         nLastStart = nIdx;
         pLastStart = pHt;
@@ -203,7 +200,7 @@ bool SwpHints::Check(bool bPortionsMerged) const
 
         // 4b) IsLessEnd consistency
         if( pLastEnd )
-            CHECK_ERR( lcl_IsLessEnd( *pLastEnd, *pHtEnd ), "HintsCheck: IsLastEnd" );
+            CHECK_ERR( CompareSwpHtEnd( pLastEnd, pHtEnd ), "HintsCheck: IsLastEnd" );
 
         nLastEnd = nIdx;
         pLastEnd = pHtEnd;
@@ -211,13 +208,13 @@ bool SwpHints::Check(bool bPortionsMerged) const
         // --- cross checks ---
 
         // 5) same pointers in both arrays
-        if (m_HintsByStart.find(const_cast<SwTextAttr*>(pHt)) == m_HintsByStart.end())
+        if (std::lower_bound(m_HintsByStart.begin(), m_HintsByStart.end(), const_cast<SwTextAttr*>(pHt), CompareSwpHtStart) == m_HintsByStart.end())
             nIdx = COMPLETE_STRING;
 
         CHECK_ERR( COMPLETE_STRING != nIdx, "HintsCheck: no GetStartOf" );
 
         // 6) same pointers in both arrays
-        if (m_HintsByEnd.find(const_cast<SwTextAttr*>(pHt)) == m_HintsByEnd.end())
+        if (std::lower_bound(m_HintsByEnd.begin(), m_HintsByEnd.end(), const_cast<SwTextAttr*>(pHt), CompareSwpHtEnd) == m_HintsByEnd.end())
             nIdx = COMPLETE_STRING;
 
         CHECK_ERR( COMPLETE_STRING != nIdx, "HintsCheck: no GetEndOf" );
@@ -368,10 +365,48 @@ bool SwpHints::Check(bool bPortionsMerged) const
 // sort order of the m_HintsByStart, m_HintsByEnd arrays, so this method is needed
 // to restore the order.
 
-void SwpHints::Resort()
+void SwpHints::Resort() const
+{
+    auto & rStartMap = const_cast<SwpHints*>(this)->m_HintsByStart;
+    std::sort(rStartMap.begin(), rStartMap.end(), CompareSwpHtStart);
+    auto & rEndMap = const_cast<SwpHints*>(this)->m_HintsByEnd;
+    std::sort(rEndMap.begin(), rEndMap.end(), CompareSwpHtEnd);
+    m_bStartMapNeedsSorting = false;
+    m_bEndMapNeedsSorting = false;
+}
+
+void SwpHints::ResortStartMap() const
+{
+    auto & rStartMap = const_cast<SwpHints*>(this)->m_HintsByStart;
+    std::sort(rStartMap.begin(), rStartMap.end(), CompareSwpHtStart);
+    m_bStartMapNeedsSorting = false;
+}
+
+void SwpHints::ResortEndMap() const
+{
+    auto & rEndMap = const_cast<SwpHints*>(this)->m_HintsByEnd;
+    std::sort(rEndMap.begin(), rEndMap.end(), CompareSwpHtEnd);
+    m_bEndMapNeedsSorting = false;
+}
+
+int SwpHints::GetLastPosSortedByEnd( sal_Int32 nEndPos ) const
+{
+    if (m_bEndMapNeedsSorting)
+        ResortEndMap();
+    SfxBoolItem aFindItem(0);
+    SwTextAttrEnd aTmp(aFindItem, nEndPos, nEndPos);
+    auto it = std::upper_bound(m_HintsByEnd.begin(), m_HintsByEnd.end(), &aTmp, CompareSwpHtEnd);
+    return it - m_HintsByEnd.begin() - 1;
+}
+
+size_t SwpHints::GetIndexOf( const SwTextAttr *pHt ) const
 {
-    m_HintsByStart.Resort();
-    m_HintsByEnd.Resort();
+    if (m_bStartMapNeedsSorting)
+        ResortStartMap();
+    auto it = std::lower_bound(m_HintsByStart.begin(), m_HintsByStart.end(), const_cast<SwTextAttr*>(pHt), CompareSwpHtStart);
+    if ( it == m_HintsByStart.end() || *it != pHt )
+        return SAL_MAX_SIZE;
+    return it - m_HintsByStart.begin();
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/ndtxt.cxx b/sw/source/core/txtnode/ndtxt.cxx
index 532dec392920..4135b308fcc0 100644
--- a/sw/source/core/txtnode/ndtxt.cxx
+++ b/sw/source/core/txtnode/ndtxt.cxx
@@ -1190,31 +1190,30 @@ void SwTextNode::Update(
             {
                 bool bTextAttrChanged = false;
                 bool bStartOfTextAttrChanged = false;
-                SwTextAttr * const pHint = m_pSwpHints->Get(n);
-                sal_Int32 &       rStart = pHint->GetStart();
-                if ( rStart > nChangePos )
+                SwTextAttr * const pHint = m_pSwpHints->GetWithoutResorting(n);
+                if ( pHint->GetStart() > nChangePos )
                 {
-                    if ( rStart > nChangeEnd )
+                    if ( pHint->GetStart() > nChangeEnd )
                     {
-                         rStart = rStart - nChangeLen;
+                         pHint->SetStart( pHint->GetStart() - nChangeLen );
                     }
                     else
                     {
-                         rStart = nChangePos;
+                         pHint->SetStart( nChangePos );
                     }
                     bStartOfTextAttrChanged = true;
                 }
 
-                sal_Int32 * const pEnd = pHint->GetEnd();
+                const sal_Int32 * const pEnd = pHint->GetEnd();
                 if (pEnd && *pEnd > nChangePos )
                 {
                     if( *pEnd > nChangeEnd )
                     {
-                        *pEnd = *pEnd - nChangeLen;
+                        pHint->SetEnd(*pEnd - nChangeLen);
                     }
                     else
                     {
-                        *pEnd = nChangePos;
+                        pHint->SetEnd(nChangePos);
                     }
                     bTextAttrChanged = !bStartOfTextAttrChanged;
                 }
@@ -1251,22 +1250,21 @@ void SwTextNode::Update(
             for ( size_t n = 0; n < m_pSwpHints->Count(); ++n )
             {
                 bool bTextAttrChanged = false;
-                SwTextAttr * const pHint = m_pSwpHints->Get(n);
-                sal_Int32 &       rStart = pHint->GetStart();
-                sal_Int32 * const pEnd = pHint->GetEnd();
-                if ( rStart >= nChangePos )
+                SwTextAttr * const pHint = m_pSwpHints->GetWithoutResorting(n);
+                const sal_Int32 * const pEnd = pHint->GetEnd();
+                if ( pHint->GetStart() >= nChangePos )
                 {
-                    rStart = rStart + nChangeLen;
+                    pHint->SetStart( pHint->GetStart() + nChangeLen );
                     if ( pEnd )
                     {
-                        *pEnd = *pEnd + nChangeLen;
+                        pHint->SetEnd(*pEnd + nChangeLen);
                     }
                 }
                 else if ( pEnd && (*pEnd >= nChangePos) )
                 {
                     if ( (*pEnd > nChangePos) || IsIgnoreDontExpand() )
                     {
-                        *pEnd = *pEnd + nChangeLen;
+                        pHint->SetEnd(*pEnd + nChangeLen);
                         bTextAttrChanged = true;
                     }
                     else // *pEnd == nChangePos
@@ -1321,7 +1319,7 @@ void SwTextNode::Update(
                         }
                         else
                         {
-                            *pEnd = *pEnd + nChangeLen;
+                            pHint->SetEnd(*pEnd + nChangeLen);
                             bTextAttrChanged = true;
                         }
                     }
@@ -1616,12 +1614,13 @@ bool SwTextNode::DontExpandFormat( const SwIndex& rIdx, bool bFlag,
     bool bRet = false;
     if ( HasHints() )
     {
+        m_pSwpHints->SortIfNeedBe();
         const size_t nEndCnt = m_pSwpHints->Count();
         size_t nPos = nEndCnt;
         while( nPos )
         {
             SwTextAttr *pTmp = m_pSwpHints->GetSortedByEnd( --nPos );
-            sal_Int32 *pEnd = pTmp->GetEnd();
+            const sal_Int32 *pEnd = pTmp->GetEnd();
             if( !pEnd || *pEnd > nIdx )
                 continue;
             if( nIdx != *pEnd )
@@ -1660,7 +1659,9 @@ lcl_GetTextAttrs(
     enum SwTextNode::GetTextAttrMode const eMode)
 {
     assert(nWhich >= RES_TXTATR_BEGIN && nWhich < RES_TXTATR_END);
-    size_t const nSize = pSwpHints ? pSwpHints->Count() : 0;
+    if (!pSwpHints)
+        return;
+    size_t const nSize = pSwpHints->Count();
     sal_Int32 nPreviousIndex(0); // index of last hint with nWhich
     bool (*pMatchFunc)(sal_Int32, sal_Int32, sal_Int32)=nullptr;
     switch (eMode)
@@ -2324,13 +2325,14 @@ OUString SwTextNode::InsertText( const OUString & rStr, const SwIndex & rIdx,
 
     if ( HasHints() )
     {
+        m_pSwpHints->SortIfNeedBe();
         bool const bHadHints(!m_pSwpHints->CanBeDeleted());
         bool bMergePortionsNeeded(false);
         for ( size_t i = 0; i < m_pSwpHints->Count() &&
-                rIdx >= m_pSwpHints->Get(i)->GetStart(); ++i )
+                rIdx >= m_pSwpHints->GetWithoutResorting(i)->GetStart(); ++i )
         {
-            SwTextAttr * const pHt = m_pSwpHints->Get( i );
-            sal_Int32 * const pEndIdx = pHt->GetEnd();
+            SwTextAttr * const pHt = m_pSwpHints->GetWithoutResorting( i );
+            const sal_Int32 * const pEndIdx = pHt->GetEnd();
             if( !pEndIdx )
                 continue;
 
@@ -2340,11 +2342,11 @@ OUString SwTextNode::InsertText( const OUString & rStr, const SwIndex & rIdx,
                     (!(nMode & SwInsertFlags::FORCEHINTEXPAND)
                      && pHt->DontExpand()) )
                 {
+                    m_pSwpHints->DeleteAtPos(i);
                     // on empty attributes also adjust Start
                     if( rIdx == pHt->GetStart() )
-                        pHt->GetStart() = pHt->GetStart() - nLen;
-                    *pEndIdx = *pEndIdx - nLen;
-                    m_pSwpHints->DeleteAtPos(i);
+                        pHt->SetStart( pHt->GetStart() - nLen );
+                    pHt->SetEnd(*pEndIdx - nLen);
                     // could be that pHt has IsFormatIgnoreEnd set, and it's
                     // not a RSID-only hint - now we have the inserted text
                     // between pHt and its continuation... which we don't know.
@@ -2359,9 +2361,9 @@ OUString SwTextNode::InsertText( const OUString & rStr, const SwIndex & rIdx,
                 else if ( (nMode & SwInsertFlags::EMPTYEXPAND)
                         && (*pEndIdx == pHt->GetStart()) )
                 {
-                    pHt->GetStart() = pHt->GetStart() - nLen;
-                    const size_t nCurrentLen = m_pSwpHints->Count();
                     m_pSwpHints->DeleteAtPos(i);
+                    pHt->SetStart( pHt->GetStart() - nLen );
+                    const size_t nCurrentLen = m_pSwpHints->Count();
                     InsertHint( pHt/* AUTOSTYLES:, SetAttrMode::NOHINTADJUST*/ );
                     if ( nCurrentLen > m_pSwpHints->Count() && i )
                     {
@@ -2380,7 +2382,7 @@ OUString SwTextNode::InsertText( const OUString & rStr, const SwIndex & rIdx,
             {
                 // no field, at paragraph start, HintExpand
                 m_pSwpHints->DeleteAtPos(i);
-                pHt->GetStart() = pHt->GetStart() - nLen;
+                pHt->SetStart( pHt->GetStart() - nLen );
                 // no effect on format ignore flags here (para start)
                 InsertHint( pHt, SetAttrMode::NOHINTADJUST );
             }
@@ -2576,14 +2578,13 @@ void SwTextNode::CutImpl( SwTextNode * const pDest, const SwIndex & rDestStart,
                 {
                     bMergePortionsNeeded = true;
                 }
-                pHt->GetStart() =
-                        nDestStart + (nAttrStartIdx - nTextStartIdx);
+                pHt->SetStart(nDestStart + (nAttrStartIdx - nTextStartIdx));
                 if (pEndIdx)
                 {
-                    *pHt->GetEnd() = nDestStart + (
+                    pHt->SetEnd( nDestStart + (
                                     *pEndIdx > nEnd
                                         ? nLen
-                                        : *pEndIdx - nTextStartIdx );
+                                        : *pEndIdx - nTextStartIdx ) );
                 }
                 pDest->InsertHint( pHt,
                           SetAttrMode::NOTXTATRCHR
@@ -2644,7 +2645,8 @@ void SwTextNode::CutImpl( SwTextNode * const pDest, const SwIndex & rDestStart,
 
         for (SwTextAttr* pHt : aArr)
         {
-            pHt->GetStart() = *pHt->GetEnd() = rStart.GetIndex();
+            pHt->SetStart( rStart.GetIndex() );
+            pHt->SetEnd( rStart.GetIndex() );
             InsertHint( pHt );
         }
     }
diff --git a/sw/source/core/txtnode/thints.cxx b/sw/source/core/txtnode/thints.cxx
index b7b80cbfb23c..3a1584098813 100644
--- a/sw/source/core/txtnode/thints.cxx
+++ b/sw/source/core/txtnode/thints.cxx
@@ -100,6 +100,8 @@ SwpHints::SwpHints(const SwTextNode& rParent)
     , m_bHiddenByParaField(false)
     , m_bFootnote(false)
     , m_bDDEFields(false)
+    , m_bStartMapNeedsSorting(false)
+    , m_bEndMapNeedsSorting(false)
 {
 }
 
@@ -270,7 +272,7 @@ lcl_DoSplitNew(NestList_t & rSplits, SwTextNode & rNode,
             (bSplitAtStart && bOtherDummy) ? nSplitPos + 1 : nSplitPos );
         SwTextAttrNesting * const pNew( MakeTextAttrNesting(
                 rNode, **iter, nStartPos, *(*iter)->GetEnd() ) );
-        *(*iter)->GetEnd() = nSplitPos;
+        (*iter)->SetEnd(nSplitPos);
         rSplits.insert(iter + 1, pNew);
     }
 }
@@ -425,9 +427,8 @@ SwpHints::TryInsertNesting( SwTextNode & rNode, SwTextAttrNesting & rNewHint )
                         // should be corrected because it may lead to problems
                         // in SwXMeta::createEnumeration
                         // SplitNew is sorted, so this is the first split
-                        sal_Int32& rStart(SplitNew.front()->GetStart());
-                        assert(rStart == nNewStart);
-                        rStart = nNewStart + 1;
+                        assert(SplitNew.front()->GetStart() == nNewStart);
+                        SplitNew.front()->SetStart(nNewStart + 1);
                     }
                 }
             }
@@ -476,7 +477,7 @@ SwpHints::TryInsertNesting( SwTextNode & rNode, SwTextAttrNesting & rNewHint )
                 case SwComparePosition::OverlapBefore:
                     {
                         Delete( rpOther ); // this also does NoteInHistory!
-                        rpOther->GetStart() = nSplitNewEnd;
+                        rpOther->SetStart(nSplitNewEnd);
                         InsertNesting( *rpOther );
                         if (!bRemoveOverlap)
                         {
@@ -495,7 +496,7 @@ SwpHints::TryInsertNesting( SwTextNode & rNode, SwTextAttrNesting & rNewHint )
                 case SwComparePosition::OverlapBehind:
                     {
                         Delete( rpOther ); // this also does NoteInHistory!
-                        *rpOther->GetEnd() = nSplitNewStart;
+                        rpOther->SetEnd(nSplitNewStart);
                         InsertNesting( *rpOther );
                         if (!bRemoveOverlap)
                         {
@@ -561,7 +562,7 @@ SwpHints::TryInsertNesting( SwTextNode & rNode, SwTextAttrNesting & rNewHint )
             }
             if (nOtherStart < nNewStart)
             {
-                *rpOther->GetEnd() = nNewStart;
+                rpOther->SetEnd(nNewStart);
                 bool const bSuccess( TryInsertNesting(rNode, *rpOther) );
                 SAL_WARN_IF(!bSuccess, "sw.core", "recursive call 2 failed?");
             }
@@ -604,7 +605,9 @@ void SwpHints::BuildPortions( SwTextNode& rNode, SwTextAttr& rNewHint,
     {
         for ( size_t i = 0; i < Count(); ++i )
         {
-            SwTextAttr* pOther = Get(i);
+            // we're modifying stuff here which affects the sorting, and we
+            // don't want it changing underneath us
+            SwTextAttr* pOther = GetWithoutResorting(i);
 
             if ( RES_TXTATR_CHARFMT != pOther->Which() &&
                  RES_TXTATR_AUTOFMT != pOther->Which() )
@@ -627,7 +630,7 @@ void SwpHints::BuildPortions( SwTextNode& rNode, SwTextAttr& rNewHint,
                 aInsDelHints.push_back( pNewAttr );
 
                 NoteInHistory( pOther );
-                pOther->GetStart() = nThisStart;
+                pOther->SetStart(nThisStart);
                 NoteInHistory( pOther, true );
 
                 nOtherStart = nThisStart;
@@ -647,7 +650,7 @@ void SwpHints::BuildPortions( SwTextNode& rNode, SwTextAttr& rNewHint,
                 aInsDelHints.push_back( pNewAttr );
 
                 NoteInHistory( pOther );
-                pOther->GetStart() = nThisEnd;
+                pOther->SetStart(nThisEnd);
                 NoteInHistory( pOther, true );
             }
         }
@@ -707,7 +710,8 @@ void SwpHints::BuildPortions( SwTextNode& rNode, SwTextAttr& rNewHint,
         // Get all hints that are in [nPorStart, nPorEnd[:
         for ( size_t i = 0; i < Count(); ++i )
         {
-            SwTextAttr *pOther = Get(i);
+            // we get called from TryInsertHint, which changes ordering
+            SwTextAttr *pOther = GetWithoutResorting(i);
 
             if ( RES_TXTATR_CHARFMT != pOther->Which() &&
                  RES_TXTATR_AUTOFMT != pOther->Which() )
@@ -1482,10 +1486,10 @@ bool SwTextNode::InsertHint( SwTextAttr * const pAttr, const SetAttrMode nMode )
             }
 
             // adjust end of hint to account for inserted CH_TXTATR
-            sal_Int32 * const pEnd(pAttr->GetEnd());
+            const sal_Int32 * const pEnd(pAttr->GetEnd());
             if (pEnd)
             {
-                *pEnd = *pEnd + 1;
+                pAttr->SetEnd(*pEnd + 1);
             }
         }
     }
@@ -1511,10 +1515,10 @@ bool SwTextNode::InsertHint( SwTextAttr * const pAttr, const SetAttrMode nMode )
                             + pTextInputField->GetFieldContent() + OUStringLiteral1(CH_TXT_ATR_INPUTFIELDEND);
                         InsertText( aContent, aIdx, nInsertFlags );
 
-                        sal_Int32* const pEnd(pAttr->GetEnd());
+                        const sal_Int32* const pEnd(pAttr->GetEnd());
                         assert(pEnd != nullptr);
-                        *pEnd = *pEnd + aContent.getLength();
-                        nEnd = *pEnd;
+                        pAttr->SetEnd(*pEnd + aContent.getLength());
+                        nEnd = *pAttr->GetEnd();
                     }
                     else
                     {
@@ -1524,21 +1528,21 @@ bool SwTextNode::InsertHint( SwTextAttr * const pAttr, const SetAttrMode nMode )
                             SwIndex aIdx( this, pAttr->GetStart() );
                             InsertText( OUString(CH_TXT_ATR_INPUTFIELDSTART), aIdx, nInsertFlags );
                             bInputFieldStartCharInserted = true;
-                            sal_Int32* const pEnd(pAttr->GetEnd());
+                            const sal_Int32* const pEnd(pAttr->GetEnd());
                             assert(pEnd != nullptr);
-                            *pEnd = *pEnd + 1;
-                            nEnd = *pEnd;
+                            pAttr->SetEnd(*pEnd + 1);
+                            nEnd = *pAttr->GetEnd();
                         }
 
-                        sal_Int32* const pEnd(pAttr->GetEnd());
+                        const sal_Int32* const pEnd(pAttr->GetEnd());
                         assert(pEnd != nullptr);
                         if (m_Text[ *pEnd - 1 ] != CH_TXT_ATR_INPUTFIELDEND)
                         {
                             SwIndex aIdx( this, *pEnd );
                             InsertText( OUString(CH_TXT_ATR_INPUTFIELDEND), aIdx, nInsertFlags );
                             bInputFieldEndCharInserted = true;
-                            *pEnd = *pEnd + 1;
-                            nEnd = *pEnd;
+                            pAttr->SetEnd(*pEnd + 1);
+                            nEnd = *pAttr->GetEnd();
                         }
                     }
                 }
@@ -1565,11 +1569,11 @@ bool SwTextNode::InsertHint( SwTextAttr * const pAttr, const SetAttrMode nMode )
             {
                 if ( pAttr->GetStart() > pTextInputField->GetStart() )
                 {
-                    pAttr->GetStart() = pTextInputField->GetStart();
+                    pAttr->SetStart( pTextInputField->GetStart() );
                 }
                 if ( *(pAttr->End()) < *(pTextInputField->End()) )
                 {
-                    *(pAttr->GetEnd()) = *(pTextInputField->End());
+                    pAttr->SetEnd(*(pTextInputField->End()));
                 }
             }
         }
@@ -2850,7 +2854,7 @@ bool SwpHints::MergePortions( SwTextNode& rNode )
             {
                 SwTextAttr *const p1 = aIter1->second.first;
                 NoteInHistory( p1 );
-                *p1->GetEnd() = nNewPortionEnd;
+                p1->SetEnd(nNewPortionEnd);
                 NoteInHistory( p1, true );
                 bRet = true;
             }
@@ -2948,7 +2952,7 @@ bool SwpHints::TryInsertHint(
         return false;
     }
 
-    sal_Int32 *pHtEnd = pHint->GetEnd();
+    const sal_Int32 *pHtEnd = pHint->GetEnd();
     const sal_uInt16 nWhich = pHint->Which();
     std::vector<sal_uInt16> aWhichSublist;
 
@@ -3094,8 +3098,8 @@ bool SwpHints::TryInsertHint(
             SwTextAttr* pTmpHt;
             for( size_t n = 0, nEnd = Count(); n < nEnd; ++n )
             {
-                sal_Int32 *pTmpHtEnd;
-                sal_Int32 *pTmpHintEnd;
+                const sal_Int32 *pTmpHtEnd;
+                const sal_Int32 *pTmpHintEnd;
                 if (RES_TXTATR_REFMARK == (pTmpHt = Get(n))->Which() &&
                     pHint->GetAttr() == pTmpHt->GetAttr() &&
                     nullptr != ( pTmpHtEnd = pTmpHt->GetEnd() ) &&
@@ -3120,9 +3124,11 @@ bool SwpHints::TryInsertHint(
                     }
 
                     if( bChgStart )
-                        pHint->GetStart() = pTmpHt->GetStart();
+                    {
+                        pHint->SetStart( pTmpHt->GetStart() );
+                    }
                     if( bChgEnd )
-                        *pTmpHintEnd = *pTmpHtEnd;
+                        pHint->SetEnd(*pTmpHtEnd);
 
                     if( bDelOld )
                     {
@@ -3190,8 +3196,8 @@ bool SwpHints::TryInsertHint(
         assert(*pHtEnd >= nHtStart);
 
         // just swap the nonsense:
-        pHint->GetStart() = *pHtEnd;
-        *pHtEnd = nHtStart;
+        pHint->SetStart(*pHtEnd);
+        pHint->SetEnd(nHtStart);
         nHtStart = pHint->GetStart();
     }
 
@@ -3270,6 +3276,7 @@ bool SwpHints::TryInsertHint(
 void SwpHints::DeleteAtPos( const size_t nPos )
 {
     SwTextAttr *pHint = Get(nPos);
+    assert( pHint->m_pHints == this );
     // ChainDelete( pHint );
     NoteInHistory( pHint );
 
@@ -3277,11 +3284,15 @@ void SwpHints::DeleteAtPos( const size_t nPos )
     SwTextAttr *pHt = m_HintsByStart[ nPos ];
     m_HintsByStart.erase( m_HintsByStart.begin() + nPos );
 
-    Resort();
+    if (m_bStartMapNeedsSorting)
+        ResortStartMap();
+    if (m_bEndMapNeedsSorting)
+        ResortEndMap();
 
-    bool const done = m_HintsByEnd.erase(pHt);
-    assert(done);
-    (void)done; // unused in NDEBUG
+    auto findIt = std::lower_bound(m_HintsByEnd.begin(), m_HintsByEnd.end(), pHt, CompareSwpHtEnd);
+    assert(*findIt == pHt);
+    m_HintsByEnd.erase(findIt);
+    pHt->m_pHints = nullptr;
 
     if( pHint->Which() == RES_TXTATR_FIELD )
     {
diff --git a/sw/source/core/txtnode/txatbase.cxx b/sw/source/core/txtnode/txatbase.cxx
index 8497116f1d7f..188ec6f9a663 100644
--- a/sw/source/core/txtnode/txatbase.cxx
+++ b/sw/source/core/txtnode/txatbase.cxx
@@ -46,11 +46,16 @@ SwTextAttr::~SwTextAttr() COVERITY_NOEXCEPT_FALSE
 {
 }
 
-sal_Int32* SwTextAttr::GetEnd()
+const sal_Int32* SwTextAttr::GetEnd() const
 {
     return nullptr;
 }
 
+void SwTextAttr::SetEnd(sal_Int32 )
+{
+    assert(false);
+}
+
 void SwTextAttr::Destroy( SwTextAttr * pToDestroy, SfxItemPool& rPool )
 {
     if (!pToDestroy) return;
@@ -70,11 +75,18 @@ SwTextAttrEnd::SwTextAttrEnd( SfxPoolItem& rAttr,
 {
 }
 
-sal_Int32* SwTextAttrEnd::GetEnd()
+const sal_Int32* SwTextAttrEnd::GetEnd() const
 {
     return & m_nEnd;
 }
 
+void SwTextAttrEnd::SetEnd(sal_Int32 n)
+{
+    m_nEnd = n;
+    if (m_pHints)
+        m_pHints->EndPosChanged();
+}
+
 void SwTextAttr::dumpAsXml(xmlTextWriterPtr pWriter) const
 {
     xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextAttr"));
diff --git a/sw/source/core/txtnode/txtedt.cxx b/sw/source/core/txtnode/txtedt.cxx
index d00dbf9c5358..d38c2978b751 100644
--- a/sw/source/core/txtnode/txtedt.cxx
+++ b/sw/source/core/txtnode/txtedt.cxx
@@ -409,18 +409,19 @@ void SwTextNode::RstTextAttr(
     std::vector<SwTextAttr *> delAttributes;
 
     // iterate over attribute array until start of attribute is behind deletion range
+    m_pSwpHints->SortIfNeedBe(); // trigger sorting now, we don't want it during iteration
     size_t i = 0;
     sal_Int32 nAttrStart = sal_Int32();
     SwTextAttr *pHt = nullptr;
     while ( (i < m_pSwpHints->Count())
-            && ( ( ( nAttrStart = m_pSwpHints->Get(i)->GetStart()) < nEnd )
+            && ( ( ( nAttrStart = m_pSwpHints->GetWithoutResorting(i)->GetStart()) < nEnd )
                  || nLen==0 ) && !bExactRange)
     {
-        pHt = m_pSwpHints->Get(i);
+        pHt = m_pSwpHints->GetWithoutResorting(i);
 
         // attributes without end stay in!
         // but consider <bInclRefToxMark> used by Undo
-        sal_Int32* const pAttrEnd = pHt->GetEnd();
+        const sal_Int32* const pAttrEnd = pHt->GetEnd();
         const bool bKeepAttrWithoutEnd =
             pAttrEnd == nullptr
             && ( !bInclRefToxMark
@@ -513,7 +514,7 @@ void SwTextNode::RstTextAttr(
                     bChanged = true;
                     m_pSwpHints->NoteInHistory( pHt );
                     // UGLY: this may temporarily destroy the sorting!
-                    pHt->GetStart() = nEnd;
+                    pHt->SetStart(nEnd);
                     m_pSwpHints->NoteInHistory( pHt, true );
 
                     if ( pStyleHandle.get() && nAttrStart < nEnd )
@@ -541,7 +542,7 @@ void SwTextNode::RstTextAttr(
 
                     m_pSwpHints->NoteInHistory( pHt );
                     // UGLY: this may temporarily destroy the sorting!
-                    *pAttrEnd = nStt;
+                    pHt->SetEnd(nStt);
                     m_pSwpHints->NoteInHistory( pHt, true );
 
                     if ( pStyleHandle.get() )
@@ -563,7 +564,7 @@ void SwTextNode::RstTextAttr(
                     const sal_Int32 nTmpEnd = *pAttrEnd;
                     m_pSwpHints->NoteInHistory( pHt );
                     // UGLY: this may temporarily destroy the sorting!
-                    *pAttrEnd = nStt;
+                    pHt->SetEnd(nStt);
                     m_pSwpHints->NoteInHistory( pHt, true );
 
                     if ( pStyleHandle.get() && nStt < nEnd )


More information about the Libreoffice-commits mailing list