/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
if (maCurPos.first->type == sc::element_type_empty)
{ if (rItem.mbMatchEmpty && bSingleQueryItem)
{ // This shortcut, instead of determining if any SC_OR query // exists or this query is SC_AND'ed (which wouldn't make // sense, but..) and evaluating them in ValidQuery(), is // possible only because the interpreter is the only caller // that sets mbMatchEmpty and there is only one item in those // cases. // XXX this would have to be reworked if other filters used it // in different manners and evaluation would have to be done in // ValidQuery(). if(HandleItemFound()) return;
!mbReverseSearch ? IncPos() : DecPos(); continue;
} else
{
!mbReverseSearch ? IncBlock() : DecBlock(); continue;
}
}
ScQueryEvaluator queryEvaluatorTmp(rDoc, *rDoc.maTabs[nTab], aParamTmp, &mrContext, nullptr, bNewSearchFunction); if (queryEvaluatorTmp.ValidQuery(nRow, (nCol == static_cast<SCCOL>(nFirstQueryField) ? &aCell : nullptr)))
HandleBestFitItemFound(nCol, nRow); else
{
!mbReverseSearch ? IncPos() : DecPos(); continue;
}
}
} else
{
!mbReverseSearch ? IncPos() : DecPos(); continue;
}
} if (HandleItemFound()) return;
!mbReverseSearch ? IncPos() : DecPos(); continue;
} elseif ( nStopOnMismatch )
{ // Yes, even a mismatch may have a fulfilled equal // condition if regular expressions were involved and // SC_LESS_EQUAL or SC_GREATER_EQUAL were queried. if ( nTestEqualCondition && bTestEqualCondition )
{
nTestEqualCondition |= nTestEqualConditionMatched;
nStopOnMismatch |= nStopOnMismatchOccurred; return;
} bool bStop; if (bFirstStringIgnore)
{ if (aCell.hasString())
{
!mbReverseSearch ? IncPos() : DecPos();
bStop = false;
} else
bStop = true;
} else
bStop = true; if (bStop)
{
nStopOnMismatch |= nStopOnMismatchOccurred; return;
}
} else
!mbReverseSearch ? IncPos() : DecPos();
}
bFirstStringIgnore = false;
}
}
template< ScQueryCellIteratorAccess accessType, ScQueryCellIteratorType queryType > void ScQueryCellIteratorBase< accessType, queryType >::InitPos()
{ if constexpr( accessType != ScQueryCellIteratorAccess::SortedCache )
AccessBase::InitPos(); else
{ // This should be all in AccessBase::InitPos(), but that one can't call // BinarySearch(), so do it this way instead. bool bNewSearchFunction = nSearchOpCode == SC_OPCODE_X_LOOKUP || nSearchOpCode == SC_OPCODE_X_MATCH;
AccessBase::InitPosStart(bNewSearchFunction, nSortedBinarySearch);
ScQueryOp& op = maParam.GetEntry(0).eOp;
SCCOLROW beforeColRow = -1;
SCCOLROW lastColRow = -1; if( op == SC_EQUAL )
{ if( BinarySearch( maParam.bByRow ? nCol : nRow) )
{ // BinarySearch() searches for the last item that matches. Now we // also need to find the first item where to start. Find the last // non-matching position using SC_LESS and the start position // is the one after it.
lastColRow = maParam.bByRow ? nRow : nCol;
ScQueryOp saveOp = op;
op = SC_LESS; if( BinarySearch(maParam.bByRow ? nCol : nRow, true) )
beforeColRow = maParam.bByRow ? nRow : nCol; // If BinarySearch() returns false, there was no match, which means // there's no value smaller. In that case BinarySearch() has set // the position to the first row in the range.
op = saveOp; // back to SC_EQUAL
} elseif( maParam.GetEntry(0).GetQueryItem().mbMatchEmpty
&& rDoc.IsEmptyData(maParam.nCol1, maParam.nRow1, maParam.nCol2, maParam.nRow2, nTab))
{ // BinarySearch() returns false in case it's all empty data, // handle that specially.
lastColRow = maParam.nRow2;
} if (maParam.bByRow)
AccessBase::InitPosFinish(beforeColRow, lastColRow, false/*bFirstMatch*/); else
AccessBase::InitPosColFinish(beforeColRow, lastColRow, false/*bFirstMatch*/);
} else
{ // The range is from the start up to and including the last matching. if( BinarySearch( maParam.bByRow ? nCol : nRow) )
{
lastColRow = maParam.bByRow ? nRow : nCol; if (nSearchOpCode == SC_OPCODE_X_LOOKUP || nSearchOpCode == SC_OPCODE_X_MATCH)
{
ScQueryOp saveOp = op;
op = SC_LESS; if( BinarySearch(maParam.bByRow ? nCol : nRow, true) )
beforeColRow = maParam.bByRow ? nRow : nCol;
op = saveOp;
}
} if ((nSearchOpCode == SC_OPCODE_X_LOOKUP || nSearchOpCode == SC_OPCODE_X_MATCH) &&
(lastColRow == beforeColRow || beforeColRow == -1))
{
beforeColRow = -1; if (maParam.bByRow)
AccessBase::InitPosFinish(beforeColRow, lastColRow, true/*bFirstMatch*/); else
{
AccessBase::InitPosColFinish(beforeColRow, lastColRow, true/*bFirstMatch*/);
AdvanceQueryParamEntryFieldForBinarySearch();
}
} else
{ if (maParam.bByRow)
AccessBase::InitPosFinish(beforeColRow, lastColRow, false/*bFirstMatch*/); else
{
AccessBase::InitPosColFinish(beforeColRow, lastColRow, false/*bFirstMatch*/);
AdvanceQueryParamEntryFieldForBinarySearch();
}
}
}
}
}
template<typename Iter> void incBlock(std::pair<Iter, size_t>& rPos)
{ // Move to the next block.
++rPos.first;
rPos.second = 0;
}
template<typename Iter> void decBlock(std::pair<Iter, size_t>& rPos)
{ // Move to the last element of the previous block.
--rPos.first;
rPos.second = rPos.first->size - 1;
}
// Bookkeeping values for breaking up the binary search in case the data // range isn't strictly sorted.
size_t nLastInRange = nLo; double fLastInRangeValue = bAscending ?
-(::std::numeric_limits<double>::max()) :
::std::numeric_limits<double>::max();
OUString aLastInRangeString; if (!bAscending)
aLastInRangeString = OUString(u'\xFFFF');
if (maParam.bByRow)
aCellData = aIndexer.getCell(nLastInRange); else
aCellData = aIndexer.getColCell(nLastInRange, nRow);
bool isInRange; if (orderBroken)
{ // Reset position to the first row in range and force caller // to search from start.
nLo = aIndexer.getLowIndex();
isInRange = false;
} elseif (found)
{
nLo = *found;
isInRange = true;
} else
{ // Not nothing was found and the search position is at the start, // then the possible match would need to be before the data range. // In that case return false to force the caller to search from the start // and detect this.
isInRange = nLo != aIndexer.getLowIndex(); // If nothing was found, that is either because there is no value // that would match exactly, or the data range is not properly sorted // and we failed to detect (doing so reliably would require a linear scan). // Set the position to the last one that was in matching range (i.e. before // where the exact match would be), and leave sorting it out to GetThis() // or whatever the caller uses.
nLo = nLastInRange;
}
if (maParam.bByRow)
{
aCellData = aIndexer.getCell(nLo); if (nLo <= nHi && aCellData.second <= maParam.nRow2)
{
nRow = aCellData.second;
maCurPos = aIndexer.getPosition(nLo); return isInRange;
} else
{
nRow = maParam.nRow2 + 1; // Set current position to the last possible row.
maCurPos.first = pCol->maCells.end();
--maCurPos.first;
maCurPos.second = maCurPos.first->size - 1; returnfalse;
}
} else
{
aCellData = aIndexer.getColCell(nLo, nRow); if (nLo <= nHi && aCellData.second <= maParam.nCol2)
{
nCol = aCellData.second;
maCurPos = aIndexer.getColPosition(nLo, nRow); return isInRange;
} else
{
nCol = maParam.nCol2 + 1; // Set current position to the last possible col.
pCol = &(rDoc.maTabs[nTab])->aCol[maParam.nCol2];
maCurPos.first = pCol->maCells.end();
--maCurPos.first;
maCurPos.second = maCurPos.first->size - 1; returnfalse;
}
}
}
template< ScQueryCellIteratorAccess accessType > bool ScQueryCellIterator< accessType >::FindEqualOrSortedLastInRange( SCCOL& nFoundCol,
SCROW& nFoundRow )
{ // Set and automatically reset mpParam->mbRangeLookup when returning.
comphelper::FlagRestorationGuard aRangeLookupResetter( maParam.mbRangeLookup, true );
// assume not sorted properly if we are using XLookup/XMatch with forward or backward search if (bBinary && (nSearchOpCode == SC_OPCODE_X_LOOKUP || nSearchOpCode == SC_OPCODE_X_MATCH) &&
nSortedBinarySearch == nBinarySearchDisabled)
bBinary = false;
bool bFound = false; if (bBinary)
{ if (BinarySearch( maParam.nCol1 ))
{ // BinarySearch() already positions correctly and only needs real // query comparisons afterwards, skip the verification check below.
maParam.mbRangeLookup = false;
bFound = GetThis();
} else// Not sorted properly, or before the range (in which case GetFirst() will be simple).
bFound = GetFirst();
} else
{
bFound = GetFirst();
} if (bFound)
{ // First equal entry or last smaller than (greater than) entry.
PositionType aPosSave; bool bNext = false;
SCSIZE nEntries = maParam.GetEntryCount();
std::vector<SCCOL> aFoundFieldPositions(nEntries); do
{
nFoundCol = GetCol();
nFoundRow = GetRow();
aPosSave = maCurPos; // If we might need to rewind below, save the position to rewind to // rather than calculate it as a diff between nCol and nFoundCol as // PerformQuery can return early if nCol is greater than // maParam.nCol2 or AllocatedColumns if (maParam.mbRangeLookup && bAdvanceQuery)
{ for (SCSIZE j=0; j < nEntries; ++j)
{
ScQueryEntry& rEntry = maParam.GetEntry( j ); if (rEntry.bDoQuery)
aFoundFieldPositions[j] = maParam.GetEntry(j).nField; else break; // for
}
} if (IsEqualConditionFulfilled()) break;
bNext = GetNext();
} while (bNext);
// There may be no pNext but equal condition fulfilled if regular // expressions are involved. Keep the found entry and proceed. if (!bNext && !IsEqualConditionFulfilled())
{ // Step back to last in range and adjust position markers for // GetNumberFormat() or similar. bool bColDiff = nCol != nFoundCol;
nCol = nFoundCol;
nRow = nFoundRow;
maCurPos = std::move(aPosSave); if (maParam.mbRangeLookup)
{ // Verify that the found entry does not only fulfill the range // lookup but also the real query, i.e. not numeric was found // if query is ByString and vice versa.
maParam.mbRangeLookup = false; // Step back the last field advance if GetNext() did one. if (bAdvanceQuery && bColDiff)
{ for (SCSIZE j=0; j < nEntries; ++j)
{
ScQueryEntry& rEntry = maParam.GetEntry( j ); if (rEntry.bDoQuery)
{
rEntry.nField = aFoundFieldPositions[j];
assert(rEntry.nField >= 0);
} else break; // for
}
} // Check it. if (!GetThis())
{
nFoundCol = rDoc.MaxCol()+1;
nFoundRow = rDoc.MaxRow()+1;
}
}
}
} if (IsEqualConditionFulfilled() && (nSearchOpCode != SC_OPCODE_X_LOOKUP &&
nSearchOpCode != SC_OPCODE_X_MATCH))
{ // Position on last equal entry, except for XLOOKUP, // which looking for the first equal entry
SCSIZE nEntries = maParam.GetEntryCount(); for ( SCSIZE j = 0; j < nEntries; j++ )
{
ScQueryEntry& rEntry = maParam.GetEntry( j ); if ( rEntry.bDoQuery )
{ switch ( rEntry.eOp )
{ case SC_LESS_EQUAL : case SC_GREATER_EQUAL :
rEntry.eOp = SC_EQUAL; break; default:
{ // added to avoid warnings
}
}
} else break; // for
}
PositionType aPosSave;
bIgnoreMismatchOnLeadingStrings = false;
SetTestEqualCondition( false ); do
{
nFoundCol = GetCol();
nFoundRow = GetRow();
aPosSave = maCurPos;
} while (GetNext());
// Step back conditions are the same as above
nCol = nFoundCol;
nRow = nFoundRow;
maCurPos = std::move(aPosSave); returntrue;
} if ( (maParam.eSearchType != utl::SearchParam::SearchType::Normal) &&
StoppedOnMismatch() )
{ // Assume found entry to be the last value less than respectively // greater than the query. But keep on searching for an equal match.
SCSIZE nEntries = maParam.GetEntryCount(); for ( SCSIZE j = 0; j < nEntries; j++ )
{
ScQueryEntry& rEntry = maParam.GetEntry( j ); if ( rEntry.bDoQuery )
{ switch ( rEntry.eOp )
{ case SC_LESS_EQUAL : case SC_GREATER_EQUAL :
rEntry.eOp = SC_EQUAL; break; default:
{ // added to avoid warnings
}
}
} else break; // for
}
SetStopOnMismatch( false );
SetTestEqualCondition( false ); if (GetNext())
{ // Last of a consecutive area, avoid searching the entire parameter // range as it is a real performance bottleneck in case of regular // expressions.
PositionType aPosSave; do
{
nFoundCol = GetCol();
nFoundRow = GetRow();
aPosSave = maCurPos;
SetStopOnMismatch( true );
} while (GetNext());
nCol = nFoundCol;
nRow = nFoundRow;
maCurPos = std::move(aPosSave);
}
} return (nFoundCol <= rDoc.MaxCol()) && (nFoundRow <= rDoc.MaxRow());
}
// Direct linear cell access using mdds.
ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::Direct >
::ScQueryCellIteratorAccessSpecific( ScDocument& rDocument,
ScInterpreterContext& rContext, const ScQueryParam& rParam, bool bReverseSearch )
: maParam( rParam )
, rDoc( rDocument )
, mrContext( rContext )
, mbReverseSearch( bReverseSearch )
{ // coverity[uninit_member] - this just contains data, subclass will initialize some of it
}
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 >::DecPos()
{ if (maCurPos.second > 0)
{ // Move within the same block.
--maCurPos.second;
--nRow;
} else // Move to the prev block.
DecBlock();
}
void ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::Direct >::DecBlock()
{ // Set current position to the last possible row. const ScColumn& rCol = rDoc.maTabs[nTab]->CreateColumnIfNotExists(nCol); if (maCurPos.first != rCol.maCells.begin())
{
--maCurPos.first;
maCurPos.second = maCurPos.first->size - 1;
nRow = maCurPos.first->position + maCurPos.second;
} else
{ // No rows, set to end. This will make PerformQuery() go to next column.
nRow = maParam.nRow1 - 1;
maCurPos.first = rCol.maCells.end();
maCurPos.second = 0;
}
}
/** * 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;
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;
}
sc::CellStoreType::const_position_type aHiPos = mpCells->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.
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);
// The idea in iterating using the sorted cache is that the iteration is instead done // over indexes of the sorted cache (which is a stable sort of the cell contents) in the range // that fits the query condition and then that is mapped to rows. This will result in iterating // over only matching rows in their sorted order (and for equal rows in their row order). void ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::SortedCache >::InitPosStart(bool bNewSearchFunction, sal_uInt8 nSortedBinarySearch)
{
ScRange aSortedRangeRange( maParam.nCol1, maParam.nRow1, nTab, maParam.nCol2, maParam.nRow2, nTab ); // We want all matching values first in the sort order,
SetSortedRangeCache( rDoc.GetSortedRangeCache( aSortedRangeRange, maParam, &mrContext, bNewSearchFunction, nSortedBinarySearch )); // InitPosFinish() needs to be called after this, ScQueryCellIteratorBase::InitPos() // will handle that
}
template<bool fast> bool ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::SortedCache >::IncPosImpl()
{ if(sortedCachePos < sortedCachePosLast)
{
++sortedCachePos; if (maParam.bByRow)
nRow = sortedCache->rowForIndex(sortedCachePos); else
nCol = sortedCache->colForIndex(sortedCachePos); #ifndef DBG_UTIL if constexpr (!fast) #endif
{ if (maParam.bByRow)
{ // Avoid mdds position() call if row is in the same block. if (maCurPos.first != pColumn->maCells.end() && o3tl::make_unsigned(nRow) >= maCurPos.first->position
&& o3tl::make_unsigned(nRow) < maCurPos.first->position + maCurPos.first->size)
maCurPos.second = nRow - maCurPos.first->position; else
maCurPos = pColumn->maCells.position(nRow);
}
} returntrue;
} else
{ // This will make PerformQuery() go to next column. // Necessary even in fast mode, as GetNext() will call GetThis() in this case. if (!maParam.bByRow)
++nRow;
maCurPos.first = pColumn->maCells.end();
maCurPos.second = 0; returnfalse;
}
}
// Helper that allows binary search of unsorted cells using ScSortedRangeCache. // Rows in the given range are kept in a sorted vector and that vector is binary-searched. class ScQueryCellIteratorAccessSpecific< ScQueryCellIteratorAccess::SortedCache >::SortedCacheIndexer
{
std::vector<SCCOLROW> mSortedColsRowsCopy; const std::vector<SCCOLROW>& mSortedColsRows;
ScDocument& mrDoc; const sc::CellStoreType* mpCells;
size_t mLowIndex;
size_t mHighIndex; bool mValid;
SCTAB mnTab;
const std::vector<SCCOLROW>& makeSortedColsRows( const ScSortedRangeCache* cache, SCCOLROW startColRow, SCCOLROW endColRow )
{ // Keep a reference to cols/rows from the cache if equal, otherwise make a copy. if (cache->isRowSearch())
{ if (startColRow == cache->getRange().aStart.Row() && endColRow == cache->getRange().aEnd.Row()) return cache->sortedRows(); else
{
mSortedColsRowsCopy.reserve(cache->sortedRows().size()); for (SCROW row : cache->sortedRows()) if (row >= startColRow && row <= endColRow)
mSortedColsRowsCopy.emplace_back(row); return mSortedColsRowsCopy;
}
} else
{ if (startColRow == cache->getRange().aStart.Col() && endColRow == cache->getRange().aEnd.Col()) return cache->sortedCols(); else
{
mSortedColsRowsCopy.reserve(cache->sortedCols().size()); for (SCCOL col : cache->sortedCols()) if (col >= startColRow && col <= endColRow)
mSortedColsRowsCopy.emplace_back(col); return mSortedColsRowsCopy;
}
}
}
staticbool CanBeUsedForSorterCache(ScDocument& /*rDoc*/, const ScQueryParam& /*rParam*/,
SCTAB /*nTab*/, const ScFormulaCell* /*cell*/, const ScComplexRefData* /*refData*/,
ScInterpreterContext& /*context*/)
{ #if 1 /* TODO: tdf#151958 broken by string query of binary search on sorted * cache, use the direct query instead for releases and fix SortedCache * implementation after. Not only COUNTIF() is broken, but also COUNTIFS(), * and maybe lcl_LookupQuery() for VLOOKUP() etc. as well. Just disable * this for now. * Can't just return false because below would be unreachable code. Can't * just #if/#else/#endif either because parameters would be unused. Crap
* this and comment out parameter names. */ returnfalse; #else if(!rParam.GetEntry(0).bDoQuery || rParam.GetEntry(1).bDoQuery
|| rParam.GetEntry(0).GetQueryItems().size() != 1 ) returnfalse; if(rParam.eSearchType != utl::SearchParam::SearchType::Normal) returnfalse; if(rParam.GetEntry(0).GetQueryItem().meType != ScQueryEntry::ByValue
&& rParam.GetEntry(0).GetQueryItem().meType != ScQueryEntry::ByString) returnfalse; if(!rParam.bByRow) returnfalse; if(rParam.bHasHeader) returnfalse; if(rParam.mbRangeLookup) returnfalse; constbool bNewSearchFunction = nSearchOpCode == SC_OPCODE_X_LOOKUP || nSearchOpCode == SC_OPCODE_X_MATCH; if(rParam.GetEntry(0).GetQueryItem().meType == ScQueryEntry::ByString && !bNewSearchFunction
&& !ScQueryEvaluator::isMatchWholeCell(rDoc, rParam.GetEntry(0).eOp)) returnfalse; // substring matching cannot be sorted if(rParam.GetEntry(0).eOp != SC_LESS && rParam.GetEntry(0).eOp != SC_LESS_EQUAL
&& rParam.GetEntry(0).eOp != SC_GREATER && rParam.GetEntry(0).eOp != SC_GREATER_EQUAL
&& rParam.GetEntry(0).eOp != SC_EQUAL) returnfalse; // For unittests allow inefficient caching, in order for the code to be checked. staticconstbool bRunningUnitTest = o3tl::IsRunningUnitTest(); if(refData == nullptr || refData->Ref1.IsRowRel() || refData->Ref2.IsRowRel())
{ // If this is not a range, then a cache is not worth it. If rows are relative, then each // computation will use a different area, so the cache wouldn't be reused. Tab/cols are // not a problem, because formula group computations are done for the same tab/col. if(!bRunningUnitTest) returnfalse;
} if(rParam.nRow2 - rParam.nRow1 < 10)
{ if(!bRunningUnitTest) returnfalse;
} if( !cell ) returnfalse; if( !cell->GetCellGroup() || cell->GetCellGroup()->mnLength < 10 )
{ if(!bRunningUnitTest) returnfalse;
} // Check that all the relevant caches would be valid (may not be the case when mixing // numeric and string cells for ByValue lookups). for(SCCOL col : rDoc.GetAllocatedColumnsRange(nTab, rParam.nCol1, rParam.nCol2))
{
ScRange aSortedRangeRange( col, rParam.nRow1, nTab, col, rParam.nRow2, nTab); if( aSortedRangeRange.Contains( cell->aPos )) returnfalse; // self-referencing, can't create cache
ScSortedRangeCache& cache = rDoc.GetSortedRangeCache( aSortedRangeRange, rParam, &context ); if(!cache.isValid()) returnfalse;
} returntrue; #endif
}
template<> bool ScQueryCellIterator< ScQueryCellIteratorAccess::SortedCache >::GetNext()
{
assert( !nStopOnMismatch );
assert( !nTestEqualCondition ); // When searching using sorted cache, we should always find cells that match, // because InitPos()/IncPos() select only such rows, so skip GetThis() (and thus // the somewhat expensive PerformQuery) as long as we're not at the end // of a column. As an optimization IncPosFast() returns true if not at the end, // in which case in non-DBG_UTIL mode it doesn't even bother to set maCurPos. if( IncPosFast())
{ #ifdef DBG_UTIL
assert(GetThis()); #endif returntrue;
} return GetThis();
}
template<>
sal_uInt64 ScCountIfCellIterator< ScQueryCellIteratorAccess::SortedCache >::GetCount()
{ // Keep Entry.nField in iterator on column change
SetAdvanceQueryParamEntryField( true );
assert(nTab < rDoc.GetTableCount() && "try to access index out of bounds, FIX IT");
sal_uInt64 count = 0; // Each column must be sorted separately. for(SCCOL col : rDoc.GetAllocatedColumnsRange(nTab, maParam.nCol1, maParam.nCol2))
{
nCol = col;
nRow = maParam.nRow1;
ScRange aSortedRangeRange( col, maParam.nRow1, nTab, col, maParam.nRow2, nTab);
ScQueryOp& op = maParam.GetEntry(0).eOp; bool bNewSearchFunction = nSearchOpCode == SC_OPCODE_X_LOOKUP || nSearchOpCode == SC_OPCODE_X_MATCH;
SetSortedRangeCache( rDoc.GetSortedRangeCache( aSortedRangeRange, maParam, &mrContext, bNewSearchFunction, nSearchOpCode )); if( op == SC_EQUAL )
{ // BinarySearch() searches for the last item that matches. Therefore first // find the last non-matching position using SC_LESS and then find the last // matching position using SC_EQUAL.
ScQueryOp saveOp = op;
op = SC_LESS; if( BinarySearch( nCol, true ))
{
op = saveOp; // back to SC_EQUAL
size_t lastNonMatching = sortedCache->indexForRow(nRow); if( BinarySearch( nCol ))
{
size_t lastMatching = sortedCache->indexForRow(nRow);
assert(lastMatching >= lastNonMatching);
count += lastMatching - lastNonMatching;
} else
{ // BinarySearch() should at least find the same result as the SC_LESS
--> --------------------
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.