diff options
author | Kohei Yoshida <kohei.yoshida@collabora.com> | 2013-10-25 22:03:18 -0400 |
---|---|---|
committer | Kohei Yoshida <kohei.yoshida@collabora.com> | 2013-10-28 10:42:59 -0400 |
commit | 7171b26873988058d64a4bbf37c6ea211ea0d3ce (patch) | |
tree | dac1d37cbaca8cb31dd2707ebcf986e3da953608 /sc | |
parent | bb13b922b66d14f40a0be16bc380e462a1ea6432 (diff) |
Add cache of column data arrays to the group formula context.
Change-Id: I225491a1c6dd0b5aa2663331b30d177a7e62329b
Diffstat (limited to 'sc')
-rw-r--r-- | sc/inc/formulagroup.hxx | 38 | ||||
-rw-r--r-- | sc/qa/unit/ucalc_formula.cxx | 78 | ||||
-rw-r--r-- | sc/source/core/data/column2.cxx | 396 | ||||
-rw-r--r-- | sc/source/core/tool/formulagroup.cxx | 64 |
4 files changed, 416 insertions, 160 deletions
diff --git a/sc/inc/formulagroup.hxx b/sc/inc/formulagroup.hxx index ca240f718089..c1eadbb1d779 100644 --- a/sc/inc/formulagroup.hxx +++ b/sc/inc/formulagroup.hxx @@ -33,8 +33,42 @@ struct FormulaGroupContext : boost::noncopyable typedef boost::ptr_vector<NumArrayType> NumArrayStoreType; typedef boost::ptr_vector<StrArrayType> StrArrayStoreType; - NumArrayStoreType maNumArrays; - StrArrayStoreType maStrArrays; + struct ColKey + { + SCTAB mnTab; + SCCOL mnCol; + + struct Hash + { + size_t operator() ( const ColKey& rKey ) const; + }; + + ColKey( SCTAB nTab, SCCOL nCol ); + + bool operator== ( const ColKey& r ) const; + bool operator!= ( const ColKey& r ) const; + }; + + struct ColArray + { + NumArrayType* mpNumArray; + StrArrayType* mpStrArray; + size_t mnSize; + + ColArray( NumArrayType* pNumArray, StrArrayType* pStrArray ); + }; + + typedef boost::unordered_map<ColKey, ColArray, ColKey::Hash> ColArraysType; + + NumArrayStoreType maNumArrays; /// manage life cycle of numeric arrays. + StrArrayStoreType maStrArrays; /// manage life cycle of string arrays. + + ColArraysType maColArrays; /// keep track of longest array for each column. + + ColArray* getCachedColArray( SCTAB nTab, SCCOL nCol, size_t nSize ); + + ColArray* setCachedColArray( + SCTAB nTab, SCCOL nCol, NumArrayType* pNumArray, StrArrayType* pStrArray ); }; /** diff --git a/sc/qa/unit/ucalc_formula.cxx b/sc/qa/unit/ucalc_formula.cxx index 034e0e351249..ab1614134561 100644 --- a/sc/qa/unit/ucalc_formula.cxx +++ b/sc/qa/unit/ucalc_formula.cxx @@ -152,6 +152,84 @@ void Test::testFetchVectorRefArray() CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 6, 13)); CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 7, "ABC")); + // Column E consists of formula cells whose results are all numeric. + for (SCROW i = 0; i <= 6; ++i) + m_pDoc->SetString(ScAddress(4,i,0), "=ROW()"); + m_pDoc->CalcAll(); + + // Leave row 7 empty. + m_pDoc->SetString(ScAddress(4,8,0), "Andy"); + m_pDoc->SetValue(ScAddress(4,9,0), 123); + + // This array fits within a single formula block. + aArray = m_pDoc->FetchVectorRefArray(aCxt, ScAddress(4,0,0), 5); + CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid()); + CPPUNIT_ASSERT_MESSAGE("Array should be purely numeric.", aArray.mpNumericArray && !aArray.mpStringArray); + CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 1)); + CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 2)); + CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 3)); + CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 3, 4)); + CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 4, 5)); + + // This array spans over multiple blocks. + aArray = m_pDoc->FetchVectorRefArray(aCxt, ScAddress(4,0,0), 11); + CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid()); + CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray.mpNumericArray && aArray.mpStringArray); + CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 1)); + CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 2)); + CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 3)); + CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 3, 4)); + CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 4, 5)); + CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 5, 6)); + CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 6, 7)); + CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 7)); + CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 8, "Andy")); + CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 9, 123)); + CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 10)); + + // Hit the cache but at a different start row. + aArray = m_pDoc->FetchVectorRefArray(aCxt, ScAddress(4,2,0), 3); + CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid()); + CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray.mpNumericArray); + CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 3)); + CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 4)); + CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 5)); + + // Column F begins with empty rows at the top. + m_pDoc->SetValue(ScAddress(5,2,0), 1.1); + m_pDoc->SetValue(ScAddress(5,3,0), 1.2); + m_pDoc->SetString(ScAddress(5,4,0), "=2*8"); + m_pDoc->CalcAll(); + + aArray = m_pDoc->FetchVectorRefArray(aCxt, ScAddress(5,2,0), 4); + CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid()); + CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray.mpNumericArray); + CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 1.1)); + CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 1.2)); + CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 16)); + CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 3)); + + aArray = m_pDoc->FetchVectorRefArray(aCxt, ScAddress(5,0,0), 3); + CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid()); + CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray.mpNumericArray); + CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 0)); + CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 1)); + CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 1.1)); + + aArray = m_pDoc->FetchVectorRefArray(aCxt, ScAddress(5,0,0), 10); + CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid()); + CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray.mpNumericArray); + CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 0)); + CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 1)); + CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 1.1)); + CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 3, 1.2)); + CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 4, 16)); + CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 5)); + CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 6)); + CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 7)); + CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 8)); + CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 9)); + m_pDoc->DeleteTab(0); } diff --git a/sc/source/core/data/column2.cxx b/sc/source/core/data/column2.cxx index 5113435d4e03..22aca0a46cb5 100644 --- a/sc/source/core/data/column2.cxx +++ b/sc/source/core/data/column2.cxx @@ -2483,14 +2483,13 @@ bool appendDouble( return false; } -formula::VectorRefArray appendToNumBlock( - ScDocument* pDoc, sc::FormulaGroupContext& rCxt, size_t nPos, - size_t nLenRequested, sc::CellStoreType::iterator it, const sc::CellStoreType::iterator& itEnd ) +bool appendToNumBlock( + ScDocument* pDoc, sc::FormulaGroupContext& rCxt, sc::FormulaGroupContext::ColArray& rColArray, + size_t nPos, size_t nArrayLen, sc::CellStoreType::iterator it, const sc::CellStoreType::iterator& itEnd ) { - sc::FormulaGroupContext::NumArrayType& rNumArray = rCxt.maNumArrays.back(); - sc::FormulaGroupContext::StrArrayType* pStrArray = NULL; + sc::FormulaGroupContext::NumArrayType& rNumArray = *rColArray.mpNumArray; svl::SharedStringPool& rPool = pDoc->GetSharedStringPool(); - size_t nLenRemain = nLenRequested - nPos; + size_t nLenRemain = nArrayLen - nPos; for (; it != itEnd; ++it) { @@ -2501,15 +2500,15 @@ formula::VectorRefArray appendToNumBlock( sc::string_block::iterator itData, itDataEnd; getBlockIterators<sc::string_block>(it, nLenRemain, itData, itDataEnd); - if (!pStrArray) + if (!rColArray.mpStrArray) { rCxt.maStrArrays.push_back( - new sc::FormulaGroupContext::StrArrayType(nLenRequested, NULL)); - pStrArray = &rCxt.maStrArrays.back(); + new sc::FormulaGroupContext::StrArrayType(nArrayLen, NULL)); + rColArray.mpStrArray = &rCxt.maStrArrays.back(); } for (; itData != itDataEnd; ++itData, ++nPos) - (*pStrArray)[nPos] = itData->getDataIgnoreCase(); + (*rColArray.mpStrArray)[nPos] = itData->getDataIgnoreCase(); } break; case sc::element_type_edittext: @@ -2517,17 +2516,17 @@ formula::VectorRefArray appendToNumBlock( sc::edittext_block::iterator itData, itDataEnd; getBlockIterators<sc::edittext_block>(it, nLenRemain, itData, itDataEnd); - if (!pStrArray) + if (!rColArray.mpStrArray) { rCxt.maStrArrays.push_back( - new sc::FormulaGroupContext::StrArrayType(nLenRequested, NULL)); - pStrArray = &rCxt.maStrArrays.back(); + new sc::FormulaGroupContext::StrArrayType(nArrayLen, NULL)); + rColArray.mpStrArray = &rCxt.maStrArrays.back(); } for (; itData != itDataEnd; ++itData, ++nPos) { OUString aStr = ScEditUtil::GetString(**itData, pDoc); - (*pStrArray)[nPos] = rPool.intern(aStr).getDataIgnoreCase(); + (*rColArray.mpStrArray)[nPos] = rPool.intern(aStr).getDataIgnoreCase(); } } break; @@ -2548,21 +2547,21 @@ formula::VectorRefArray appendToNumBlock( rFC.SetErrCode(0); rFC.SetDirtyVar(); } - return formula::VectorRefArray(); + return false; } if (aRes.meType == sc::FormulaResultValue::Value) rNumArray[nPos] = aRes.mfValue; else { - if (!pStrArray) + if (!rColArray.mpStrArray) { rCxt.maStrArrays.push_back( - new sc::FormulaGroupContext::StrArrayType(nLenRequested, NULL)); - pStrArray = &rCxt.maStrArrays.back(); + new sc::FormulaGroupContext::StrArrayType(nArrayLen, NULL)); + rColArray.mpStrArray = &rCxt.maStrArrays.back(); } - (*pStrArray)[nPos] = aRes.maString.getDataIgnoreCase(); + (*rColArray.mpStrArray)[nPos] = aRes.maString.getDataIgnoreCase(); } } } @@ -2576,7 +2575,7 @@ formula::VectorRefArray appendToNumBlock( } else { - nPos = nLenRequested; + nPos = nArrayLen; nLenRemain = 0; } } @@ -2591,29 +2590,23 @@ formula::VectorRefArray appendToNumBlock( } break; default: - return formula::VectorRefArray(); + return false; } if (!nLenRemain) - { - if (pStrArray) - return formula::VectorRefArray(&rNumArray[0], &(*pStrArray)[0]); - else - return formula::VectorRefArray(&rNumArray[0]); - } + return true; } - return formula::VectorRefArray(); + return false; } -formula::VectorRefArray appendToStringBlock( - ScDocument* pDoc, sc::FormulaGroupContext& rCxt, size_t nPos, - size_t nLenRequested, sc::CellStoreType::iterator it, const sc::CellStoreType::iterator& itEnd ) +bool appendToStringBlock( + ScDocument* pDoc, sc::FormulaGroupContext& rCxt, sc::FormulaGroupContext::ColArray& rColArray, + size_t nPos, size_t nArrayLen, sc::CellStoreType::iterator it, const sc::CellStoreType::iterator& itEnd ) { - sc::FormulaGroupContext::StrArrayType& rStrArray = rCxt.maStrArrays.back(); - sc::FormulaGroupContext::NumArrayType* pNumArray = NULL; + sc::FormulaGroupContext::StrArrayType& rStrArray = *rColArray.mpStrArray; svl::SharedStringPool& rPool = pDoc->GetSharedStringPool(); - size_t nLenRemain = nLenRequested - nPos; + size_t nLenRemain = nArrayLen - nPos; double fNan; rtl::math::setNan(&fNan); @@ -2659,21 +2652,21 @@ formula::VectorRefArray appendToStringBlock( rFC.SetErrCode(0); rFC.SetDirtyVar(); } - return formula::VectorRefArray(); + return false; } if (aRes.meType == sc::FormulaResultValue::String) rStrArray[nPos] = aRes.maString.getDataIgnoreCase(); else { - if (!pNumArray) + if (!rColArray.mpNumArray) { rCxt.maNumArrays.push_back( - new sc::FormulaGroupContext::NumArrayType(nLenRequested, fNan)); - pNumArray = &rCxt.maNumArrays.back(); + new sc::FormulaGroupContext::NumArrayType(nArrayLen, fNan)); + rColArray.mpNumArray = &rCxt.maNumArrays.back(); } - (*pNumArray)[nPos] = aRes.mfValue; + (*rColArray.mpNumArray)[nPos] = aRes.mfValue; } } } @@ -2687,7 +2680,7 @@ formula::VectorRefArray appendToStringBlock( } else { - nPos = nLenRequested; + nPos = nArrayLen; nLenRemain = 0; } } @@ -2697,60 +2690,56 @@ formula::VectorRefArray appendToStringBlock( sc::numeric_block::iterator itData, itDataEnd; getBlockIterators<sc::numeric_block>(it, nLenRemain, itData, itDataEnd); - if (!pNumArray) + if (!rColArray.mpNumArray) { rCxt.maNumArrays.push_back( - new sc::FormulaGroupContext::NumArrayType(nLenRequested, fNan)); - pNumArray = &rCxt.maNumArrays.back(); + new sc::FormulaGroupContext::NumArrayType(nArrayLen, fNan)); + rColArray.mpNumArray = &rCxt.maNumArrays.back(); } for (; itData != itDataEnd; ++itData, ++nPos) - (*pNumArray)[nPos] = *itData; + (*rColArray.mpNumArray)[nPos] = *itData; } break; default: - return formula::VectorRefArray(); + return false; } if (!nLenRemain) - { - if (pNumArray) - return formula::VectorRefArray(&(*pNumArray)[0], &rStrArray[0]); - else - return formula::VectorRefArray(&rStrArray[0]); - } + return true; } - return formula::VectorRefArray(); + return false; } void copyFirstStringBlock( - ScDocument& rDoc, sc::FormulaGroupContext& rCxt, size_t nLen, const sc::CellStoreType::position_type& rPos ) + ScDocument& rDoc, sc::FormulaGroupContext::StrArrayType& rArray, size_t nLen, const sc::CellStoreType::iterator& itBlk ) { - rCxt.maStrArrays.push_back(new sc::FormulaGroupContext::StrArrayType); - sc::FormulaGroupContext::StrArrayType& rArray = rCxt.maStrArrays.back(); - rArray.reserve(nLen); + sc::FormulaGroupContext::StrArrayType::iterator itArray = rArray.begin(); - switch (rPos.first->type) + switch (itBlk->type) { case sc::element_type_string: { - svl::SharedString* p = &sc::string_block::at(*rPos.first->data, rPos.second); - svl::SharedString* pEnd = p + nLen; - for (; p != pEnd; ++p) - rArray.push_back(p->getDataIgnoreCase()); + sc::string_block::iterator it = sc::string_block::begin(*itBlk->data); + sc::string_block::iterator itEnd = it; + std::advance(itEnd, nLen); + for (; it != itEnd; ++it, ++itArray) + *itArray = it->getDataIgnoreCase(); } break; case sc::element_type_edittext: { - EditTextObject** p = &sc::edittext_block::at(*rPos.first->data, rPos.second); - EditTextObject** pEnd = p + nLen; + sc::edittext_block::iterator it = sc::edittext_block::begin(*itBlk->data); + sc::edittext_block::iterator itEnd = it; + std::advance(itEnd, nLen); + svl::SharedStringPool& rPool = rDoc.GetSharedStringPool(); - for (; p != pEnd; ++p) + for (; it != itEnd; ++it, ++itArray) { - EditTextObject* pText = *p; + EditTextObject* pText = *it; OUString aStr = ScEditUtil::GetString(*pText, &rDoc); - rArray.push_back(rPool.intern(aStr).getDataIgnoreCase()); + *itArray = rPool.intern(aStr).getDataIgnoreCase(); } } break; @@ -2759,6 +2748,71 @@ void copyFirstStringBlock( } } +sc::FormulaGroupContext::ColArray* +copyFirstFormulaBlock( + sc::FormulaGroupContext& rCxt, sc::CellStoreType::iterator itBlk, size_t nArrayLen, + SCTAB nTab, SCCOL nCol ) +{ + double fNan; + rtl::math::setNan(&fNan); + + size_t nLen = std::min(itBlk->size, nArrayLen); + + sc::formula_block::iterator it = sc::formula_block::begin(*itBlk->data); + sc::formula_block::iterator itEnd; + + sc::FormulaGroupContext::NumArrayType* pNumArray = NULL; + sc::FormulaGroupContext::StrArrayType* pStrArray = NULL; + + itEnd = it; + std::advance(itEnd, nLen); + size_t nPos = 0; + for (; it != itEnd; ++it, ++nPos) + { + ScFormulaCell& rFC = **it; + sc::FormulaResultValue aRes = rFC.GetResult(); + if (aRes.meType == sc::FormulaResultValue::Invalid || aRes.mnError) + { + if (aRes.mnError == ScErrorCodes::errCircularReference) + { + // This cell needs to be recalculated on next visit. + rFC.SetErrCode(0); + rFC.SetDirtyVar(); + } + return NULL; + } + + if (aRes.meType == sc::FormulaResultValue::Value) + { + if (!pNumArray) + { + rCxt.maNumArrays.push_back( + new sc::FormulaGroupContext::NumArrayType(nArrayLen, fNan)); + pNumArray = &rCxt.maNumArrays.back(); + } + + (*pNumArray)[nPos] = aRes.mfValue; + } + else + { + if (!pStrArray) + { + rCxt.maStrArrays.push_back( + new sc::FormulaGroupContext::StrArrayType(nArrayLen, NULL)); + pStrArray = &rCxt.maStrArrays.back(); + } + + (*pStrArray)[nPos] = aRes.maString.getDataIgnoreCase(); + } + } + + if (!pNumArray && !pStrArray) + // At least one of these arrays should be allocated. + return NULL; + + return rCxt.setCachedColArray(nTab, nCol, pNumArray, pStrArray); +} + } formula::VectorRefArray ScColumn::FetchVectorRefArray( sc::FormulaGroupContext& rCxt, SCROW nRow1, SCROW nRow2 ) @@ -2766,142 +2820,168 @@ formula::VectorRefArray ScColumn::FetchVectorRefArray( sc::FormulaGroupContext& if (nRow1 > nRow2) return formula::VectorRefArray(); + // See if the requested range is already cached. + sc::FormulaGroupContext::ColArray* pColArray = rCxt.getCachedColArray(nTab, nCol, nRow2+1); + if (pColArray) + { + const double* pNum = NULL; + if (pColArray->mpNumArray) + pNum = &(*pColArray->mpNumArray)[nRow1]; + + rtl_uString** pStr = NULL; + if (pColArray->mpStrArray) + pStr = &(*pColArray->mpStrArray)[nRow1]; + + return formula::VectorRefArray(pNum, pStr); + } + double fNan; rtl::math::setNan(&fNan); - size_t nLenRequested = nRow2 - nRow1 + 1; - sc::CellStoreType::position_type aPos = maCells.position(nRow1); - size_t nLen = aPos.first->size - aPos.second; // length of data from first block - switch (aPos.first->type) + // We need to fetch all cell values from row 0 to nRow2 for caching purposes. + sc::CellStoreType::iterator itBlk = maCells.begin(); + switch (itBlk->type) { case sc::element_type_numeric: { - // This is a numeric cell block. - if (nLenRequested <= nLen) + if (static_cast<size_t>(nRow2) < itBlk->size) { - // Requested length fits a single block. - const double* p = &sc::numeric_block::at(*aPos.first->data, aPos.second); + // Requested range falls within the first block. No need to cache. + const double* p = &sc::numeric_block::at(*itBlk->data, nRow1); return formula::VectorRefArray(p); } // Allocate a new array and copy the values to it. - sc::numeric_block::const_iterator it = sc::numeric_block::begin(*aPos.first->data); - sc::numeric_block::const_iterator itEnd = sc::numeric_block::end(*aPos.first->data); - std::advance(it, aPos.second); + sc::numeric_block::const_iterator it = sc::numeric_block::begin(*itBlk->data); + sc::numeric_block::const_iterator itEnd = sc::numeric_block::end(*itBlk->data); rCxt.maNumArrays.push_back(new sc::FormulaGroupContext::NumArrayType(it, itEnd)); sc::FormulaGroupContext::NumArrayType& rArray = rCxt.maNumArrays.back(); - rArray.resize(nLenRequested, fNan); // allocate to the requested length. + rArray.resize(nRow2+1, fNan); // allocate to the requested length. + + pColArray = rCxt.setCachedColArray(nTab, nCol, &rArray, NULL); + if (!pColArray) + // Failed to insert a new cached column array. + return formula::VectorRefArray(); // Fill the remaining array with values from the following blocks. - ++aPos.first; - return appendToNumBlock(pDocument, rCxt, nLen, nLenRequested, aPos.first, maCells.end()); + size_t nPos = itBlk->size; + ++itBlk; + if (!appendToNumBlock(pDocument, rCxt, *pColArray, nPos, nRow2+1, itBlk, maCells.end())) + { + return formula::VectorRefArray(); + } + + if (pColArray->mpStrArray) + return formula::VectorRefArray(&(*pColArray->mpNumArray)[nRow1], &(*pColArray->mpStrArray)[nRow1]); + else + return formula::VectorRefArray(&(*pColArray->mpNumArray)[nRow1]); } break; - case sc::element_type_formula: + case sc::element_type_string: + case sc::element_type_edittext: { - sal_uInt16 nErr; - double fVal; - - rCxt.maNumArrays.push_back(new sc::FormulaGroupContext::NumArrayType); - sc::FormulaGroupContext::NumArrayType& rArray = rCxt.maNumArrays.back(); - rArray.reserve(nLenRequested); + rCxt.maStrArrays.push_back(new sc::FormulaGroupContext::StrArrayType(nRow2+1, NULL)); + sc::FormulaGroupContext::StrArrayType& rArray = rCxt.maStrArrays.back(); + pColArray = rCxt.setCachedColArray(nTab, nCol, NULL, &rArray); + if (!pColArray) + // Failed to insert a new cached column array. + return formula::VectorRefArray(); - sc::formula_block::const_iterator it = sc::formula_block::begin(*aPos.first->data); - std::advance(it, aPos.second); - sc::formula_block::const_iterator itEnd; - if (nLenRequested <= nLen) + if (static_cast<size_t>(nRow2) < itBlk->size) { - // Requested length is within a single block. - itEnd = it; - std::advance(itEnd, nLenRequested); - for (; it != itEnd; ++it) - { - ScFormulaCell& rCell = **it; - if (!rCell.GetErrorOrValue(nErr, fVal) || nErr) - { - if (nErr == ScErrorCodes::errCircularReference) - { - // This cell needs to be recalculated on next visit. - rCell.SetErrCode(0); - rCell.SetDirtyVar(); - } - return formula::VectorRefArray(); - } - - rArray.push_back(fVal); - } - + // Requested range falls within the first block. + copyFirstStringBlock(*pDocument, rArray, nRow2+1, itBlk); return formula::VectorRefArray(&rArray[0]); } - // Requested length goes beyond a single block. Fill the array - // with the content of this formula block first. - itEnd = sc::formula_block::end(*aPos.first->data); - for (; it != itEnd; ++it) - { - ScFormulaCell& rCell = **it; - if (!rCell.GetErrorOrValue(nErr, fVal) || nErr) - { - if (nErr == ScErrorCodes::errCircularReference) - { - // This cell needs to be recalculated on next visit. - rCell.SetErrCode(0); - rCell.SetDirtyVar(); - } - return formula::VectorRefArray(); - } - - rArray.push_back(fVal); - } + copyFirstStringBlock(*pDocument, rArray, itBlk->size, itBlk); // Fill the remaining array with values from the following blocks. - ++aPos.first; - if (!appendDouble(rArray, nLenRequested - nLen, aPos.first, maCells.end())) + size_t nPos = itBlk->size; + ++itBlk; + if (!appendToStringBlock(pDocument, rCxt, *pColArray, nPos, nRow2+1, itBlk, maCells.end())) return formula::VectorRefArray(); - return formula::VectorRefArray(&rArray[0]); + if (pColArray->mpNumArray) + return formula::VectorRefArray(&(*pColArray->mpNumArray)[nRow1], &(*pColArray->mpStrArray)[nRow1]); + else + return formula::VectorRefArray(&(*pColArray->mpStrArray)[nRow1]); } break; - case sc::element_type_string: - case sc::element_type_edittext: + case sc::element_type_formula: { - if (nLenRequested <= nLen) + if (static_cast<size_t>(nRow2) < itBlk->size) { - // Requested length fits a single block. - copyFirstStringBlock(*pDocument, rCxt, nLenRequested, aPos); - sc::FormulaGroupContext::StrArrayType& rArray = rCxt.maStrArrays.back(); - return formula::VectorRefArray(&rArray[0]); + // Requested length is within a single block, and the data is + // not cached. + pColArray = copyFirstFormulaBlock(rCxt, itBlk, nRow2+1, nTab, nCol); + if (!pColArray) + // Failed to insert a new cached column array. + return formula::VectorRefArray(); + + const double* pNum = NULL; + rtl_uString** pStr = NULL; + if (pColArray->mpNumArray) + pNum = &(*pColArray->mpNumArray)[0]; + if (pColArray->mpStrArray) + pStr = &(*pColArray->mpStrArray)[0]; + + return formula::VectorRefArray(pNum, pStr); } - copyFirstStringBlock(*pDocument, rCxt, nLen, aPos); - rCxt.maStrArrays.back().resize(nLenRequested, NULL); // allocate array to requested length. + pColArray = copyFirstFormulaBlock(rCxt, itBlk, nRow2+1, nTab, nCol); + if (!pColArray) + // Failed to insert a new cached column array. + return formula::VectorRefArray(); - // Fill the remaining array with values from the following blocks. - ++aPos.first; - return appendToStringBlock(pDocument, rCxt, nLen, nLenRequested, aPos.first, maCells.end()); + size_t nPos = itBlk->size; + ++itBlk; + if (pColArray->mpNumArray) + { + if (!appendToNumBlock(pDocument, rCxt, *pColArray, nPos, nRow2+1, itBlk, maCells.end())) + return formula::VectorRefArray(); + } + else + { + if (!appendToStringBlock(pDocument, rCxt, *pColArray, nPos, nRow2+1, itBlk, maCells.end())) + return formula::VectorRefArray(); + } + + const double* pNum = NULL; + rtl_uString** pStr = NULL; + if (pColArray->mpNumArray) + pNum = &(*pColArray->mpNumArray)[0]; + if (pColArray->mpStrArray) + pStr = &(*pColArray->mpStrArray)[0]; + + return formula::VectorRefArray(pNum, pStr); } break; case sc::element_type_empty: { - if (nLenRequested <= nLen) - { - // Fill the whole length with NaN's. - rCxt.maNumArrays.push_back(new sc::FormulaGroupContext::NumArrayType(nLenRequested, fNan)); - return formula::VectorRefArray(&rCxt.maNumArrays.back()[0]); - } - - // Fill the array with zero for the length of the empty block. - rCxt.maNumArrays.push_back(new sc::FormulaGroupContext::NumArrayType(nLen, 0.0)); + // Fill the whole length with NaN's. + rCxt.maNumArrays.push_back(new sc::FormulaGroupContext::NumArrayType(nRow2+1, fNan)); sc::FormulaGroupContext::NumArrayType& rArray = rCxt.maNumArrays.back(); - rArray.reserve(nLenRequested); + pColArray = rCxt.setCachedColArray(nTab, nCol, &rArray, NULL); + if (!pColArray) + // Failed to insert a new cached column array. + return formula::VectorRefArray(); + + if (static_cast<size_t>(nRow2) < itBlk->size) + return formula::VectorRefArray(&(*pColArray->mpNumArray)[0]); // Fill the remaining array with values from the following blocks. - ++aPos.first; - if (!appendDouble(rArray, nLenRequested - nLen, aPos.first, maCells.end())) + size_t nPos = itBlk->size; + ++itBlk; + if (!appendToNumBlock(pDocument, rCxt, *pColArray, nPos, nRow2+1, itBlk, maCells.end())) return formula::VectorRefArray(); - return formula::VectorRefArray(&rArray[0]); + if (pColArray->mpStrArray) + return formula::VectorRefArray(&(*pColArray->mpNumArray)[nRow1], &(*pColArray->mpStrArray)[nRow1]); + else + return formula::VectorRefArray(&(*pColArray->mpNumArray)[nRow1]); } + break; default: ; } diff --git a/sc/source/core/tool/formulagroup.cxx b/sc/source/core/tool/formulagroup.cxx index 7f1bd747d78c..9482b1fa80bf 100644 --- a/sc/source/core/tool/formulagroup.cxx +++ b/sc/source/core/tool/formulagroup.cxx @@ -39,6 +39,70 @@ extern "C" void compileOpenCLKernels(const OUString*); namespace sc { +size_t FormulaGroupContext::ColKey::Hash::operator ()( const FormulaGroupContext::ColKey& rKey ) const +{ + return rKey.mnTab * MAXCOLCOUNT + rKey.mnCol; +} + +FormulaGroupContext::ColKey::ColKey( SCTAB nTab, SCCOL nCol ) : mnTab(nTab), mnCol(nCol) {} + +bool FormulaGroupContext::ColKey::operator== ( const ColKey& r ) const +{ + return mnTab == r.mnTab && mnCol == r.mnCol; +} + +bool FormulaGroupContext::ColKey::operator!= ( const ColKey& r ) const +{ + return !operator==(r); +} + +FormulaGroupContext::ColArray::ColArray( NumArrayType* pNumArray, StrArrayType* pStrArray ) : + mpNumArray(pNumArray), mpStrArray(pStrArray), mnSize(0) +{ + if (mpNumArray) + mnSize = mpNumArray->size(); + else if (mpStrArray) + mnSize = mpStrArray->size(); +} + +FormulaGroupContext::ColArray* FormulaGroupContext::getCachedColArray( SCTAB nTab, SCCOL nCol, size_t nSize ) +{ + ColArraysType::iterator itColArray = maColArrays.find(ColKey(nTab, nCol)); + if (itColArray == maColArrays.end()) + // Not cached for this column. + return NULL; + + ColArray& rCached = itColArray->second; + if (nSize > rCached.mnSize) + // Cached data array is not long enough for the requested range. + return NULL; + + return &rCached; +} + +FormulaGroupContext::ColArray* FormulaGroupContext::setCachedColArray( + SCTAB nTab, SCCOL nCol, NumArrayType* pNumArray, StrArrayType* pStrArray ) +{ + ColArraysType::iterator it = maColArrays.find(ColKey(nTab, nCol)); + if (it == maColArrays.end()) + { + std::pair<ColArraysType::iterator,bool> r = + maColArrays.insert( + ColArraysType::value_type(ColKey(nTab, nCol), ColArray(pNumArray, pStrArray))); + + if (!r.second) + // Somehow the insertion failed. + return NULL; + + return &r.first->second; + } + + // Prior array exists for this column. Overwrite it. + ColArray& rArray = it->second; + rArray = ColArray(pNumArray, pStrArray); + return &rArray; +} + namespace { /** |