sw/source/core/doc/docbm.cxx | 58 ++++++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 17 deletions(-)
New commits: commit 5850b22e04a7a055e5b4c6b23a1d32d74270506d Author: Bjoern Michaelsen <bjoern.michael...@libreoffice.org> AuthorDate: Sat Aug 3 02:31:03 2024 +0200 Commit: Bjoern Michaelsen <bjoern.michael...@libreoffice.org> CommitDate: Sun Aug 4 12:10:20 2024 +0200 speed up getInnerFieldmarkFor some more with bisectional and reverse search prerequisites: - the fieldmark vector is sorted by field start - a fieldmark start and end either have to both be in any other fieldmark, or both be outside of it strategy: - first we find the last fieldmark before our position (those behind it will never cover the position) - then iterate backwards to find the first that ends behind the position (and thus covers it) - the innermost fieldmark is now either the fieldmark found, or another fieldmark starting at the same position: * fieldmarks starting before the one we found will not be the innermost * thus we only need to iterate check to the first fieldmark starting earlier There are further optimizations, that likely arent worth it, e.g.: - sort the fieldmarks with the same start position by length or end position (longest first), this would remove the need for the last step Change-Id: I49164697a44ff0a5e11809dfba82029c160c5d2a Reviewed-on: https://gerrit.libreoffice.org/c/core/+/171442 Tested-by: Jenkins Reviewed-by: Bjoern Michaelsen <bjoern.michael...@libreoffice.org> diff --git a/sw/source/core/doc/docbm.cxx b/sw/source/core/doc/docbm.cxx index 19661ad37ff0..dab51bb1d39a 100644 --- a/sw/source/core/doc/docbm.cxx +++ b/sw/source/core/doc/docbm.cxx @@ -324,6 +324,13 @@ namespace } }; + struct CompareIMarkStartsAfterReverse + { + bool operator()(const sw::mark::IMark* pMark, SwPosition const& rPos) + { + return pMark->GetMarkStart() > rPos; + } + }; IMark* lcl_getMarkAfter(const MarkManager::container_t& rMarks, const SwPosition& rPos, bool bLoop) @@ -1475,27 +1482,44 @@ namespace sw::mark IFieldmark* MarkManager::getInnerFieldmarkFor(const SwPosition& rPos) const { - auto itFieldmark = find_if( - m_vFieldmarks.begin(), - m_vFieldmarks.end(), - [&rPos] (const ::sw::mark::MarkBase *const pMark) { return pMark->IsCoveringPosition(rPos); } ); - if (itFieldmark == m_vFieldmarks.end()) + // find the first mark starting on or before the position in reverse order + // (as we are reverse searching, this is the one closest to the position) + // m_vFieldmarks should be ordered by mark start, so we can bisect with lower_bound + auto itEnd = m_vFieldmarks.rend(); + auto itStart = lower_bound( + m_vFieldmarks.rbegin(), + itEnd, + rPos, + CompareIMarkStartsAfterReverse()); + // now continue a linear search for the first (still in reverse order) ending behind the position + auto itCurrent = find_if( + itStart, + itEnd, + [&rPos](const sw::mark::MarkBase* const pMark) { return rPos < pMark->GetMarkEnd(); }); + // if we reached the end (in reverse order) there is no match + if(itCurrent == itEnd) return nullptr; - auto pFieldmark(*itFieldmark); - - // See if any fieldmarks after the first hit are closer to rPos. - ++itFieldmark; - for ( ; itFieldmark != m_vFieldmarks.end() - && (**itFieldmark).GetMarkStart() <= rPos; ++itFieldmark) - { // find the innermost fieldmark - if (rPos < (**itFieldmark).GetMarkEnd() - && (pFieldmark->GetMarkStart() < (**itFieldmark).GetMarkStart() - || (**itFieldmark).GetMarkEnd() < pFieldmark->GetMarkEnd())) + // we found our first candidate covering the position ... + auto pMark = *itCurrent; + const auto aMarkStart = pMark->GetMarkStart(); + auto aMarkEnd = pMark->GetMarkEnd(); + // ... however we still need to check if there is a smaller/'more inner' one with the same start position + for(++itCurrent; itCurrent != itEnd; ++itCurrent) + { + if((*itCurrent)->GetMarkStart() < aMarkStart) + // any following mark (in reverse order) will have an earlier + // start and thus can not be more 'inner' than our previous + // match, so we are done. + break; + auto aCurrentMarkEnd = (*itCurrent)->GetMarkEnd(); + if(rPos < aCurrentMarkEnd && aCurrentMarkEnd <= aMarkEnd) { - pFieldmark = *itFieldmark; + // both covering the position and more inner/smaller => use this one instead + pMark = *itCurrent; + aMarkEnd = aCurrentMarkEnd; } } - return dynamic_cast<IFieldmark*>(pFieldmark); + return dynamic_cast<IFieldmark*>(pMark); } IMark* MarkManager::getOneInnermostBookmarkFor(const SwPosition& rPos) const