sc/inc/column.hxx                 |    5 
 sc/inc/document.hxx               |    5 
 sc/inc/queryiter.hxx              |  172 +++++++++---
 sc/inc/table.hxx                  |    5 
 sc/inc/types.hxx                  |    8 
 sc/source/core/data/queryiter.cxx |  530 +++++++++++++++++++-------------------
 sc/source/core/tool/interpr1.cxx  |   18 -
 7 files changed, 432 insertions(+), 311 deletions(-)

New commits:
commit 80606b89bbd38f63423eccfac4d1b8e6732b9560
Author:     Luboš Luňák <l.lu...@collabora.com>
AuthorDate: Wed May 4 19:46:57 2022 +0200
Commit:     Luboš Luňák <l.lu...@collabora.com>
CommitDate: Tue May 10 16:23:32 2022 +0200

    extend ScQueryCellIteratorBase for custom cell access
    
    This moves code related to accessing cells to query to a template
    base class. Creating more specialization of that class will allow
    queries that use a cache of sort order for the cells.
    
    Change-Id: I47d1bc2d038717ff64f1d11fa6c7dba2359fec11
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/134096
    Tested-by: Jenkins
    Reviewed-by: Luboš Luňák <l.lu...@collabora.com>

diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index 3f0785429ca9..feba105169e6 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -208,9 +208,10 @@ friend class ScTable;
 friend class ScValueIterator;
 friend class ScHorizontalValueIterator;
 friend class ScDBQueryDataIterator;
-template< ScQueryCellIteratorType >
+template< ScQueryCellIteratorAccess accessType, ScQueryCellIteratorType 
queryType >
 friend class ScQueryCellIteratorBase;
-friend class ScQueryCellIterator;
+template< ScQueryCellIteratorAccess accessType >
+friend class ScQueryCellIteratorAccessSpecific;
 friend class ScFormulaGroupIterator;
 friend class ScCellIterator;
 friend class ScHorizontalCellIterator;
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index 2518acd63773..2242ba2450ac 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -323,9 +323,10 @@ friend class ScHorizontalValueIterator;
 friend class ScDBQueryDataIterator;
 friend class ScFormulaGroupIterator;
 friend class ScCellIterator;
-template< ScQueryCellIteratorType >
+template< ScQueryCellIteratorAccess accessType, ScQueryCellIteratorType 
queryType >
 friend class ScQueryCellIteratorBase;
-friend class ScQueryCellIterator;
+template< ScQueryCellIteratorAccess accessType >
+friend class ScQueryCellIteratorAccessSpecific;
 friend class ScHorizontalCellIterator;
 friend class ScHorizontalAttrIterator;
 friend class ScDocAttrIterator;
diff --git a/sc/inc/queryiter.hxx b/sc/inc/queryiter.hxx
index f5cba350bff2..e247a471db65 100644
--- a/sc/inc/queryiter.hxx
+++ b/sc/inc/queryiter.hxx
@@ -26,21 +26,78 @@
 #include "mtvelements.hxx"
 #include "types.hxx"
 
-// Query-related iterators. There is one template class ScQueryCellIteratorBase
-// that implements most of the shared functionality, specific parts are done
-// by specializing the templates and then subclassing as the actual class to 
use.
+/*
+Query-related iterators. There is one template class ScQueryCellIteratorBase
+that implements most of the shared functionality, specific parts are done
+by specializing the templates and then subclassing as the actual class to use.
+A template is used for maximum performance, as that allows fast code 
specializing,
+inlining, etc.
+There are two template arguments:
+* ScQueryCellIteratorAccess specifies how cells are accessed:
+  + Direct - direct access to cells using mdds.
+  + SortedCache - for accessing unsorted cells in a sorted way using 
ScSortedRangeCache.
+* ScQueryCellIteratorType specifies the type of the query operation:
+  + Generic - the generic lookup, used e.g. by VLOOKUP.
+  + CountIf - faster implementation for COUNTIF(S).
 
-// Specific data should be in ScQueryCellIteratorSpecific (otherwise adding 
data
-// members here would mean specializing the entire ScQueryCellIteratorBase).
-template< ScQueryCellIteratorType iteratorType >
-class ScQueryCellIteratorSpecific
+Specific data should be in specific templated base classes, otherwise adding 
data
+members would mean specializing the entire ScQueryCellIteratorBase. Some 
specific
+functionality may also be implemented in the base classes or depending on the 
template
+parameter.
+*/
+
+// Data and functionality for accessing cells in a specific way.
+// Needs specialization, see ScQueryCellIteratorAccess::Direct for what is 
needed.
+template< ScQueryCellIteratorAccess accessType >
+class ScQueryCellIteratorAccessSpecific
 {
 };
 
-// Shared code for query-based iterators.
+// The implementation using linear direct mdds access.
+template<>
+class ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::Direct >
+{
+protected:
+    ScQueryCellIteratorAccessSpecific( ScDocument& rDocument, const 
ScQueryParam& rParam );
+    // Initialize position for new column.
+    void InitPos();
+    // Increase position (next row).
+    void IncPos();
+    // Next mdds block. If access is not direct/linear, then
+    // should call IncPos().
+    void IncBlock();
+
+    // These members needs to be available already in the base class.
+    typedef sc::CellStoreType::const_position_type PositionType;
+    PositionType maCurPos;
+    ScQueryParam    maParam;
+    ScDocument&     rDoc;
+    SCTAB           nTab;
+    SCCOL           nCol;
+    SCROW           nRow;
+
+    class NonEmptyCellIndexer;
+    typedef std::pair<ScRefCellValue, SCROW> BinarySearchCellType;
+    static NonEmptyCellIndexer MakeBinarySearchIndexer(const 
sc::CellStoreType& rCells,
+        SCROW nStartRow, SCROW nEndRow);
+};
+
+// Data and functionality for specific types of query.
 template< ScQueryCellIteratorType iteratorType >
-class ScQueryCellIteratorBase : public ScQueryCellIteratorSpecific< 
iteratorType >
+class ScQueryCellIteratorTypeSpecific
 {
+protected:
+    bool HandleItemFound(); // not implemented, needs specialization
+};
+
+// Shared code for query-based iterators. The main class.
+template< ScQueryCellIteratorAccess accessType, ScQueryCellIteratorType 
queryType >
+class ScQueryCellIteratorBase
+    : public ScQueryCellIteratorAccessSpecific< accessType >
+    , public ScQueryCellIteratorTypeSpecific< queryType >
+{
+    typedef ScQueryCellIteratorAccessSpecific< accessType > AccessBase;
+    typedef ScQueryCellIteratorTypeSpecific< queryType > TypeBase;
 protected:
     enum StopOnMismatchBits
     {
@@ -58,32 +115,29 @@ protected:
         nTestEqualConditionFulfilled = nTestEqualConditionEnabled | 
nTestEqualConditionMatched
     };
 
-    typedef sc::CellStoreType::const_position_type PositionType;
-    PositionType maCurPos;
-
-    ScQueryParam    maParam;
-    ScDocument&     rDoc;
     const ScInterpreterContext& mrContext;
-    SCTAB           nTab;
-    SCCOL           nCol;
-    SCROW           nRow;
     sal_uInt8            nStopOnMismatch;
     sal_uInt8            nTestEqualCondition;
     bool            bAdvanceQuery;
     bool            bIgnoreMismatchOnLeadingStrings;
 
-    /** Initialize position for new column. */
-    void InitPos();
-    void IncPos();
-    void IncBlock();
+    // Make base members directly visible here (templated bases need 'this->').
+    using AccessBase::maCurPos;
+    using AccessBase::maParam;
+    using AccessBase::rDoc;
+    using AccessBase::nTab;
+    using AccessBase::nCol;
+    using AccessBase::nRow;
+    using AccessBase::InitPos;
+    using AccessBase::IncPos;
+    using AccessBase::IncBlock;
+    using typename AccessBase::BinarySearchCellType;
+    using AccessBase::MakeBinarySearchIndexer;
+    using TypeBase::HandleItemFound;
 
     // The actual query function. It will call HandleItemFound() for any 
matching type
     // and return if HandleItemFound() returns true.
     void PerformQuery();
-    bool HandleItemFound(); // not implemented, needs specialization
-
-    SCCOL           GetCol() const { return nCol; }
-    SCROW           GetRow() const { return nRow; }
 
     /* Only works if no regular expression is involved, only searches for rows 
in one column,
        and only the first query entry is considered with simple conditions 
SC_LESS_EQUAL
@@ -137,24 +191,55 @@ public:
                         { return nTestEqualCondition == 
nTestEqualConditionFulfilled; }
 };
 
+
 template<>
-class ScQueryCellIteratorSpecific< ScQueryCellIteratorType::Generic >
+class ScQueryCellIteratorTypeSpecific< ScQueryCellIteratorType::Generic >
 {
 protected:
+    bool HandleItemFound();
     bool getThisResult;
 };
 
 // The generic query iterator, used e.g. by VLOOKUP.
-class ScQueryCellIterator : public ScQueryCellIteratorBase< 
ScQueryCellIteratorType::Generic >
+template< ScQueryCellIteratorAccess accessType >
+class ScQueryCellIterator
+    : public ScQueryCellIteratorBase< accessType, 
ScQueryCellIteratorType::Generic >
 {
+    typedef ScQueryCellIteratorBase< accessType, 
ScQueryCellIteratorType::Generic > Base;
+    // Make base members directly visible here (templated bases need 'this->').
+    using Base::maParam;
+    using Base::rDoc;
+    using Base::nTab;
+    using Base::nCol;
+    using Base::nRow;
+    using Base::InitPos;
+    using Base::IncPos;
+    using Base::bIgnoreMismatchOnLeadingStrings;
+    using Base::SetStopOnMismatch;
+    using Base::SetTestEqualCondition;
+    using Base::BinarySearch;
+    using typename Base::PositionType;
+    using Base::maCurPos;
+    using Base::IsEqualConditionFulfilled;
+    using Base::bAdvanceQuery;
+    using Base::StoppedOnMismatch;
+    using Base::nStopOnMismatch;
+    using Base::nStopOnMismatchEnabled;
+    using Base::nTestEqualCondition;
+    using Base::nTestEqualConditionEnabled;
+    using Base::PerformQuery;
+    using Base::getThisResult;
+
     bool GetThis();
 
 public:
-    using ScQueryCellIteratorBase::ScQueryCellIteratorBase;
+    ScQueryCellIterator(ScDocument& rDocument, const ScInterpreterContext& 
rContext, SCTAB nTable,
+                        const ScQueryParam& aParam, bool bMod)
+        : Base( rDocument, rContext, nTable, aParam, bMod ) {}
     bool GetFirst();
     bool GetNext();
-    using ScQueryCellIteratorBase::GetCol;
-    using ScQueryCellIteratorBase::GetRow;
+    SCCOL GetCol() const { return nCol; }
+    SCROW GetRow() const { return nRow; }
 
                     /** In a range assumed to be sorted find either the last of
                         a sequence of equal entries or the last being less than
@@ -177,20 +262,41 @@ public:
     bool            FindEqualOrSortedLastInRange( SCCOL& nFoundCol, SCROW& 
nFoundRow );
 };
 
+typedef ScQueryCellIterator< ScQueryCellIteratorAccess::Direct > 
ScQueryCellIteratorDirect;
+
 
 template<>
-class ScQueryCellIteratorSpecific< ScQueryCellIteratorType::CountIf >
+class ScQueryCellIteratorTypeSpecific< ScQueryCellIteratorType::CountIf >
 {
 protected:
+    bool HandleItemFound();
     sal_uInt64 countIfCount;
 };
 
 // Used by ScInterpreter::ScCountIf.
-class ScCountIfCellIterator : public ScQueryCellIteratorBase< 
ScQueryCellIteratorType::CountIf >
+template< ScQueryCellIteratorAccess accessType >
+class ScCountIfCellIterator
+    : public ScQueryCellIteratorBase< accessType, 
ScQueryCellIteratorType::CountIf >
 {
+    typedef ScQueryCellIteratorBase< accessType, 
ScQueryCellIteratorType::CountIf > Base;
+    // Make base members directly visible here (templated bases need 'this->').
+    using Base::maParam;
+    using Base::rDoc;
+    using Base::nTab;
+    using Base::nCol;
+    using Base::nRow;
+    using Base::InitPos;
+    using Base::PerformQuery;
+    using Base::SetAdvanceQueryParamEntryField;
+    using Base::countIfCount;
+
 public:
-    using ScQueryCellIteratorBase::ScQueryCellIteratorBase;
+    ScCountIfCellIterator(ScDocument& rDocument, const ScInterpreterContext& 
rContext, SCTAB nTable,
+                          const ScQueryParam& aParam, bool bMod)
+        : Base( rDocument, rContext, nTable, aParam, bMod ) {}
     sal_uInt64 GetCount();
 };
 
+typedef ScCountIfCellIterator< ScQueryCellIteratorAccess::Direct > 
ScCountIfCellIteratorDirect;
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index 64b94b0995f0..290dfcf74f3d 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -260,9 +260,10 @@ friend class ScHorizontalValueIterator;
 friend class ScDBQueryDataIterator;
 friend class ScFormulaGroupIterator;
 friend class ScCellIterator;
-template< ScQueryCellIteratorType >
+template< ScQueryCellIteratorAccess accessType, ScQueryCellIteratorType 
queryType >
 friend class ScQueryCellIteratorBase;
-friend class ScQueryCellIterator;
+template< ScQueryCellIteratorAccess accessType >
+friend class ScQueryCellIteratorAccessSpecific;
 friend class ScHorizontalCellIterator;
 friend class ScColumnTextWidthIterator;
 friend class ScDocumentImport;
diff --git a/sc/inc/types.hxx b/sc/inc/types.hxx
index ef027058cec1..e3a019da5008 100644
--- a/sc/inc/types.hxx
+++ b/sc/inc/types.hxx
@@ -133,10 +133,18 @@ namespace o3tl{
     template<> struct typed_flags<sc::MatrixEdge> : 
o3tl::is_typed_flags<sc::MatrixEdge, 63> {};
 }
 
+// Type of query done by ScQueryCellIteratorBase.
 enum class ScQueryCellIteratorType
 {
   Generic,
   CountIf
 };
 
+// Type of cell access done by ScQueryCellIteratorBase.
+enum class ScQueryCellIteratorAccess
+{
+  Direct, // Accessing directly cells.
+  SortedCache // Using ScSortedRangeCache.
+};
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/queryiter.cxx 
b/sc/source/core/data/queryiter.cxx
index cbdd83668920..ec88e35ff6a5 100644
--- a/sc/source/core/data/queryiter.cxx
+++ b/sc/source/core/data/queryiter.cxx
@@ -19,6 +19,7 @@
 
 #include <queryiter.hxx>
 
+#include <comphelper/flagguard.hxx>
 #include <svl/numformat.hxx>
 #include <svl/zforlist.hxx>
 
@@ -55,18 +56,17 @@
 #include <limits>
 #include <vector>
 
-template< ScQueryCellIteratorType iteratorType >
-ScQueryCellIteratorBase< iteratorType >::ScQueryCellIteratorBase(ScDocument& 
rDocument,
+template< ScQueryCellIteratorAccess accessType, ScQueryCellIteratorType 
queryType >
+ScQueryCellIteratorBase< accessType, queryType 
>::ScQueryCellIteratorBase(ScDocument& rDocument,
     const ScInterpreterContext& rContext, SCTAB nTable, const ScQueryParam& 
rParam, bool bMod )
-    : maParam(rParam)
-    , rDoc( rDocument )
+    : AccessBase( rDocument, rParam )
     , mrContext( rContext )
-    , nTab( nTable)
     , nStopOnMismatch( nStopOnMismatchDisabled )
     , nTestEqualCondition( nTestEqualConditionDisabled )
     , bAdvanceQuery( false )
     , bIgnoreMismatchOnLeadingStrings( false )
 {
+    nTab = nTable;
     nCol = maParam.nCol1;
     nRow = maParam.nRow1;
     SCSIZE i;
@@ -85,41 +85,8 @@ ScQueryCellIteratorBase< iteratorType 
>::ScQueryCellIteratorBase(ScDocument& rDo
     }
 }
 
-template< ScQueryCellIteratorType iteratorType >
-void ScQueryCellIteratorBase< iteratorType >::InitPos()
-{
-    nRow = maParam.nRow1;
-    if (maParam.bHasHeader && maParam.bByRow)
-        ++nRow;
-    const ScColumn& rCol = rDoc.maTabs[nTab]->CreateColumnIfNotExists(nCol);
-    maCurPos = rCol.maCells.position(nRow);
-}
-
-template< ScQueryCellIteratorType iteratorType >
-void ScQueryCellIteratorBase< iteratorType >::IncPos()
-{
-    if (maCurPos.second + 1 < maCurPos.first->size)
-    {
-        // Move within the same block.
-        ++maCurPos.second;
-        ++nRow;
-    }
-    else
-        // Move to the next block.
-        IncBlock();
-}
-
-template< ScQueryCellIteratorType iteratorType >
-void ScQueryCellIteratorBase< iteratorType >::IncBlock()
-{
-    ++maCurPos.first;
-    maCurPos.second = 0;
-
-    nRow = maCurPos.first->position;
-}
-
-template< ScQueryCellIteratorType iteratorType >
-void ScQueryCellIteratorBase< iteratorType >::PerformQuery()
+template< ScQueryCellIteratorAccess accessType, ScQueryCellIteratorType 
queryType >
+void ScQueryCellIteratorBase< accessType, queryType >::PerformQuery()
 {
     assert(nTab < rDoc.GetTableCount() && "index out of bounds, FIX IT");
     const ScQueryEntry& rEntry = maParam.GetEntry(0);
@@ -136,7 +103,7 @@ void ScQueryCellIteratorBase< iteratorType >::PerformQuery()
     bool bTestEqualCondition = false;
     ScQueryEvaluator queryEvaluator(rDoc, *rDoc.maTabs[nTab], maParam, 
&mrContext,
         (nTestEqualCondition ? &bTestEqualCondition : nullptr));
-    if( iteratorType == ScQueryCellIteratorType::CountIf )
+    if( queryType == ScQueryCellIteratorType::CountIf )
     {
         // These are not used for COUNTIF, so should not be set, make the 
compiler
         // explicitly aware of it so that the relevant parts are optimized 
away.
@@ -266,8 +233,8 @@ void ScQueryCellIteratorBase< iteratorType >::PerformQuery()
     }
 }
 
-template< ScQueryCellIteratorType iteratorType >
-void ScQueryCellIteratorBase< iteratorType >::AdvanceQueryParamEntryField()
+template< ScQueryCellIteratorAccess accessType, ScQueryCellIteratorType 
queryType >
+void ScQueryCellIteratorBase< accessType, queryType 
>::AdvanceQueryParamEntryField()
 {
     SCSIZE nEntries = maParam.GetEntryCount();
     for ( SCSIZE j = 0; j < nEntries; j++  )
@@ -305,205 +272,10 @@ void decBlock(std::pair<Iter, size_t>& rPos)
     rPos.second = rPos.first->size - 1;
 }
 
-/**
- * This class sequentially indexes non-empty cells in order, from the top of
- * the block where the start row position is, to the bottom of the block
- * where the end row position is.  It skips all empty blocks that may be
- * present in between.
- *
- * The index value is an offset from the first element of the first block
- * disregarding all empty cell blocks.
- */
-class NonEmptyCellIndexer
-{
-    typedef std::map<size_t, sc::CellStoreType::const_iterator> BlockMapType;
-
-    BlockMapType maBlockMap;
-
-    const sc::CellStoreType& mrCells;
-
-    size_t mnLowIndex;
-    size_t mnHighIndex;
-
-    bool mbValid;
-
-public:
-
-    typedef std::pair<ScRefCellValue, SCROW> CellType;
-
-    /**
-     * @param rCells cell storage container
-     * @param nStartRow logical start row position
-     * @param nEndRow logical end row position, inclusive.
-     * @param bSkipTopStrBlock when true, skip all leading string cells.
-     */
-    NonEmptyCellIndexer(
-        const sc::CellStoreType& rCells, SCROW nStartRow, SCROW nEndRow, bool 
bSkipTopStrBlock ) :
-        mrCells(rCells), mnLowIndex(0), mnHighIndex(0), mbValid(true)
-    {
-        if (nEndRow < nStartRow)
-        {
-            mbValid = false;
-            return;
-        }
-
-        // Find the low position.
-
-        sc::CellStoreType::const_position_type aLoPos = 
mrCells.position(nStartRow);
-        if (aLoPos.first->type == sc::element_type_empty)
-            incBlock(aLoPos);
-
-        if (aLoPos.first == rCells.end())
-        {
-            mbValid = false;
-            return;
-        }
-
-        if (bSkipTopStrBlock)
-        {
-            // Skip all leading string or empty blocks.
-            while (aLoPos.first->type == sc::element_type_string ||
-                   aLoPos.first->type == sc::element_type_edittext ||
-                   aLoPos.first->type == sc::element_type_empty)
-            {
-                incBlock(aLoPos);
-                if (aLoPos.first == rCells.end())
-                {
-                    mbValid = false;
-                    return;
-                }
-            }
-        }
-
-        SCROW nFirstRow = aLoPos.first->position;
-        SCROW nLastRow = aLoPos.first->position + aLoPos.first->size - 1;
-
-        if (nFirstRow > nEndRow)
-        {
-            // Both start and end row positions are within the leading skipped
-            // blocks.
-            mbValid = false;
-            return;
-        }
-
-        // Calculate the index of the low position.
-        if (nFirstRow < nStartRow)
-            mnLowIndex = nStartRow - nFirstRow;
-        else
-        {
-            // Start row is within the skipped block(s). Set it to the first
-            // element of the low block.
-            mnLowIndex = 0;
-        }
-
-        if (nEndRow < nLastRow)
-        {
-            assert(nEndRow >= nFirstRow);
-            mnHighIndex = nEndRow - nFirstRow;
-
-            maBlockMap.emplace(aLoPos.first->size, aLoPos.first);
-            return;
-        }
-
-        // Find the high position.
-
-        sc::CellStoreType::const_position_type aHiPos = 
mrCells.position(aLoPos.first, nEndRow);
-        if (aHiPos.first->type == sc::element_type_empty)
-        {
-            // Move to the last position of the previous block.
-            decBlock(aHiPos);
-
-            // Check the row position of the end of the previous block, and 
make sure it's valid.
-            SCROW nBlockEndRow = aHiPos.first->position + aHiPos.first->size - 
1;
-            if (nBlockEndRow < nStartRow)
-            {
-                mbValid = false;
-                return;
-            }
-        }
-
-        // Tag the start and end blocks, and all blocks in between in order
-        // but skip all empty blocks.
-
-        size_t nPos = 0;
-        sc::CellStoreType::const_iterator itBlk = aLoPos.first;
-        while (itBlk != aHiPos.first)
-        {
-            if (itBlk->type == sc::element_type_empty)
-            {
-                ++itBlk;
-                continue;
-            }
-
-            nPos += itBlk->size;
-            maBlockMap.emplace(nPos, itBlk);
-            ++itBlk;
-
-            if (itBlk->type == sc::element_type_empty)
-                ++itBlk;
-
-            assert(itBlk != mrCells.end());
-        }
-
-        assert(itBlk == aHiPos.first);
-        nPos += itBlk->size;
-        maBlockMap.emplace(nPos, itBlk);
-
-        // Calculate the high index.
-        BlockMapType::const_reverse_iterator ri = maBlockMap.rbegin();
-        mnHighIndex = ri->first;
-        mnHighIndex -= ri->second->size;
-        mnHighIndex += aHiPos.second;
-    }
-
-    sc::CellStoreType::const_position_type getPosition( size_t nIndex ) const
-    {
-        assert(mbValid);
-        assert(mnLowIndex <= nIndex);
-        assert(nIndex <= mnHighIndex);
-
-        sc::CellStoreType::const_position_type aRet(mrCells.end(), 0);
-
-        BlockMapType::const_iterator it = maBlockMap.upper_bound(nIndex);
-        if (it == maBlockMap.end())
-            return aRet;
-
-        sc::CellStoreType::const_iterator itBlk = it->second;
-        size_t nBlkIndex = it->first - itBlk->size; // index of the first 
element of the block.
-        assert(nBlkIndex <= nIndex);
-        assert(nIndex < it->first);
-
-        size_t nOffset = nIndex - nBlkIndex;
-        aRet.first = itBlk;
-        aRet.second = nOffset;
-        return aRet;
-    }
-
-    CellType getCell( size_t nIndex ) const
-    {
-        std::pair<ScRefCellValue, SCROW> aRet;
-        aRet.second = -1;
-
-        sc::CellStoreType::const_position_type aPos = getPosition(nIndex);
-        if (aPos.first == mrCells.end())
-            return aRet;
-
-        aRet.first = sc::toRefCell(aPos.first, aPos.second);
-        aRet.second = aPos.first->position + aPos.second;
-        return aRet;
-    }
-
-    size_t getLowIndex() const { return mnLowIndex; }
-
-    size_t getHighIndex() const { return mnHighIndex; }
-
-    bool isValid() const { return mbValid; }
-};
-
 }
 
-template< ScQueryCellIteratorType iteratorType >
-bool ScQueryCellIteratorBase< iteratorType >::BinarySearch()
+template< ScQueryCellIteratorAccess accessType, ScQueryCellIteratorType 
queryType >
+bool ScQueryCellIteratorBase< accessType, queryType >::BinarySearch()
 {
     assert(maParam.GetEntry(0).bDoQuery && !maParam.GetEntry(1).bDoQuery
         && maParam.GetEntry(0).GetQueryItems().size() == 1 );
@@ -556,13 +328,34 @@ bool ScQueryCellIteratorBase< iteratorType 
>::BinarySearch()
         }
     }
 
-    NonEmptyCellIndexer aIndexer(pCol->maCells, nRow, maParam.nRow2, 
bAllStringIgnore);
+    // Skip leading empty block, if any.
+    sc::CellStoreType::const_position_type startPos = 
pCol->maCells.position(nRow);
+    if (startPos.first->type == sc::element_type_empty)
+        incBlock(startPos);
+    if(bAllStringIgnore)
+    {
+        // Skip all leading string or empty blocks.
+        while (startPos.first != pCol->maCells.end()
+            && (startPos.first->type == sc::element_type_string ||
+                startPos.first->type == sc::element_type_edittext ||
+                startPos.first->type == sc::element_type_empty))
+        {
+            incBlock(startPos);
+        }
+    }
+    if(startPos.first == pCol->maCells.end())
+        return false;
+    nRow = startPos.first->position + startPos.second;
+    if (nRow > maParam.nRow2)
+        return false;
+
+    auto aIndexer = MakeBinarySearchIndexer(pCol->maCells, nRow, 
maParam.nRow2);
     if (!aIndexer.isValid())
         return false;
 
     size_t nLo = aIndexer.getLowIndex();
     size_t nHi = aIndexer.getHighIndex();
-    NonEmptyCellIndexer::CellType aCellData;
+    BinarySearchCellType aCellData;
 
     // Bookkeeping values for breaking up the binary search in case the data
     // range isn't strictly sorted.
@@ -780,19 +573,12 @@ bool ScQueryCellIteratorBase< iteratorType 
>::BinarySearch()
 }
 
 
-bool ScQueryCellIterator::FindEqualOrSortedLastInRange( SCCOL& nFoundCol,
+template< ScQueryCellIteratorAccess accessType >
+bool ScQueryCellIterator< accessType >::FindEqualOrSortedLastInRange( SCCOL& 
nFoundCol,
         SCROW& nFoundRow )
 {
-    // Set and automatically reset mpParam->mbRangeLookup when returning. We
-    // could use comphelper::FlagRestorationGuard, but really, that one is
-    // overengineered for this simple purpose here.
-    struct BoolResetter
-    {
-        bool& mr;
-        bool  mb;
-        BoolResetter( bool& r, bool b ) : mr(r), mb(r) { r = b; }
-        ~BoolResetter() { mr = mb; }
-    } aRangeLookupResetter( maParam.mbRangeLookup, true);
+    // Set and automatically reset mpParam->mbRangeLookup when returning.
+    comphelper::FlagRestorationGuard aRangeLookupResetter( 
maParam.mbRangeLookup, true );
 
     nFoundCol = rDoc.MaxCol()+1;
     nFoundRow = rDoc.MaxRow()+1;
@@ -969,21 +755,233 @@ bool ScQueryCellIterator::FindEqualOrSortedLastInRange( 
SCCOL& nFoundCol,
     return (nFoundCol <= rDoc.MaxCol()) && (nFoundRow <= rDoc.MaxRow());
 }
 
-template<>
-bool ScQueryCellIteratorBase< ScQueryCellIteratorType::Generic 
>::HandleItemFound()
+// Direct linear cell access using mdds.
+
+ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::Direct >
+    ::ScQueryCellIteratorAccessSpecific( ScDocument& rDocument, const 
ScQueryParam& rParam )
+    : maParam( rParam )
+    , rDoc( rDocument )
+{
+}
+
+void ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::Direct 
>::InitPos()
+{
+    nRow = maParam.nRow1;
+    if (maParam.bHasHeader && maParam.bByRow)
+        ++nRow;
+    const ScColumn& rCol = rDoc.maTabs[nTab]->CreateColumnIfNotExists(nCol);
+    maCurPos = rCol.maCells.position(nRow);
+}
+
+void ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::Direct 
>::IncPos()
+{
+    if (maCurPos.second + 1 < maCurPos.first->size)
+    {
+        // Move within the same block.
+        ++maCurPos.second;
+        ++nRow;
+    }
+    else
+        // Move to the next block.
+        IncBlock();
+}
+
+void ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::Direct 
>::IncBlock()
+{
+    ++maCurPos.first;
+    maCurPos.second = 0;
+
+    nRow = maCurPos.first->position;
+}
+
+/**
+ * This class sequentially indexes non-empty cells in order, from the top of
+ * the block where the start row position is, to the bottom of the block
+ * where the end row position is.  It skips all empty blocks that may be
+ * present in between.
+ *
+ * The index value is an offset from the first element of the first block
+ * disregarding all empty cell blocks.
+ */
+class ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::Direct 
>::NonEmptyCellIndexer
+{
+    typedef std::map<size_t, sc::CellStoreType::const_iterator> BlockMapType;
+
+    BlockMapType maBlockMap;
+
+    const sc::CellStoreType& mrCells;
+
+    size_t mnLowIndex;
+    size_t mnHighIndex;
+
+    bool mbValid;
+
+public:
+    /**
+     * @param rCells cell storage container
+     * @param nStartRow logical start row position
+     * @param nEndRow logical end row position, inclusive.
+     */
+    NonEmptyCellIndexer(
+        const sc::CellStoreType& rCells, SCROW nStartRow, SCROW nEndRow) :
+        mrCells(rCells), mnLowIndex(0), mnHighIndex(0), mbValid(true)
+    {
+        // Find the low position.
+
+        sc::CellStoreType::const_position_type aLoPos = 
mrCells.position(nStartRow);
+        assert(aLoPos.first->type != sc::element_type_empty);
+        assert(aLoPos.first != rCells.end());
+
+        SCROW nFirstRow = aLoPos.first->position;
+        SCROW nLastRow = aLoPos.first->position + aLoPos.first->size - 1;
+
+        if (nFirstRow > nEndRow)
+        {
+            // Both start and end row positions are within the leading skipped
+            // blocks.
+            mbValid = false;
+            return;
+        }
+
+        // Calculate the index of the low position.
+        if (nFirstRow < nStartRow)
+            mnLowIndex = nStartRow - nFirstRow;
+        else
+        {
+            // Start row is within the skipped block(s). Set it to the first
+            // element of the low block.
+            mnLowIndex = 0;
+        }
+
+        if (nEndRow < nLastRow)
+        {
+            assert(nEndRow >= nFirstRow);
+            mnHighIndex = nEndRow - nFirstRow;
+
+            maBlockMap.emplace(aLoPos.first->size, aLoPos.first);
+            return;
+        }
+
+        // Find the high position.
+
+        sc::CellStoreType::const_position_type aHiPos = 
mrCells.position(aLoPos.first, nEndRow);
+        if (aHiPos.first->type == sc::element_type_empty)
+        {
+            // Move to the last position of the previous block.
+            decBlock(aHiPos);
+
+            // Check the row position of the end of the previous block, and 
make sure it's valid.
+            SCROW nBlockEndRow = aHiPos.first->position + aHiPos.first->size - 
1;
+            if (nBlockEndRow < nStartRow)
+            {
+                mbValid = false;
+                return;
+            }
+        }
+
+        // Tag the start and end blocks, and all blocks in between in order
+        // but skip all empty blocks.
+
+        size_t nPos = 0;
+        sc::CellStoreType::const_iterator itBlk = aLoPos.first;
+        while (itBlk != aHiPos.first)
+        {
+            if (itBlk->type == sc::element_type_empty)
+            {
+                ++itBlk;
+                continue;
+            }
+
+            nPos += itBlk->size;
+            maBlockMap.emplace(nPos, itBlk);
+            ++itBlk;
+
+            if (itBlk->type == sc::element_type_empty)
+                ++itBlk;
+
+            assert(itBlk != mrCells.end());
+        }
+
+        assert(itBlk == aHiPos.first);
+        nPos += itBlk->size;
+        maBlockMap.emplace(nPos, itBlk);
+
+        // Calculate the high index.
+        BlockMapType::const_reverse_iterator ri = maBlockMap.rbegin();
+        mnHighIndex = ri->first;
+        mnHighIndex -= ri->second->size;
+        mnHighIndex += aHiPos.second;
+    }
+
+    sc::CellStoreType::const_position_type getPosition( size_t nIndex ) const
+    {
+        assert(mbValid);
+        assert(mnLowIndex <= nIndex);
+        assert(nIndex <= mnHighIndex);
+
+        sc::CellStoreType::const_position_type aRet(mrCells.end(), 0);
+
+        BlockMapType::const_iterator it = maBlockMap.upper_bound(nIndex);
+        if (it == maBlockMap.end())
+            return aRet;
+
+        sc::CellStoreType::const_iterator itBlk = it->second;
+        size_t nBlkIndex = it->first - itBlk->size; // index of the first 
element of the block.
+        assert(nBlkIndex <= nIndex);
+        assert(nIndex < it->first);
+
+        size_t nOffset = nIndex - nBlkIndex;
+        aRet.first = itBlk;
+        aRet.second = nOffset;
+        return aRet;
+    }
+
+    BinarySearchCellType getCell( size_t nIndex ) const
+    {
+        BinarySearchCellType aRet;
+        aRet.second = -1;
+
+        sc::CellStoreType::const_position_type aPos = getPosition(nIndex);
+        if (aPos.first == mrCells.end())
+            return aRet;
+
+        aRet.first = sc::toRefCell(aPos.first, aPos.second);
+        aRet.second = aPos.first->position + aPos.second;
+        return aRet;
+    }
+
+    size_t getLowIndex() const { return mnLowIndex; }
+
+    size_t getHighIndex() const { return mnHighIndex; }
+
+    bool isValid() const { return mbValid; }
+};
+
+ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::Direct 
>::NonEmptyCellIndexer
+ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::Direct 
>::MakeBinarySearchIndexer(
+    const sc::CellStoreType& rCells, SCROW nStartRow, SCROW nEndRow )
+{
+    return NonEmptyCellIndexer(rCells, nStartRow, nEndRow);
+}
+
+// Generic query implementation.
+
+bool ScQueryCellIteratorTypeSpecific< ScQueryCellIteratorType::Generic 
>::HandleItemFound()
 {
     getThisResult = true;
     return true; // Return from PerformQuery().
 }
 
-bool ScQueryCellIterator::GetThis()
+template< ScQueryCellIteratorAccess accessType >
+bool ScQueryCellIterator< accessType >::GetThis()
 {
     getThisResult = false;
     PerformQuery();
     return getThisResult;
 }
 
-bool ScQueryCellIterator::GetFirst()
+template< ScQueryCellIteratorAccess accessType >
+bool ScQueryCellIterator< accessType >::GetFirst()
 {
     assert(nTab < rDoc.GetTableCount() && "index out of bounds, FIX IT");
     nCol = maParam.nCol1;
@@ -991,7 +989,8 @@ bool ScQueryCellIterator::GetFirst()
     return GetThis();
 }
 
-bool ScQueryCellIterator::GetNext()
+template< ScQueryCellIteratorAccess accessType >
+bool ScQueryCellIterator< accessType >::GetNext()
 {
     IncPos();
     if ( nStopOnMismatch )
@@ -1001,15 +1000,16 @@ bool ScQueryCellIterator::GetNext()
     return GetThis();
 }
 
+// Countifs implementation.
 
-template<>
-bool ScQueryCellIteratorBase< ScQueryCellIteratorType::CountIf 
>::HandleItemFound()
+bool ScQueryCellIteratorTypeSpecific< ScQueryCellIteratorType::CountIf 
>::HandleItemFound()
 {
     ++countIfCount;
     return false; // Continue searching.
 }
 
-sal_uInt64 ScCountIfCellIterator::GetCount()
+template< ScQueryCellIteratorAccess accessType >
+sal_uInt64 ScCountIfCellIterator< accessType >::GetCount()
 {
     // Keep Entry.nField in iterator on column change
     SetAdvanceQueryParamEntryField( true );
@@ -1023,7 +1023,11 @@ sal_uInt64 ScCountIfCellIterator::GetCount()
     return countIfCount;
 }
 
-template class ScQueryCellIteratorBase< ScQueryCellIteratorType::Generic >;
-template class ScQueryCellIteratorBase< ScQueryCellIteratorType::CountIf >;
+template class ScQueryCellIterator< ScQueryCellIteratorAccess::Direct >;
+template class ScCountIfCellIterator< ScQueryCellIteratorAccess::Direct >;
+
+// gcc for some reason needs these too
+template class ScQueryCellIteratorBase< ScQueryCellIteratorAccess::Direct, 
ScQueryCellIteratorType::Generic >;
+template class ScQueryCellIteratorBase< ScQueryCellIteratorAccess::Direct, 
ScQueryCellIteratorType::CountIf >;
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/tool/interpr1.cxx b/sc/source/core/tool/interpr1.cxx
index c3f095cfa526..f6dbbffa2d3f 100644
--- a/sc/source/core/tool/interpr1.cxx
+++ b/sc/source/core/tool/interpr1.cxx
@@ -5049,7 +5049,7 @@ void ScInterpreter::ScMatch()
             rParam.bByRow = false;
             rParam.nRow2 = nRow1;
             rEntry.nField = nCol1;
-            ScQueryCellIterator aCellIter(mrDoc, mrContext, nTab1, rParam, 
false);
+            ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, nTab1, 
rParam, false);
             // Advance Entry.nField in Iterator if column changed
             aCellIter.SetAdvanceQueryParamEntryField( true );
             if (fTyp == 0.0)
@@ -5533,7 +5533,7 @@ void ScInterpreter::IterateParametersIf( ScIterFuncIf 
eFunc )
             }
             else
             {
-                ScQueryCellIterator aCellIter(mrDoc, mrContext, nTab1, rParam, 
false);
+                ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, nTab1, 
rParam, false);
                 // Increment Entry.nField in iterator when switching to next 
column.
                 aCellIter.SetAdvanceQueryParamEntryField( true );
                 if ( aCellIter.GetFirst() )
@@ -5786,7 +5786,7 @@ void ScInterpreter::ScCountIf()
             }
             else
             {
-                ScCountIfCellIterator aCellIter(mrDoc, mrContext, nTab1, 
rParam, false);
+                ScCountIfCellIteratorDirect aCellIter(mrDoc, mrContext, nTab1, 
rParam, false);
                 fCount += aCellIter.GetCount();
             }
         }
@@ -6140,7 +6140,7 @@ void ScInterpreter::IterateParametersIfs( 
double(*ResultFunc)( const sc::ParamIf
             }
             else
             {
-                ScQueryCellIterator aCellIter(mrDoc, mrContext, nTab1, rParam, 
false);
+                ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, nTab1, 
rParam, false);
                 // Increment Entry.nField in iterator when switching to next 
column.
                 aCellIter.SetAdvanceQueryParamEntryField( true );
                 if ( aCellIter.GetFirst() )
@@ -7074,7 +7074,7 @@ void ScInterpreter::ScLookup()
     if (rItem.meType == ScQueryEntry::ByString)
         aParam.eSearchType = DetectSearchType(rItem.maString.getString(), 
mrDoc);
 
-    ScQueryCellIterator aCellIter(mrDoc, mrContext, nTab1, aParam, false);
+    ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, nTab1, aParam, 
false);
     SCCOL nC;
     SCROW nR;
     // Advance Entry.nField in iterator upon switching columns if
@@ -7421,7 +7421,7 @@ void ScInterpreter::CalculateLookup(bool bHLookup)
             rEntry.eOp = SC_LESS_EQUAL;
         if ( bHLookup )
         {
-            ScQueryCellIterator aCellIter(mrDoc, mrContext, nTab1, aParam, 
false);
+            ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, nTab1, 
aParam, false);
             // advance Entry.nField in Iterator upon switching columns
             aCellIter.SetAdvanceQueryParamEntryField( true );
             if ( bSorted )
@@ -7891,11 +7891,11 @@ void ScInterpreter::ScDBCount()
             ScDBQueryParamInternal* p = 
static_cast<ScDBQueryParamInternal*>(pQueryParam.get());
             p->nCol2 = p->nCol1; // Don't forget to select only one column.
             SCTAB nTab = p->nTab;
-            // ScQueryCellIterator doesn't make use of 
ScDBQueryParamBase::mnField,
+            // ScQueryCellIteratorDirect doesn't make use of 
ScDBQueryParamBase::mnField,
             // so the source range has to be restricted, like before the 
introduction
             // of ScDBQueryParamBase.
             p->nCol1 = p->nCol2 = p->mnField;
-            ScQueryCellIterator aCellIter(mrDoc, mrContext, nTab, *p, true);
+            ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, nTab, *p, 
true);
             if ( aCellIter.GetFirst() )
             {
                 do
@@ -9942,7 +9942,7 @@ static bool lcl_LookupQuery( ScAddress & o_rResultPos, 
ScDocument& rDoc, const S
         const ScQueryParam & rParam, const ScQueryEntry & rEntry )
 {
     bool bFound = false;
-    ScQueryCellIterator aCellIter( rDoc, rContext, rParam.nTab, rParam, false);
+    ScQueryCellIteratorDirect aCellIter( rDoc, rContext, rParam.nTab, rParam, 
false);
     if (rEntry.eOp != SC_EQUAL)
     {
         // range lookup <= or >=

Reply via email to